Skip to content

advanced

Rust Targets with AFL Instrumentation

rust-logo google-logo

Need to test a Rust target instrumented with Google's AFL? We'll walk you through how to build Rust targets with AFL instrumentation and test the resulting binaries in Mayhem.

Estimated Time: 15 minutes

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

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

Run through the lesson:

See prerequisites before beginning.

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

    docker build -t <DOCKERHUB_USERNAME>/rust-afl .
    docker push <DOCKERHUB_USERNAME>/rust-afl
    
    docker build -t $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-afl .
    docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-afl
    
  2. Execute a Mayhem run on the forallsecure/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-afl:latest
    duration: 90
    project: mayhem-examples
    target: rust-afl
    cmds:
      - cmd: /mayhemit/target/debug/mayhemit
        afl: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-afl:latest
    duration: 90
    project: mayhem-examples
    target: rust-afl
    cmds:
      - cmd: /mayhemit/target/debug/mayhemit
        afl: 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 AFL 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-afl-run

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

Testing a Rust Target with AFL Instrumentation

File: rust-afl.tgz

Download and extract the rust-afl.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
#[macro_use]
extern crate afl;

use std::process;

fn main() {
    fuzz!(|data: &[u8]| {
        if data[0] == 'b' as u8 {
            if data[1] == 'u' as u8 {
                if data[2] == 'g' as u8 {
                    process::abort();
                }
            }
        }
    });
}

Here we see the external crate afl library is imported into the program at the top of the source file, and that there are three main functions: main and fuzz!.

The main function serves as the entrypoint to the program, containing the fuzz! function provided by the afl crate that reads bytes from standard input and passes the bytes to the underlying logic. In particular, if the bytes read as "bug", then the abort() function is initialized to produce the improper input validation defect.

Let's now take a look at how the rust-afl 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
FROM rust:1.44-buster as rust-target
RUN cargo install afl --version 0.7.0
COPY mayhemit.rs .
RUN export USER=root && \
    cargo new mayhemit && \
    cd mayhemit && \
    mv /mayhemit.rs src/main.rs && \
    echo afl = '"0.4"' >> Cargo.toml && \
    cargo afl build
RUN mkdir /testsuite && echo seed > /testsuite/seed

# Set to fuzz!
ENTRYPOINT ["cargo", "afl", "fuzz", "-i", "/tmp", "-o", "/out"]
CMD ["/mayhemit/target/debug/mayhemit"]
  • Line 1: The rust:1.44-buster base image is imported to gather the necessary Rust dependencies.
  • Line 2: The afl crate library is installed.
  • Line 3: The mayhemit.rs source file is copied into the Docker container.
  • Lines 4-9: The AFL cargo subcommand (provided by the afl crate) is used to compile the mayhemit.rs source code into the mayhemit Rust executable.
  • Line 14: The /mayhemit/target/debug/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 Docker Hub 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 $DOCKER_REGISTRY represents the URL of the internal Mayhem Docker registry.

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

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-afl 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-afl Docker image. Confirm that your Mayhemfile looks similar to the following:

1
2
3
4
5
6
7
image: <DOCKERHUB_USERNAME>/rust-afl:latest
duration: 90
project: mayhem-examples
target: rust-afl
cmds:
  - cmd: /mayhemit/target/debug/mayhemit
    afl: true
1
2
3
4
5
6
7
image: $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-afl:latest
duration: 90
project: mayhem-examples
target: rust-afl
cmds:
  - cmd: /mayhemit/target/debug/mayhemit
    afl: true

Note

Make sure that the cmd is specifically set to /mayhemit/target/debug/mayhemit without any parameters like @@ that instruct Mayhem to fuzz a target using file inputs. This is because the rust afl crate library fuzzes using standard input only. Check out the Rust Fuzz Book for more information.

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-afl-run

Congratulations! You just tested a Rust target with AFL instrumentation in Mayhem!

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

Now that you know how to build and test a Rust AFL 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.

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

