Fast, parallel, cross-variant ROP/JOP gadget search for x86 (32-bit) and x64 (64-bit) binaries. Uses the iced-x86 disassembler library.
Current state: decent test coverage, but still in beta. Issues/PRs welcome :)
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 commandline options
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
, Ropper
, and rp
.
Though not yet as mature as some of its contemporaries, it contains unique and experimental functionality.
To the best of our knowledge, xgadget
is the first gadget search tool to have these features:
pop
instruction or matches for a given regex
--reg-ctrl <optional_register_name>
flag--dispatcher
pop rsp; add [rax-0x77], cl; ret ------------------------------------- [ 0xc748d ]
pop rsp; add [rax-0x77], cl; ret; --- [ 'bin_v1.1': 0xc748d, 'bin_v1.2': 0xc9106 ]
Other features include:
Find gadgets:
```rust use xgadget;
let maxgadgetlen = 5;
// Search single binary let searchconfig = xgadget::SearchConfig::DEFAULT; let bin1 = xgadget::Binary::frompathstr("/path/to/binv1").unwrap(); let bins = vec![bin1]; let gadgets = xgadget::findgadgets(&bins, maxgadgetlen, searchconfig).unwrap(); let stackpivotgadgets = xgadget::filterstackpivot(&gadgets);
// Search for cross-variant gadgets, including partial matches let searchconfig = xgadget::SearchConfig::DEFAULT | xgadget::SearchConfig::PART; let bin1 = xgadget::Binary::frompathstr("/path/to/binv1").unwrap(); let bin2 = xgadget::Binary::frompathstr("/path/to/binv2").unwrap(); let bins = vec![bin1, bin2]; let crossgadgets = xgadget::findgadgets(&bins, maxgadgetlen, searchconfig).unwrap(); let crossregpopgadgets = xgadget::filterregpoponly(&cross_gadgets); ```
Custom filters can be created using the GadgetAnalysis
object and/or functions from the semantics
module.
How the above 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 filterstackpivot<'a>(gadgets: &[Gadget<'a>]) -> Vec
Run xgadget --help
:
```ignore xgadget v0.7.0
About: Fast, parallel, cross-variant ROP/JOP gadget search for x86/x64 binaries. Cores: 8 logical, 8 physical
USAGE:
xgadget [OPTIONS] ARGS:
OPTIONS:
-a, --arch ``` Build a dynamically-linked binary from source and install it locally: Commits to this repo's Unfortunately the statically-linked binary is several times slower on an i7-9700K, likely due to the built-in memory allocator for target Tools that attempt to automate ROP/JOP chain generation require heavyweight analysis - typically symbolic execution of an intermediate representation.
This works well for small binaries and CTF problems, but tends to be error-prone and difficult to scale for large, real-world programs.
At present, To build a Docker container and connect to it: The final build step runs Once it's done, run On an i7-9700K (8C/8T, 3.6GHz base, 4.9 GHz max) machine with The ```bash
root@container# cd ./benches/kernels/
root@container# xgadget vmlinux-5.0.1 vmlinux-5.0.5 vmlinux-5.0.10 --fess
TARGET 0 - 'vmlinux-5.0.1': ELF-X64, 0x00000001000000 entry, 21065728/2 executable bytes/segments
TARGET 1 - 'vmlinux-5.0.5': ELF-X64, 0x00000001000000 entry, 21069824/2 executable bytes/segments
TARGET 2 - 'vmlinux-5.0.10': ELF-X64, 0x00000001000000 entry, 21069824/2 executable bytes/segments +-------------+----------------------+----------------------+-----------------------+
| Gadget Type | vmlinux-5.0.1 (base) | vmlinux-5.0.5 (diff) | vmlinux-5.0.10 (diff) |
+-------------+----------------------+----------------------+-----------------------+
| ROP (full) | 175,740 | 11,124 (6.33%) | 699 (0.40%) |
+-------------+----------------------+----------------------+-----------------------+
| ROP (part) | - | 85,717 (48.77%) | 79,367 (45.16%) |
+-------------+----------------------+----------------------+-----------------------+
| JOP (full) | 97,239 | 1,093 (1.12%) | 277 (0.28%) |
+-------------+----------------------+----------------------+-----------------------+
| JOP (part) | - | 16,792 (17.27%) | 12,635 (12.99%) |
+-------------+----------------------+----------------------+-----------------------+
| SYS (full) | 81 | 20 (24.69%) | 20 (24.69%) |
+-------------+----------------------+----------------------+-----------------------+
| SYS (part) | - | 59 (72.84%) | 58 (71.60%) |
+-------------+----------------------+----------------------+-----------------------+
``` In the output table, we see that up to 45.16% of gadgets of individual ROP gadgets are portable across all three versions (counting partial matches). This project started as an optimized solution to Chapter 8, exercise 3 of "Practical Binary Analysis" by Dennis Andreisse [6], and builds on the design outlined therein. Licensed under the MIT license.
Contributions are welcome!CLI Build and Install (Recommended)
bash
cargo install xgadget --features cli-bin # Build on host (pre-req: https://www.rust-lang.org/tools/install)
CLI Binary Releases for Linux
master
branch automatically run integration tests and build a statically-linked binary for 64-bit Linux.
You can download it here to try out the CLI immediately, instead of building from source.
Static binaries for Windows may also be supported in the future.x86_64-unknown-linux-musl
.
So building a dynamically-linked binary from source with the above cargo install
command is highly recommended for performance (links against your system's allocator).Why No Chain Generation?
xgadget
has a different goal: enable an expert user to manually craft stable exploits by providing fast, accurate gadget discovery.~~Yeah, but can it do 10 OS kernels under 10 seconds?!~~ Repeatable Benchmark Harness
bash
user@host$ git clone git@github.com: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
.
This script downloads and builds 10 consecutive Linux kernels (versions 5.0.1
to 5.0.10
- with x86_64_defconfig
).
Grab a coffee, it can take a while.cargo bench
to search all 10 kernels for common gadgets (among other benchmarks):bash
root@container:/xgadget# cargo bench
gcc
version 8.4.0: the average runtime, to process all ten 54MB kernels simultaneously with a max gadget length of 5 instructions and full-match search for all gadget types (ROP, JOP, and syscall gadgets), is only 6.3 seconds! Including partial matches as well takes just 7.9 seconds.Fast Exploit Similarity Score (FESS)
--fess
flag uses cross-variant gadget matching as metric of binary similarity.
It's a experiment in anti-diversification for exploitation.
To view similarity scores for kernel versions 5.0.1
, 5.0.5
, and 5.0.10
within the container:Acknowledgements
License and Contributing
References