Super hero Rust fuzzing

Super Hero Rust Fuzzing

Super hero rust firo solutions

Summary:

A guide to finding flaws, 0days and bugs in your rust code using fuzzing with code samples.
This article shows you how to fuzz rust code with afl, hongfuzz and libfuzzer.

Rust is a beautiful language but that doesnt mean you cant break things by poking
at them.

Rust kryptonite

Firo Solutions Kryptonite

Rust is a memory safe language, but safety can also be disabled
and unsafe code can be created by using the “unsafe” function.
Using unsafe rust you can bypass a lot of safety checks the rust compiler does.
Disable the use of unsafe rust:
You can simply tell the compiler to not allow any unsafe rust
in your code by adding

#![forbid(unsafe_code)]

At the beginning of your code.

The awesome folks at the rust core team have
published a book covering the dark arts of rust, linked below:
https://doc.rust-lang.org/stable/nomicon/

Finding Flaws, 0days, and bugs in rust code

How to fuzz rust code

Fuzzing - the art of finding bugs by feeding a program automated generated data

What we want to do is:
* Pic a crate from crates.io
* Pic a function to fuzz, using docs.rs we can easily see a high-level overview of functions we can cherry-pick from
* Feed the function inputs from our fuzzer

When fuzzing with rust you have some options:

AFL

Rust fuzzing with AFL

The legendary American Fuzzy loop which has taken home several prices and
found bugs in a countless amount of software.

Fuzzing rust with AFL is pretty straight forward you create a crate: First of we want to install the AFL crate:

# cargo search afl                                                                                      
afl = "0.8.0"                 # Fuzzing Rust code with american-fuzzy-lop                                              
afl-stat = "0.1.0"            # Parsing AFL status file fuzzer_stats                                                   
afl-sys = "0.0.0"             # Wrapper around AFL source                                                              
bolero-afl = "0.5.0"          # afl plugin for bolero                                                                  
afl-plugin = "0.0.0"          # LLVM instrumentation compiler plugin for afl.rs                                        
roughenough-fuzz = "0.1.0"    # Fuzzing for Roughenough: a Rust implementation 
ol                                                                                                                     
... and 20 crates more (use --limit N to see more)                                                                     


# cargo install afl
    Updating crates.io index
  Downloaded afl v0.8.0
  Downloaded 1 crate (2.7 MB) in 8.13s
  Installing afl v0.8.0
  Downloaded xdg v2.2.0
   Compiling libc v0.2.71
   Compiling semver-parser v0.7.0
   Compiling bitflags v1.2.1
   Compiling unicode-width v0.1.7
   Compiling xdg v2.2.0
   Compiling strsim v0.8.0
   Compiling ansi_term v0.11.0
   Compiling vec_map v0.8.2
   Compiling cc v1.0.54
   Compiling semver v0.9.0
   Compiling textwrap v0.11.0
   Compiling rustc_version v0.2.3
   Compiling afl v0.8.0
   Compiling atty v0.2.14
   Compiling clap v2.33.1
    Finished release [optimized] target(s) in 1m 54s
  Installing /root/.cargo/bin/cargo-afl
   Installed package `afl v0.8.0` (executable `cargo-afl`)

cargo new –bin example cd example/

Sample code:


#[macro_use]
extern crate afl;
extern crate mycrate; //the crate we want to fuzz

fn main() {
    fuzz!(|data: &[u8]| {
	mycrate::myfunction(&data); //call the function you want to fuzz with the input from afl
    });

}

Then we need to build it with AFL:

$ cargo afl build --release

AFL wants us to give it a couple of sample inputs so we should give it some legitimate inputs

$ mkdir in && cd in/
$ echo "test0" > 0.txt
$ echo "t[es@t" > 1.txt
$ echo "l0tes" > 2.txt
$ cd ../

Alternatively we could just grab random data from urandom and feed it:

for ((i=1;i<=12;i++)); do
    head /dev/urandom | tr -dc A-Za-z0-9 | head -c $i > $i.inp.txt
done

Once all that is done we can now start fuzzing the rust code with AFL:

$ cargo afl fuzz -i in -o out target/release/example 

“in” being our directory filled with the input AFL will take
as a template and generate similar input that it will feed the program with.
“out” is our output

Sidenote:

AFL does a lot of writes to the disk and requires a lot of disk usage.
In order to give your hard drives a longer lifetime,
we recommend that you run AFL with a RAM disk system such as tmpfs.

libfuzzer

Rust fuzzing with libfuzzer

LibFuzzer is the easiest approach to fuzzing rust code

Example usage:

Install cargo-fuzz and clone the git repository:

$ cargo install cargo-fuzz
$ git clone mygitrepo/    
$ cd mygitrepo/   

