Skip to content

advanced

Java Targets with LibFuzzer Instrumentation via Jazzer

java-logo llvm-logo

Need to test a Java target with libFuzzer instrumentation? We'll walk you through how to instrument Java targets with libFuzzer/Jazzer and test the executable in Mayhem!

Estimated Time: 15 minutes

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

  1. Build and fuzz a libFuzzer Java target with an uncaught exception defect.
  2. Build and fuzz a libFuzzer Java target with an index out-of-bounds defect.

Run through the lesson:

See prerequisites before beginning.

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

    docker build -t <DOCKERHUB_USERNAME>/java-jazzer .
    docker push <DOCKERHUB_USERNAME>/java-jazzer
    
    docker build -t $MAYHEM_DOCKER_REGISTRY/forallsecure/java-jazzer .
    docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/java-jazzer
    
  2. Execute a Mayhem run on the java-base-executable Docker image using either the Mayhem UI or Mayhem CLI with the following Mayhemfile:

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/java-jazzer:latest
    duration: 90
    project: mayhem-examples
    target: java-jazzer
    cmds:
      - cmd: /usr/bin/jazzer_driver --cp=/usr/bin/MayhemIt.jar --target_class=mayhemit.MayhemIt
        libfuzzer: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/java-jazzer:latest
    duration: 90
    project: mayhem-examples
    target: java-jazzer
    cmds:
      - cmd: /usr/bin/jazzer_driver --cp=/usr/bin/MayhemIt.jar --target_class=mayhemit.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 standalone Java target! Click Next until you reach the final confirmation page and then hit Start Run!

You should see a Run page similar to the following:

java-jazzer-run

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

Testing a LibFuzzer Java Target with Jazzer

File: java-jazzer.tgz

Download and extract the above java-jazzer.tgz and take a look at the following bugged MayhemIt.java program:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package mayhemit;

public class MayhemIt {
    public static void fuzzerInitialize(){
        // any state initialization goes here!
    }

    public static void fuzzerTestOneInput(byte[] data) {
        String input = new String(data);
        if (input.length() >= 3) {
            if (input.charAt(0) == 'b') {
                if (input.charAt(1) == 'u') {
                    if (input.charAt(2) == 'g') {
                        throw new RuntimeException("Made it to the bug!");
                    }
                }
            }
        }
    }
}

Here we see that there are two functions, fuzzerInitialize and fuzzerTestOneInput. The fuzzerInitialize function allows for any state initialization to the program and the fuzzerTestOneInput acts as the entrypoint for the jazzer fuzzer to insert random values for testing. In particular, if the input data reads as "bug", the program crashes due to an uncaught exception defect.

Let's now take a look at how the java-jazzer target will be built. Looking at the associated Dockerfile we can see the following build process:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
FROM fuzzers/jazzer:0.9.1-openjdk11
COPY MayhemIt.java .

# Build MayhemIt.jar
RUN mkdir -p build && \
    javac -d build MayhemIt.java && \
    cd build && \
    jar cvf MayhemIt.jar * && \
    mv MayhemIt.jar /usr/bin/MayhemIt.jar && \
    rm -rf build

# Set to fuzz!
ENTRYPOINT []
CMD [ "/usr/bin/jazzer_driver", "--cp=/usr/bin/MayhemIt.jar", "--target_class=mayhemit.MayhemIt" ]
  1. Line 1: The fuzzers/jazzer:0.9.1-openjdk11 base image is imported to gather the necessary java dependencies.
  2. Line 2: The MayhemIt.java source file is copied over into the Docker container.
  3. Lines 5-10: The MayhemIt.jar file is compiled using the javac compiler.
  4. Line 14: The executable /usr/bin/jazzer_driver along with its parameters --cp=/usr/bin/MayhemIt.jar and --target_class=mayhemit.MayhemIt is set 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>/java-jazzer .
docker push <DOCKERHUB_USERNAME>/java-jazzer
docker build -t $MAYHEM_DOCKER_REGISTRY/forallsecure/java-jazzer .
docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/java-jazzer

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

1
2
3
4
5
6
7
image: <DOCKERHUB_USERNAME>/java-jazzer:latest
duration: 90
project: mayhem-examples
target: java-jazzer
cmds:
  - cmd: /usr/bin/jazzer_driver --cp=/usr/bin/MayhemIt.jar --target_class=mayhemit.MayhemIt
    libfuzzer: true
1
2
3
4
5
6
7
image: $MAYHEM_DOCKER_REGISTRY/forallsecure/java-jazzer:latest
duration: 90
project: mayhem-examples
target: java-jazzer
cmds:
  - cmd: /usr/bin/jazzer_driver --cp=/usr/bin/MayhemIt.jar --target_class=mayhemit.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:

java-jazzer-run

Congratulations! You just tested a libFuzzer Java target in Mayhem!

Real World Exercise: Building and Testing the mayhemit-out-of-bounds LibFuzzer Java Target

Now that you know how to build and test a libFuzzer Java target with an uncaught exception defect, let's see if you can modify the source code to use an array out-of-bounds defect instead.

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

Instructions:

  • Modify the MayhemIt.java source code by adding a max length constraint and the corresponding index out-of-bounds defect:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    if (input.length() >= 3 && input.length() < 5) {
        if (input.startsWith("b", 0)) {
            if (input.startsWith("u", 1)) {
                if (input.startsWith("g", 2)) {
                    char x = input.charAt(10);
                }
            }
        }
    }
    
  • Rebuild the Dockerfile using the docker build command and tag the resulting Docker image as <DOCKERHUB_USERNAME>/java-libfuzzer-mayhemit-out-of-bounds.

  • Push the <DOCKERHUB_USERNAME>/java-libfuzzer-mayhemit-out-of-bounds Docker image to the public Docker Hub registry using the docker push command.
  • Fuzz the <DOCKERHUB_USERNAME>/java-libfuzzer-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/java/libfuzzer/mayhemit-out-of-bounds.
  • Push the $MAYHEM_DOCKER_REGISTRY/java/libfuzzer/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/java/libfuzzer/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 LibFuzzer Java Target

