#gadget #x86-64 #rop #jop

bin+lib xgadget

针对 x86/x64 可执行文件的快速、并行、跨变体 ROP/JOP gadget 搜索

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 开发工具

Download history 7/week @ 2024-03-09 1/week @ 2024-03-16 19/week @ 2024-03-30 1/week @ 2024-04-06 40/week @ 2024-04-27 3/week @ 2024-05-04

每月 103 次下载

MIT 许可证

170KB
3.5K SLoC

xgadget

crates.io docs.rs GitHub Actions License: MIT

针对 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)使得 代码注入 不太可能(没有同时具有 WRITEEXECUTE 内存权限)之后。攻击者利用栈控制,将短小的现有汇编序列(称为 "gadget")链接在一起——如果泄露允许在 ASLR 面前计算 gadget 地址。当连续的 ROP gadget 地址写入已损坏的栈中时,每个 gadget 的结束 ret 指令将下一个 gadget 的地址弹出至 CPU 的指令指针。结果?对受害进程的图灵完备控制。

rop model

ROP 攻击模型(重新创建自: Bletsch 等。)

  • 跳转导向编程 (JOP) 是一种较新的代码重用方法,与 ROP 不同,它不依赖于栈控制。攻击 绕过 硬件辅助的影子栈实现(例如 Intel CET 的影子栈),并且 有限不是阻止 原型无关的间接目标检查(例如 Intel CET 的 IBT)。JOP 允许在任何 READ/WRITE 内存位置存储 gadget 地址表。而不是利用调用返回语义来执行 gadget 列表,一个 "调度" gadget(例如 add rax, 8; jmp [rax])控制表索引。如果每个 gadget 以跳转回调度器(而不是 ret)结束,则会发生链式反应。

jop model

JOP 攻击模型(重新创建自: Bletsch 等。)

关于

xgadget 是一个用于 返回导向编程(ROP)跳转导向编程(JOP) 漏洞开发工具。它是一个快速、多线程的替代方案,类似于 ROPGadgetRopperrp 等优秀的工具。

目标是支持实际应用,同时探索独特和实验性功能。据我们所知,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(例如,针对不同程序或编译器版本的抗多样化)。两种策略

  1. 完全匹配 - 相同的指令序列,相同的程序计数器:gadget 可以完全重用。例如
    • Gadget: pop rdi; ret;
    • 地址(在所有二进制文件中):0xc748d

full match

跨变体 完全匹配

  1. 部分匹配 - 相同的指令序列,不同的程序计数器:gadget 逻辑可移植。例如
    • Gadget: pop rdi; ret;
    • bin_v1.1 中的地址:0xc748d
    • bin_v1.2 中的地址:0xc9106

partial match

跨变体 部分匹配

  • 这完全是可选的,您可以自由地对单个二进制文件运行此工具。

其他功能包括

  • 支持 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 模块,永远不要读取 rsirdx,且出现在不包含字节 0x320x0d 的地址上
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
  • 示例:检查 sudolighttpd 编译的漏洞缓解二进制文件
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.15.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.15.0.55.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