# xgadget [![crates.io](https://img.shields.io/crates/v/xgadget.svg)](https://crates.io/crates/xgadget) [![docs.rs](https://docs.rs/xgadget/badge.svg)](https://docs.rs/xgadget/) [![GitHub Actions](https://github.com/entropic-security/xgadget/workflows/test/badge.svg)](https://github.com/entropic-security/xgadget/actions) [![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://github.com/entropic-security/xgadget/blob/main/LICENSE) Fast, parallel, cross-{patch,compiler}-variant ROP/JOP gadget search for x86 (32-bit) and x64 (64-bit) binaries. Uses the [iced-x86 disassembler library](https://github.com/icedland/iced). This crate can be used as a **CLI binary** (Windows/Linux/MacOS) or a **library** (7 well-known dependencies, all Rust). ### Quickstart Install the CLI tool and show its help menu: ```bash cargo install xgadget --features cli-bin # Build on host (pre-req: https://www.rust-lang.org/tools/install) xgadget --help # List available command line options ``` ### How do ROP and JOP code reuse attacks work? * **Return Oriented Programming (ROP)** introduced *code-reuse* attacks, after hardware mitigations (aka NX, DEP) made *code-injection* less probable (no simultaneous `WRITE` and `EXECUTE` memory permissions). An attacker with stack control chains together short, existing sequences of assembly (aka "gadgets") - should a leak enable computing gadget addresses in the face of ASLR. When contiguous ROP gadget addresses are written to a corrupted stack, each gadget's ending `ret` instruction pops the next gadget's address into the CPU's instruction pointer. The result? Turing-complete control over a victim process.
ROP Attack Model (recreated from: Bletsch et. al.)
* **Jump Oriented Programming (JOP)** is a newer code reuse method which, unlike ROP, doesn't rely on stack control. The attack *bypasses* hardware-assisted shadow-stack implementations (e.g. Intel CET's shadow stack), and is *limited* but *not prevented* by prototype-insensitive indirect target checks (e.g. Intel CET's IBT). JOP allows storing a table of gadget addresses in any `READ`/`WRITE` memory location. Instead of piggy-backing on call-return semantics to execute a gadget list, a "dispatch" gadget (e.g. `add rax, 8; jmp [rax]`) controls table indexing. Chaining happens if each gadget ends with a `jmp` back to the dispatcher (instead of a `ret`).
JOP Attack Model (recreated from: Bletsch et. al.)
### About `xgadget` is a tool for **Return-Oriented Programming (ROP)** and **Jump-Oriented Programming (JOP)** exploit development. It's a fast, multi-threaded alternative to awesome tools like [`ROPGadget`](https://github.com/JonathanSalwan/ROPgadget), [`Ropper`](https://github.com/sashs/Ropper), and [`rp`](https://github.com/0vercl0k/rp). The goal is supporting practical usage while simultaneously exploring unique and experimental features. To the best of our knowledge, `xgadget` is the first gadget search tool to be: * **Fast-register-sensitive**: Filters gadgets by register usage behavior, not just matches for a given regex, without SMT solving (more powerful, but often impractical). * `--reg-overwrite [
Cross-variant Full Match
2. ***Partial-match*** - Same instruction sequence, different program counter: gadget logic portable. Example: * Gadget: `pop rdi; ret;` * Address in `bin_v1.1`: `0xc748d` * Address in `bin_v1.2`: `0xc9106`
Cross-variant Partial Match
* This is entirely optional, you're free to run this tool on a single binary. Other features include: * Supports ELF32, ELF64, PE32, PE32+, Mach-O, and raw files * Parallel across available cores, whether searching a single binary or multiple variants * Currently 8086/x86/x64 only (uses a speed-optimized, arch-specific disassembler) ### CLI Examples Run `xgadget --help` to enumerate available options. * **Example:** Search `/usr/bin/sudo` for reliable ways to control `rdi`: ```bash xgadget /usr/bin/sudo --reg-only --reg-overwrite rdi ``` * **Example:** Search for ROP gadgets that control the value of `rdi`, never read `rsi` or `rdx`, and occur at addresses that don't contain bytes `0x32` or `0x0d`: ```bash xgadget /usr/bin/sudo --rop --reg-overwrite rdi --reg-no-read rsi rdx --bad-bytes 0x32 0x0d ``` * **Example:** Search `/usr/bin/sudo` for "pop, pop, {jmp,call}" gadgets up to 10 instructions long, print results using AT&T syntax: ```bash xgadget /usr/bin/sudo --jop --reg-pop --att --max-len 10 ``` * **Example:** Same as above, except using a regex filter to match "pop, pop, {jmp,call}" instruction strings (slower/less-accurate here, but regex enables flexible search in general): ```bash xgadget /usr/bin/sudo --regex-filter "^(?:pop)(?:.*(?:pop))*.*(?:call|jmp)" --att --max-len 10 ``` * **Example:** Examine the exploit mitigations binaries `sudo` and `lighttpd` have been compiled with: ```bash xgadget /usr/bin/sudo /usr/sbin/lighttpd --check-sec ``` * **Example:** List imported and internal symbols for `lighttpd`: ```bash xgadget /usr/sbin/lighttpd --symbols ``` ### API Usage Find gadgets: ```rust,no_run 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); ``` Custom filters can be created using the [`GadgetAnalysis`](crate::GadgetAnalysis) object and/or functions from the [`semantics`](crate::semantics) module. How the above [`filter_stack_pivot`](crate::filters::filter_stack_pivot) function is implemented: ```rust 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