Use cargo-fuzz to initialize and create a fuzzing target

$ cargo fuzz init
$ cargo fuzz list
fuzz_target_1

This will create our fuzzing directory “fuzz” with the file fuzz/fuzz_targets/fuzz_target_1.rs

Sample code:

#![no_main]   
use libfuzzer_sys::fuzz_target;  

fuzz_target!(|data: &[u8]| {  

    use snap::read; //import the crate we want to fuzz
                      
    let mut buf = vec![]; 
    read::FrameDecoder::new(data).read_to_end(&mut buf).unwrap();  
      
    // fuzzed code goes here  
}); 

start libfuzzer

cargo fuzz run fuzz_target_1 --release -s none --debug-assertions

Sidenote:

You can increase the number of threads being used by passing along the –jobs flag.

Replicate the crash

Once you have found a crash you want to be able to run the program with
the same input the fuzzer created in order to report it to the author
luckily libfuzzer will save all crashes in the artifacts directory
so you can simply view it and analyze it

$ cargo fuzz run fuzz_target_1 fuzz/artifacts/fuzz_target_1/crash-803d11

We tried fuzzing snapd which resulted in breaking it after a few seconds:

hongfuzz

Rust fuzzing with hongfuzz
$ cargo install honggfuzz

Create the example directory

$ cargo new --bin example  
$ cd example/  

add hongfuzz and the crate your fuzzing to Cargo.toml [dependencies] honggfuzz = “0.5”

#[macro_use] 
extern crate honggfuzz;

extern crate mycrate; //import the crate you want to fuzz
 
fn main() {
    loop {
        fuzz!(|data: &[u8]| {
		mycrate::thefunction(&data); // pass the data input in to the function you want to fuzz
        });
    }
}

Start fuzzing:

$ cargo hfuzz run example

If you want it to stop fuzzing after it has
found a crash you can simply run it as:

HFUZZ_RUN_ARGS="--exit_upon_crash" cargo hfuzz run example

Replicate the crash

Hong fuzz stores the details of the crash in the hfuzz_workspace directory

$ ls hfuzz_workspace/example/*.fuzz  
hfuzz_workspace/gh/SIGILL.PC.55555555b0d5.STACK.ded8a
0111.CODE.2.ADDR.55555555b0d5.INSTR.popcnt_%rsi,%rdx.fuzz  
$ cargo hfuzz run-debug example hfuzz_workspace/example/SIG
ILL.PC.55555555b0d5.STACK.ded8a
0111.CODE.2.ADDR.55555555b0d5.INSTR.popcnt_%rsi,%rdx.fuzz 

Adress Sanitizer

Address sanitizer is a tool written by Google and implemented into several compilers.
That helps detect memory bugs such as buffer overflows and similar memory bugs.
The Address sanitizer is enabled by default in cargo fuzz.
You can disable the address sanitizer
checks with cargo fuzz by passing along the “-s none” flag.

That said we would like to end it quoting one of the most active people in the rust
fuzzing space Sergey “Shnatsel” Davidoff:

if you want to claim some zero-day vulnerability discoveries to
your name, just pick a crate that has unsafe blocks in it, ideally
with something like mem::uninitialized() or vec.set_len(), and give it
a spin in a “run twice, compare results” fuzzing harness with libdiffuzz.
here should be plenty of low-hanging fruit because nobody’s
tried picking any of it yet.

What have people found so far?

Rust fuzzing trophy
A large chunk of bugs have been found in various crates such as:

  • 13 bugs in the image crate
  • 6 bugs in the claxon crate, one being a security vulnerability(memory discloser)
  • A stack overflow vulnerability in the prost crate
  • 7 bugs in rust itself (rustc) was found with AFL
  • A heap overflow vulnerability in the v_escape crate

Sidenote:

We actually live sync the rust fuzzing trophies into our backend so
that people can use it at rust.firosolutions.com and with other parts of their system

If you want to go more in-dept on fuzzing we recommend that you read hacker
Charlie millers book:
Fuzzing for Software Security Testing and Quality Assurance

https://docs.rs/afl/
https://rust.firosolutions.com/ https://doc.rust-lang.org/nomicon/meet-safe-and-unsafe.html
https://github.com/rust-fuzz/trophy-case
https://medium.com/@shnatsel/auditing-popular-rust-crates-how-a-one-line-unsafe-has-nearly-ruined-everything-fab2d837ebb1
https://medium.com/@shnatsel/how-ive-found-vulnerability-in-a-popular-rust-crate-and-you-can-too-3db081a67fb
https://github.com/seanmonstar/httparse/issues/9
https://github.com/rust-lang-deprecated/rustc-serialize/issues/109
https://github.com/alexcrichton/tar-rs/issues/23
https://github.com/netvl/xml-rs/issues/93