Instructions:

  • Modify the mayhemit.rs source code by adding a max length constraint and add the following index out-of-bounds defect:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    fuzz!(|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-afl-mayhemit-out-of-bounds.

  • Push the <DOCKERHUB_USERNAME>/rust-afl-mayhemit-out-of-bounds Docker image to the public Docker Hub registry using the docker push command.
  • Fuzz the <DOCKERHUB_USERNAME>/rust-afl-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-afl-mayhemit-out-of-bounds.
  • Push the $MAYHEM_DOCKER_REGISTRY/rust-afl-mayhemit-out-of-bounds Docker image to the private Mayhem Docker registry using the docker push command.
  • Test the $MAYHEM_DOCKER_REGISTRY/rust-afl-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 AFL Instrumentation

Solution

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

First things first, you needed to add the max length constraint data.len() < 5 and the erroneous call for data[10] so that when the fuzz! function is fuzzed, the input test case "bug" will trigger the corresponding index out-of-bounds error.

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

fn main() {
    fuzz!(|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-afl-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-afl-mayhemit-out-of-bounds:

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

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

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

docker push <DOCKERHUB_USERNAME>/rust-afl-mayhemit-out-of-bounds
docker push $MAYHEM_DOCKER_REGISTRY/rust-afl-mayhemit-out-of-bounds

Alternatively, you could have also used the included Makefile to easily build and push the resulting Docker image by setting a MAYHEM_DOCKER_REGISTRY environment variable and running the following commands:

make build
make push

Lastly, you could have executed a Mayhem run on the uploaded <DOCKERHUB_USERNAME>/rust-afl-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-afl-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-afl-mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: mayhemit-out-of-bounds
cmds:
  - cmd: /mayhemit/target/debug/mayhemit
    afl: true
1
2
3
4
5
6
7
image: $MAYHEM_DOCKER_REGISTRY/rust-afl-mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: mayhemit-out-of-bounds
cmds:
  - cmd: /mayhemit/target/debug/mayhemit
    afl: 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! You just built a Rust target with AFL instrumentation from scratch and used Mayhem to detect the bug that you added!

✏️ Summary and Recap

In this lesson, you learned how to fuzz Rust targets with AFL instrumentation in Mayhem!


I learned how to...

1. Build and test a Rust target with AFL instrumentation 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
    fn main() {
        fuzz!(|data: &[u8]| {
            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 AFL instrumentation, 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
    FROM rust:1.44-buster as rust-target
    RUN cargo install afl --version 0.7.0
    COPY mayhemit.rs .
    RUN export USER=root && \
        cargo new mayhemit && \
        cd mayhemit && \
        mv /mayhemit.rs src/main.rs && \
        echo afl = '"0.4"' >> Cargo.toml && \
        cargo afl build
    RUN mkdir /testsuite && echo seed > /testsuite/seed
    
    # Set to fuzz!
    ENTRYPOINT ["cargo", "afl", "fuzz", "-i", "/tmp", "-o", "/out"]
    CMD ["/mayhemit/target/debug/mayhemit"]
    

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/rust-afl:latest
    duration: 90
    project: mayhem-examples
    target: rust-afl
    cmds:
      - cmd: /mayhemit/target/debug/mayhemit
        afl: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/rust-afl:latest
    duration: 90
    project: mayhem-examples
    target: rust-afl
    cmds:
      - cmd: /mayhemit/target/debug/mayhemit
        afl: true
    
2. Build and fuzz a Rust target with AFL instrumentation 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
    13
    14
    fn main() {
        fuzz!(|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 AFL instrumentation, 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
    FROM rust:1.44-buster as rust-target
    RUN cargo install afl --version 0.7.0
    COPY mayhemit.rs .
    RUN export USER=root && \
        cargo new mayhemit && \
        cd mayhemit && \
        mv /mayhemit.rs src/main.rs && \
        echo afl = '"0.4"' >> Cargo.toml && \
        cargo afl build
    RUN mkdir /testsuite && echo seed > /testsuite/seed
    
    # Set to fuzz!
    ENTRYPOINT ["cargo", "afl", "fuzz", "-i", "/tmp", "-o", "/out"]
    CMD ["/mayhemit/target/debug/mayhemit"]
    

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/rust-afl-mayhemit-out-of-bounds:latest
    duration: 90
    project: mayhem-examples
    target: mayhemit-out-of-bounds
    cmds:
      - cmd: /mayhemit/target/debug/mayhemit
        afl: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/rust-afl-mayhemit-out-of-bounds:latest
    duration: 90
    project: mayhem-examples
    target: mayhemit-out-of-bounds
    cmds:
      - cmd: /mayhemit/target/debug/mayhemit
        afl: true