コンテンツにスキップ

advanced

Rust Targets with LibFuzzer (Cargo-Fuzz) Instrumentation

rust-logo llvm-logo

Need to test a Rust target with libFuzzer instrumentation? We'll walk you through how to test libFuzzer Rust targets using cargo-fuzz!

Estimated Time: 15 minutes

By the end of this lesson, you will be able to:

  1. Build and fuzz a libFuzzer instrumented Rust target with cargo-fuzz for an improper input validation defect.
  2. Build and fuzz a libFuzzer instrumented Rust target with cargo-fuzz for an index out-of-bounds defect.

Run through the lesson:

See prerequisites before beginning.

  1. Download the rust-cargo-fuzz.tgz and build the rust-cargo-fuzz Docker image, and push it to the specified Docker registry:

    docker build -t <DOCKERHUB_USERNAME>/rust-cargo-fuzz .
    docker push <DOCKERHUB_USERNAME>/rust-cargo-fuzz
    
    docker build -t $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-cargo-fuzz .
    docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-cargo-fuzz
    
  2. Execute a Mayhem run on the rust-afl Docker image using either the Mayhem UI or Mayhem CLI with the following Mayhemfile:

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/rust-cargo-fuzz:latest
    duration: 90
    project: rust
    target: rust-cargo-fuzz
    cmds:
      - cmd: /mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit
        libfuzzer: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-cargo-fuzz:latest
    duration: 90
    project: rust
    target: rust-cargo-fuzz
    cmds:
      - cmd: /mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit
        libfuzzer: true
    

You will need the following:

  • Docker installed.
  • A valid Internet connection (for pulling Docker Hub base images)

One Click Testing

Click on the button below to start testing a Rust target with libFuzzer instrumentation! Click Next until you reach the final confirmation page and then hit Start Run!

You should see a Run page similar to the following:

rust-cargo-fuzz-run

Now that you've seen Mayhem testing a Rust target with libFuzzer instrumentation, let's walk through end-to-end how the Rust target was built!

Building and Testing a Rust Target with Cargo-Fuzz

File: rust-cargo-fuzz.tgz

Download and extract the above rust-cargo-fuzz.tgz and take a look at the following bugged mayhemit.rs program:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#![no_main]
#[macro_use] extern crate libfuzzer_sys;

use std::process;

fuzz_target!(|data: &[u8]| {
    if data.len() >= 3 {
        if data[0] == 'b' as u8 {
            if data[1] == 'u' as u8 {
                if data[2] == 'g' as u8 {
                    process::abort();
                }
            }
        }
    }
});

Here we see that the external crate module libfuzzer_sys is imported into the program and that the function fuzz_target! serves as the entrypoint to the fuzzer, taking in the input parameter data and checking to see if the input test case data contains the word "bug". If so, the program crashes with an improper input validation defect.

Let's now take a look at how the rust-cargo-fuzz target will be built. Looking at the associated Dockerfile we can see the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
FROM fuzzers/cargo-fuzz:0.10.0
COPY mayhemit.rs .
RUN export USER=root && \
    cargo new mayhemit && \
    cd mayhemit && \
    cargo fuzz init && \
    cd fuzz && \
    sed -i 's/fuzz_target_1/mayhemit/g' Cargo.toml && \
    rm fuzz_targets/fuzz_target_1.rs && \
    cp /mayhemit.rs /mayhemit/fuzz/fuzz_targets && \
    cargo fuzz build

# Set to fuzz!
ENTRYPOINT []
CMD ["/mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit"]
  • Line 1: The fuzzers/cargo-fuzz:0.10.0 base image is imported to gather the necessary Rust and cargo-fuzz dependencies.
  • Line 2: The mayhemit.rs source file is copied into the Docker container.
  • Lines 3-11: The new cargo package mayhemit is created and the mayhemit.rs source code is copied into the appropriate directories for the cargo fuzz build utility to compile the mayhemit executable.
  • Line 14: The /mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit executable is defined as the default executable for the resulting Docker image.

Next, we need to build and push the resulting Docker image to the Docker Hub registry using the docker build and docker push commands.

Next, we need to build and push the resulting Docker image to the Mayhem server using the docker build and docker push commands, where $MAYHEM_DOCKER_REGISTRY represents the URL of the private Mayhem Docker registry.

docker build -t <DOCKERHUB_USERNAME>/rust-cargo-fuzz .
docker push <DOCKERHUB_USERNAME>/rust-cargo-fuzz
docker build -t $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-cargo-fuzz .
docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-cargo-fuzz

Info

You can use the mayhem login command to find your internal Mayhem Docker Registry URL and run the following command to set the DOCKER_REGISTRY environment variable, like so:

export DOCKER_REGISTRY=tutorial.forallsecure.com:5000
Here, we've provided an example Mayhem Docker registry URL, but you will need to set the DOCKER_REGISTRY environment variable for your specific Mayhem Docker Registry URL.

Upon successfully pushing the newly created Docker image to the public Docker Hub registry, create a new run via the Mayhem UI and search for the <DOCKERHUB_USERNAME>/rust-cargo-fuzz Docker image. Confirm that your Mayhemfile looks similar to the following:

Upon successfully pushing the newly created Docker image to the private Mayhem Docker Registry, create a new run via the Mayhem UI and search for the forallsecure/rust-cargo-fuzz Docker image. Confirm that your Mayhemfile looks similar to the following:

1
2
3
4
5
6
7
image: <DOCKERHUB_USERNAME>/rust-cargo-fuzz:latest
duration: 90
project: rust
target: rust-cargo-fuzz
cmds:
  - cmd: /mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit
    libfuzzer: true
1
2
3
4
5
6
7
image: $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-cargo-fuzz:latest
duration: 90
project: rust
target: rust-cargo-fuzz
cmds:
  - cmd: /mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit
    libfuzzer: true

Now just click Next until you reach the final confirmation page of the create new run flow and hit Start Run to execute your Mayhem run! You should see a Run page similar to the following:

rust-cargo-fuzz-run

Congratulations! You just tested a Rust target with libFuzzer instrumentation using cargo-fuzz!

Real World Exercise: Building and Testing the mayhemit-out-of-bounds Rust Target with Cargo-Fuzz

Now that you know how to build and test a Rust libFuzzer target with an improper input validation defect, let's see if you can modify the source code to use an index out-of-bounds defect instead!

File: mayhemit-out-of-bounds-unsolved.zip

  • Modify the mayhemit.rs source code and add the array out-of-bounds defect:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    fuzz_target!(|data: &[u8]| {
        if data.len() >= 3 && data.len() < 5 {
            if data[0] == 'b' as u8 {
                if data[1] == 'u' as u8 {
                    if data[2] == 'g' as u8 {
                        let x;
                        x = data[10];
                    }
                }
            }
        }
    });
    
  • Rebuild the Dockerfile using the docker build command and tag the resulting Docker image as <DOCKERHUB_USERNAME>/rust-cargo-fuzz-mayhemit-out-of-bounds.

  • Push the <DOCKERHUB_USERNAME>/rust-cargo-fuzz-mayhemit-out-of-bounds Docker image to the public Docker Hubs registry using the docker tag and docker push commands.
  • Fuzz the <DOCKERHUB_USERNAME>/rust-cargo-fuzz-mayhemit-out-of-bounds Docker image using either the Mayhem UI or Mayhem CLI. Make sure to set the associated Mayhemfile accordingly.
  • Rebuild the Dockerfile using the docker build command and tag the resulting Docker image as $MAYHEM_DOCKER_REGISTRY/rust-cargo-fuzz-mayhemit-out-of-bounds.
  • Push the $MAYHEM_DOCKER_REGISTRY/rust-cargo-fuzz-mayhemit-out-of-bounds Docker image to the private Mayhem Docker registry using the docker tag and docker push commands.
  • Test the $MAYHEM_DOCKER_REGISTRY/rust-cargo-fuzz-mayhemit-out-of-bounds Docker image using either the Mayhem UI or Mayhem CLI. Make sure to set the associated Mayhemfile accordingly.

🔍 Review It! Building and Testing the mayhemit-out-of-bounds Rust Target with Cargo-Fuzz

Solution

Solution: mayhemit-out-of-bounds-solved.tgz

First things first, you needed to add the index out-of-bounds code snippet to the fuzz_target! function so that when the fuzz_target! function is tested, the input test case "bug" will trigger the index out-of-bounds error. This is because the code will only proceed to the if statements if data.len() >= 3 and data.len() < 5. Therefore, when attempting to set the variable x to data[10], this will produce the index out-of-bounds error.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#![no_main]
#[macro_use] extern crate libfuzzer_sys;

fuzz_target!(|data: &[u8]| {
    if data.len() >= 3 && data.len() < 5 {
        if data[0] == 'b' as u8 {
            if data[1] == 'u' as u8 {
                if data[2] == 'g' as u8 {
                    let x;
                    x = data[10];
                }
            }
        }
    }
});

Then, you needed to run the docker build command in the same directory as the Dockerfile and simultaneously tag the resulting Docker image as <DOCKERHUB_USERNAME>/rust-cargo-fuzz-mayhemit-out-of-bounds:

Then, you needed to run the docker build command in the same directory as the Dockerfile and simultaneously tag the resulting Docker image as $MAYHEM_DOCKER_REGISTRY/rust-cargo-fuzz-mayhemit-out-of-bounds:

docker build -f Dockerfile -t <DOCKERHUB_USERNAME>/rust-cargo-fuzz-mayhemit-out-of-bounds .
docker build -f Dockerfile -t $MAYHEM_DOCKER_REGISTRY/rust-cargo-fuzz-mayhemit-out-of-bounds .

Next, you had to push the <DOCKERHUB_USERNAME>/rust-cargo-fuzz-mayhemit-out-of-bounds Docker image to the public Docker Hub registry:

Next, you had to push the $MAYHEM_DOCKER_REGISTRY/rust-cargo-fuzz-mayhemit-out-of-bounds Docker image to the private Mayhem Docker registry:

docker push <DOCKERHUB_USERNAME>/rust-cargo-fuzz-mayhemit-out-of-bounds
docker push $MAYHEM_DOCKER_REGISTRY/rust-cargo-fuzz-mayhemit-out-of-bounds
make build
make push

Lastly, you could have executed a Mayhem run on the uploaded <DOCKERHUB_USERNAME>/rust-cargo-fuzz-mayhemit-out-of-bounds Docker image using either the Mayhem UI or Mayhem CLI. As long as your Mayhemfile looked similar to the following:

Lastly, you could have executed a Mayhem run on the uploaded $MAYHEM_DOCKER_REGISTRY/rust-cargo-fuzz-mayhemit-out-of-bounds Docker image using either the Mayhem UI or Mayhem CLI. As long as your Mayhemfile looked similar to the following:

1
2
3
4
5
6
7
image: <DOCKERHUB_USERNAME>/rust-cargo-fuzz-mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: rust-cargo-fuzz-mayhemit-out-of-bounds
cmds:
  - cmd: /mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit
    libfuzzer: true
1
2
3
4
5
6
7
image: $MAYHEM_DOCKER_REGISTRY/rust-cargo-fuzz-mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: rust-cargo-fuzz-mayhemit-out-of-bounds
cmds:
  - cmd: /mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit
    libfuzzer: true

Your final Run page should have looked like the following:

mayhemit-out-of-bounds-run

Congratulations! Mayhem found the index out-of-bounds defect that you added! You just built a Rust target with libFuzzer instrumentation from scratch and used cargo-fuzz to detect the vulnerability that you added!

✏️ Summary and Recap

In this lesson, you learned how to fuzz Rust with libFuzzer instrumentation using cargo-fuzz in Mayhem!


I learned how to...

1. Build and test a libFuzzer instrumented Rust target with cargo-fuzz for an improper input validation defect.
  • The source code should contain the following defect:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    fuzz_target!(|data: &[u8]| {
        if data.len() >= 3 {
            if data[0] == 'b' as u8 {
                if data[1] == 'u' as u8 {
                    if data[2] == 'g' as u8 {
                        process::abort();
                    }
                }
            }
        }
    });
    

  • Then, to fuzz the Rust target with cargo-fuzz, use the following Dockerfile and Mayhemfile to build the Docker image containing the Rust program and fuzz it in Mayhem, respectively:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    FROM fuzzers/cargo-fuzz:0.10.0
    COPY mayhemit.rs .
    RUN export USER=root && \
        cargo new mayhemit && \
        cd mayhemit && \
        cargo fuzz init && \
        cd fuzz && \
        sed -i 's/fuzz_target_1/mayhemit/g' Cargo.toml && \
        rm fuzz_targets/fuzz_target_1.rs && \
        cp /mayhemit.rs /mayhemit/fuzz/fuzz_targets && \
        cargo fuzz build
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD ["/mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit"]
    

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/rust-cargo-fuzz:latest
    duration: 90
    project: rust
    target: rust-cargo-fuzz
    cmds:
      - cmd: /mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit
        libfuzzer: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-cargo-fuzz:latest
    duration: 90
    project: rust
    target: rust-cargo-fuzz
    cmds:
      - cmd: /mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit
        libfuzzer: true
    
2. Build and fuzz a libFuzzer instrumented Rust target with cargo-fuzz for an index out-of-bounds defect.
  • The source code should contain the following defect:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    fuzz_target!(|data: &[u8]| {
        if data.len() >= 3 && data.len() < 5 {
            if data[0] == 'b' as u8 {
                if data[1] == 'u' as u8 {
                    if data[2] == 'g' as u8 {
                        let x;
                        x = data[10];
                    }
                }
            }
        }
    });
    

  • Then, to fuzz the Rust target with cargo-fuzz, use the following Dockerfile and Mayhemfile to build the Docker image containing the Rust program and fuzz it in Mayhem, respectively:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    FROM fuzzers/cargo-fuzz:0.10.0
    COPY mayhemit.rs .
    RUN export USER=root && \
        cargo new mayhemit && \
        cd mayhemit && \
        cargo fuzz init && \
        cd fuzz && \
        sed -i 's/fuzz_target_1/mayhemit/g' Cargo.toml && \
        rm fuzz_targets/fuzz_target_1.rs && \
        cp /mayhemit.rs /mayhemit/fuzz/fuzz_targets && \
        cargo fuzz build
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD ["/mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit"]
    

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/rust-cargo-fuzz-mayhemit-out-of-bounds:latest
    duration: 90
    project: mayhem-examples
    target: rust-cargo-fuzz-mayhemit-out-of-bounds
    cmds:
      - cmd: /mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit
        libfuzzer: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/rust-cargo-fuzz-mayhemit-out-of-bounds:latest
    duration: 90
    project: mayhem-examples
    target: rust-cargo-fuzz-mayhemit-out-of-bounds
    cmds:
      - cmd: /mayhemit/fuzz/target/x86_64-unknown-linux-gnu/release/mayhemit
        libfuzzer: true