Solution

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

First things first, you needed to add the index out-of-bounds code snippet to the fuzzerTestOneInput function so that when the fuzzerTestOneInput function is fuzzed, 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 input.length() < 5. Thus attempting to set the variable x to input.charAt(10) will produce the array out-of-bounds error.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package mayhemit;

public class MayhemIt {
    public static void fuzzerInitialize(){
        // any state initialization goes here!
    }

    public static void fuzzerTestOneInput(byte[] data) {
        String input = new String(data);

        if (input.length() >= 3 && input.length() < 5) {
            if (input.startsWith("b", 0)) {
                if (input.startsWith("u", 1)) {
                    if (input.startsWith("g", 2)) {
                        char x = input.charAt(10);
                    }
                }
            }
        }
    }
}

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

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

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

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

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

docker push <DOCKERHUB_USERNAME>/java-libfuzzer-mayhemit-out-of-bounds
docker push $MAYHEM_DOCKER_REGISTRY/tutorial/java/libfuzzer/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>/java-libfuzzer-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/java/libfuzzer/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>/java-libfuzzer-mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: mayhemit-out-of-bounds
cmds:
  - cmd: /usr/bin/jazzer_driver --cp=/usr/bin/MayhemIt.jar --target_class=mayhemit.MayhemIt
    libfuzzer: true
1
2
3
4
5
6
7
image: $MAYHEM_DOCKER_REGISTRY/java/libfuzzer/mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: mayhemit-out-of-bounds
cmds:
  - cmd: /usr/bin/jazzer_driver --cp=/usr/bin/MayhemIt.jar --target_class=mayhemit.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 libFuzzer Java target from scratch and used the jazzer fuzzer to detect the bug!

✏️ Summary and Recap

In this lesson, you learned how to fuzz libFuzzer Java targets with Mayhem!


I learned how to...

1. Build and test a libFuzzer Java target with an uncaught exception defect.
  • The source code should contain the following defect:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    if (input.length() >= 3) {
        if (input.charAt(0) == 'b') {
            if (input.charAt(1) == 'u') {
                if (input.charAt(2) == 'g') {
                    throw new RuntimeException("Made it to the bug!");
                }
            }
        }
    }
    

  • Then, to fuzz the libFuzzer Java target, use the following Dockerfile and Mayhemfile to build the Docker image containing the Java program and fuzz it in Mayhem, respectively:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    FROM fuzzers/jazzer:0.9.1-openjdk11
    COPY MayhemIt.java .
    
    # Build MayhemIt.jar
    RUN mkdir -p build && \
        javac -d build MayhemIt.java && \
        cd build && \
        jar cvf MayhemIt.jar * && \
        mv MayhemIt.jar /usr/bin/MayhemIt.jar && \
        rm -rf build
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD [ "/usr/bin/jazzer_driver", "--cp=/usr/bin/MayhemIt.jar", "--target_class=mayhemit.MayhemIt" ]
    

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/java-jazzer:latest
    duration: 90
    project: mayhem-examples
    target: java-jazzer
    cmds:
      - cmd: /usr/bin/jazzer_driver --cp=/usr/bin/MayhemIt.jar --target_class=mayhemit.MayhemIt
        libfuzzer: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/java-jazzer:latest
    duration: 90
    project: mayhem-examples
    target: java-jazzer
    cmds:
      - cmd: /usr/bin/jazzer_driver --cp=/usr/bin/MayhemIt.jar --target_class=mayhemit.MayhemIt
        libfuzzer: true
    
2. Build and fuzz a libFuzzer Java target with a out-of-bounds defect.
  • The source code should contain the following defect:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    if (input.length() >= 3 && input.length() < 5) {
        if (input.startsWith("b", 0)) {
            if (input.startsWith("u", 1)) {
                if (input.startsWith("g", 2)) {
                    char x = input.charAt(10);
                }
            }
        }
    }
    

  • Then, to fuzz the libFuzzer Java target, use the following Dockerfile and Mayhemfile to build the Docker image containing the Java program and fuzz it in Mayhem, respectively:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    FROM fuzzers/jazzer:0.9.1-openjdk11
    COPY MayhemIt.java .
    
    # Build MayhemIt.jar
    RUN mkdir -p build && \
        javac -d build MayhemIt.java && \
        cd build && \
        jar cvf MayhemIt.jar * && \
        mv MayhemIt.jar /usr/bin/MayhemIt.jar && \
        rm -rf build
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD [ "/usr/bin/jazzer_driver", "--cp=/usr/bin/MayhemIt.jar", "--target_class=mayhemit.MayhemIt" ]
    

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/java-libfuzzer-mayhemit-out-of-bounds:latest
    duration: 90
    project: mayhem-examples
    target: mayhemit-out-of-bounds
    cmds:
      - cmd: /usr/bin/jazzer_driver --cp=/usr/bin/MayhemIt.jar --target_class=mayhemit.MayhemIt
        libfuzzer: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/java/libfuzzer/mayhemit-out-of-bounds:latest
    duration: 90
    project: mayhem-examples
    target: mayhemit-out-of-bounds
    cmds:
      - cmd: /usr/bin/jazzer_driver --cp=/usr/bin/MayhemIt.jar --target_class=mayhemit.MayhemIt
        libfuzzer: true