16 个版本 (10 个重大更改)
0.11.1 | 2023 年 11 月 24 日 |
---|---|
0.10.0 | 2023 年 11 月 21 日 |
0.8.0 | 2022 年 6 月 12 日 |
0.5.1 | 2021 年 6 月 12 日 |
0.1.2 | 2020 年 7 月 3 日 |
#169 in 开发工具
每月 103 次下载
170KB
3.5K SLoC
xgadget
针对 x86 (32 位) 和 x64 (64 位) 可执行文件的快速、并行、跨-{补丁、编译器}-变体的 ROP/JOP gadget 搜索。使用 iced-x86 汇编器库。
该 crate 可以用作 命令行工具 (Windows/Linux/MacOS) 或 库 (7 个知名依赖项,全部为 Rust)。
快速入门
安装 CLI 工具并显示其帮助菜单
cargo install xgadget --features cli-bin # Build on host (pre-req: https://rust-lang.net.cn/tools/install)
xgadget --help # List available command line options
ROP 和 JOP 代码重用攻击是如何工作的?
- 返回导向编程 (ROP) 引入了 代码重用 攻击,在硬件缓解措施(即 NX、DEP)使得 代码注入 不太可能(没有同时具有
WRITE
和EXECUTE
内存权限)之后。攻击者利用栈控制,将短小的现有汇编序列(称为 "gadget")链接在一起——如果泄露允许在 ASLR 面前计算 gadget 地址。当连续的 ROP gadget 地址写入已损坏的栈中时,每个 gadget 的结束ret
指令将下一个 gadget 的地址弹出至 CPU 的指令指针。结果?对受害进程的图灵完备控制。
ROP 攻击模型(重新创建自: Bletsch 等。)
- 跳转导向编程 (JOP) 是一种较新的代码重用方法,与 ROP 不同,它不依赖于栈控制。攻击 绕过 硬件辅助的影子栈实现(例如 Intel CET 的影子栈),并且 有限 但 不是阻止 原型无关的间接目标检查(例如 Intel CET 的 IBT)。JOP 允许在任何
READ
/WRITE
内存位置存储 gadget 地址表。而不是利用调用返回语义来执行 gadget 列表,一个 "调度" gadget(例如add rax, 8; jmp [rax]
)控制表索引。如果每个 gadget 以跳转回调度器(而不是ret
)结束,则会发生链式反应。
JOP 攻击模型(重新创建自: Bletsch 等。)
关于
xgadget
是一个用于 返回导向编程(ROP) 和 跳转导向编程(JOP) 漏洞开发工具。它是一个快速、多线程的替代方案,类似于 ROPGadget
、Ropper
和 rp
等优秀的工具。
目标是支持实际应用,同时探索独特和实验性功能。据我们所知,xgadget
是第一个以 快速寄存器敏感 为特点的 gadget 搜索工具。
-
快速寄存器敏感:通过寄存器使用行为过滤 gadgets,而不仅仅是与给定正则表达式匹配,无需 SMT 求解(更强大,但通常不切实际)。
-
--reg-overwrite [<OPT_REG(S)>...]
- 控制任何寄存器(无参数)或特定寄存器(参数) -
--reg-mem-write [<OPT_REG(S)>...]
- 通过任何寄存器(无参数)或特定寄存器(参数)写入内存 -
--reg-no-write [<OPT_REG(S)>...]
- 不写入任何寄存器(无参数)或特定寄存器(参数) -
--reg-read [<OPT_REG(S)>...]
- 读取任何寄存器(无参数)或特定寄存器(参数) -
--reg-mem-read [<OPT_REG(S)>...]
- 通过任何寄存器(无参数)或特定寄存器(参数)读取内存 -
--reg-no-read [<OPT_REG(S)>...]
- 不读取任何寄存器(无参数)或特定寄存器(参数)
-
-
JOP 效率化:JOP 搜索使用指令语义,而不是针对单个编码的硬编码正则表达式。
- 可选地通过标志
--dispatcher
过滤 JOP "分发器" gadgets
- 可选地通过标志
-
跨变体:寻找可以在二进制的多个变体中工作的 gadgets(例如,针对不同程序或编译器版本的抗多样化)。两种策略
- 完全匹配 - 相同的指令序列,相同的程序计数器:gadget 可以完全重用。例如
- Gadget:
pop rdi; ret;
- 地址(在所有二进制文件中):
0xc748d
- Gadget:
跨变体 完全匹配
- 部分匹配 - 相同的指令序列,不同的程序计数器:gadget 逻辑可移植。例如
- Gadget:
pop rdi; ret;
- 在
bin_v1.1
中的地址:0xc748d
- 在
bin_v1.2
中的地址:0xc9106
- Gadget:
跨变体 部分匹配
- 这完全是可选的,您可以自由地对单个二进制文件运行此工具。
其他功能包括
- 支持 ELF32、ELF64、PE32、PE32+、Mach-O 和原始文件
- 跨可用核心并行,无论搜索单个二进制文件还是多个变体
- 目前仅支持 8086/x86/x64(使用速度优化的架构特定反汇编器)
CLI 示例
运行 xgadget --help
以列出可用选项。
- 示例:在
/usr/bin/sudo
中搜索控制rdi
的可靠方法
xgadget /usr/bin/sudo --reg-only --reg-overwrite rdi
- 示例:搜索控制
rdi
值的 ROP 模块,永远不要读取rsi
或rdx
,且出现在不包含字节0x32
或0x0d
的地址上
xgadget /usr/bin/sudo --rop --reg-overwrite rdi --reg-no-read rsi rdx --bad-bytes 0x32 0x0d
- 示例:在
/usr/bin/sudo
中搜索最长 10 条指令的 "pop, pop, {jmp,call}" 模块,使用 AT&T 语法打印结果
xgadget /usr/bin/sudo --jop --reg-pop --att --max-len 10
- 示例:与上面相同,但使用正则表达式过滤器匹配 "pop, pop, {jmp,call}" 指令字符串(这里较慢/准确性较低,但正则表达式允许更灵活的搜索)
xgadget /usr/bin/sudo --regex-filter "^(?:pop)(?:.*(?:pop))*.*(?:call|jmp)" --att --max-len 10
- 示例:检查
sudo
和lighttpd
编译的漏洞缓解二进制文件
xgadget /usr/bin/sudo /usr/sbin/lighttpd --check-sec
- 示例:列出
lighttpd
的导入和内部符号
xgadget /usr/sbin/lighttpd --symbols
API 使用
查找模块
use xgadget::{Binary, SearchConfig};
let max_gadget_len = 5;
// Search single binary
let bin = &[Binary::from_path("/path/to/bin_v1").unwrap()];
let gadgets =
xgadget::find_gadgets(bin, max_gadget_len, SearchConfig::default()).unwrap();
let stack_pivot_gadgets = xgadget::filter_stack_pivot(gadgets);
// Search for cross-variant gadgets, including partial matches
let search_config = SearchConfig::default() | SearchConfig::PART;
let bins = &[
Binary::from_path("/path/to/bin_v1").unwrap(),
Binary::from_path("/path/to/bin_v2").unwrap(),
];
let cross_gadgets =
xgadget::find_gadgets(bins, max_gadget_len, search_config).unwrap();
let cross_reg_pop_gadgets = xgadget::filter_reg_pop_only(cross_gadgets);
可以使用 GadgetAnalysis
对象和/或 semantics
模块中的函数创建自定义过滤器。上述 filter_stack_pivot
函数是如何实现的
use rayon::prelude::*;
use iced_x86;
use xgadget::{Gadget, GadgetAnalysis};
/// Parallel filter to gadgets that write the stack pointer
pub fn filter_stack_pivot<'a, P>(gadgets: P) -> P
where
P: IntoParallelIterator<Item = Gadget<'a>> + FromParallelIterator<Gadget<'a>>,
{
gadgets
.into_par_iter()
.filter(|g| {
let regs_overwritten = g.analysis().regs_overwritten(true);
if regs_overwritten.contains(&iced_x86::Register::RSP)
|| regs_overwritten.contains(&iced_x86::Register::ESP)
|| regs_overwritten.contains(&iced_x86::Register::SP)
{
return true;
}
false
})
.collect()
}
为什么没有链生成?
尝试自动生成 ROP/JOP 链的工具需要进行重量级分析——通常是中间表示的符号执行。这对于小型二进制文件和 CTF 问题效果不错,但对于大型、现实世界的程序来说,往往容易出错,难以扩展。目前,xgadget
有一个不同的目标:通过提供快速、准确的模块发现,使专家用户能够手动构建稳定的漏洞利用。
但是,它能在 10 秒内处理 10 个操作系统内核吗?! 可重复基准测试 harness
构建 Docker 容器并连接到它
user@host$ git clone [email protected]:entropic-security/xgadget.git
user@host$ cd xgadget
user@host$ docker build -t xgadget_bench_container .
user@host$ docker run -it xgadget_bench_container
root@container:/xgadget#
最终构建步骤运行 ./benches/bench_setup_ubuntu.sh
。此脚本下载并构建 10 个连续的 Linux 内核(版本 5.0.1
到 5.0.10
)——使用 x86_64_defconfig
)。喝杯咖啡,这可能需要一段时间。
完成后,运行 cargo bench
以在所有 10 个内核中搜索常见模块(以及其他基准测试)
root@container:/xgadget# cargo bench
在具有 gcc
版本 8.4.0 的 i7-9700K(8C/8T,3.6GHz 基础频率,4.9 GHz 最大频率)机器上:平均运行时间,处理所有十 54MB 内核(指令最长 5 条,对所有模块类型(ROP、JOP 和系统调用模块)进行全匹配搜索),仅为 6.3 秒!包括部分匹配也需要 7.9 秒。
快速漏洞相似度评分(FESS)
使用跨变体模块匹配作为二进制相似度的指标,设置了 --fess
标志。这是针对漏洞利用的防多样化实验。要查看容器内内核版本 5.0.1
、5.0.5
和 5.0.10
的相似度评分
root@container# cd ./benches/kernels/
root@container# xgadget vmlinux-5.0.1 vmlinux-5.0.5 vmlinux-5.0.10 --fess
TARGET 0 - [ name: 'vmlinux-5.0.1' | fmt-arch: ELF-X64 | entry: 0x00000001000000 | exec bytes/segments: 21,065,728/2 ]
TARGET 1 - [ name: 'vmlinux-5.0.5' | fmt-arch: ELF-X64 | entry: 0x00000001000000 | exec bytes/segments: 21,069,824/2 ]
TARGET 2 - [ name: 'vmlinux-5.0.10' | fmt-arch: ELF-X64 | entry: 0x00000001000000 | exec bytes/segments: 21,069,824/2 ]
┌─────────────┬──────────────────────┬──────────────────────┬───────────────────────┐
│ Gadget Type │ vmlinux-5.0.1 (base) │ vmlinux-5.0.5 (diff) │ vmlinux-5.0.10 (diff) │
├─────────────┼──────────────────────┼──────────────────────┼───────────────────────┤
│ ROP (full) │ 108,380 │ 7,351 (6.78%) │ 556 (0.51%) │
├─────────────┼──────────────────────┼──────────────────────┼───────────────────────┤
│ ROP (part) │ - │ 80,783 (74.54%) │ 78,053 (72.02%) │
├─────────────┼──────────────────────┼──────────────────────┼───────────────────────┤
│ JOP (full) │ 79,685 │ 1,007 (1.26%) │ 276 (0.35%) │
├─────────────┼──────────────────────┼──────────────────────┼───────────────────────┤
│ JOP (part) │ - │ 16,458 (20.65%) │ 12,461 (15.64%) │
├─────────────┼──────────────────────┼──────────────────────┼───────────────────────┤
│ SYS (full) │ 8,276 │ 422 (5.10%) │ 119 (1.44%) │
├─────────────┼──────────────────────┼──────────────────────┼───────────────────────┤
│ SYS (part) │ - │ 4,317 (52.16%) │ 3,864 (46.69%) │
└─────────────┴──────────────────────┴──────────────────────┴───────────────────────┘
注意,这些总计不包括低质量模块(使用 --all
标志包括)。在输出表中,我们看到高达 72.02% 的单个 ROP 模块和 15.64% 的 JOP 模块在所有三个版本中都是可移植的(包括部分匹配)。
致谢
本项目最初是作为《实用二进制分析》一书(由Dennis Andreisse著,链接:"Practical Binary Analysis")第8章习题3的优化解决方案而启动的,并在此基础上构建。
相关资源
关于软件安全的免费书籍:https://highassurance.rs/
许可和贡献
遵循MIT许可协议。欢迎贡献!
依赖项
~16–28MB
~449K SLoC