コンテンツにスキップ

advanced

C/C++ Targets Instrumented with LibFuzzer

logo llvm-logo

Need to compile a C/C++ target with libFuzzer instrumentation? In this lesson, we'll walk you through how to compile C/C++ libFuzzer targets and test them in Mayhem.

Estimated Time: 15 minutes

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

  1. Compile and fuzz a C libFuzzer target with an improper input validation defect.
  2. Compile and fuzz a C++ libFuzzer target with an index out-of-bounds defect.

Run through the lesson:

See prerequisites before beginning.

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

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

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/c-libfuzzer:latest
    duration: 90
    project: mayhem-examples
    target: c-libfuzzer
    cmds:
      - cmd: /mayhemit
        libfuzzer: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/c-libfuzzer:latest
    duration: 90
    project: mayhem-examples
    target: c-libfuzzer
    cmds:
      - cmd: /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 following button and hit Start Run at the end of the Create New Run flow to get a quick start on testing a C target with AFL instrumentation! No need to configure anything, as the underlying Mayhemfile has already been configured for you!

Once the Mayhem run initiates, you should see a Run page similar to the following:

c-libfuzzer-run

Awesome! Now that you've seen Mayhem can fuzz a C target instrumented with libFuzzer, let's now walk through how to compile and test the C libFuzzer target that you just executed a Mayhem run for!

Compiling and Testing a C Target with LibFuzzer Instrumentation

File: c-libfuzzer.tgz

Download and extract the above c-libfuzzer.tgz and take a look at the following source code for mayhemit.c:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

int mayhemit(char *buf, unsigned len)
{
  if (len >= 3)
    if(buf[0] == 'b')
      if(buf[1] == 'u')
        if(buf[2] == 'g') {
          printf("You've got it!");
          abort(); // Defect: SIGABRT.
        }
  return 0;
}

int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
{
  mayhemit((char *) Data, Size);
  return 0;
}

You may notice immediately that a couple additional header files (stddef.h and stdint.h ) have been added to the source code.

In addition, instead of a typical main function, the source code utilizes the LLVMFuzzerTestOneInput function instead to accept an array of bytes that will be used to fuzz the target function, mayhemit. If the input is then found to spell out the word "bug", the program crashes due to an improper input validation error.

Next, let's inspect the associated Dockerfile:

1
2
3
4
5
6
7
FROM fuzzers/libfuzzer:12.0
COPY mayhemit.c .
RUN clang-12 -fsanitize=fuzzer,address mayhemit.c -o /mayhemit 

# Set to fuzz!
ENTRYPOINT []
CMD /mayhemit
  • Line 1: The fuzzers/libfuzzer:12.0 base image is imported to gain access to the libFuzzer dependencies.
  • Line 2: The mayhemit.c source code is copied over into the Docker container.
  • Line 3: The clang C compiler is used with the fsanitize parameter to compile the mayhemit executable and link with the libFuzzer library.
  • Line 7: The /mayhemit executable is set as the default executable for the Docker container.

Info

Check out the official documentation on libFuzzer usage for more information on using clang and -fsanitize.

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>/c-libfuzzer .
docker push <DOCKERHUB_USERNAME>/c-libfuzzer
docker build -t $MAYHEM_DOCKER_REGISTRY/forallsecure/c-libfuzzer .
docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/c-libfuzzer

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

1
2
3
4
5
6
7
image: <DOCKERHUB_USERNAME>/c-libfuzzer:latest
duration: 90
project: mayhem-examples
target: c-libfuzzer
cmds:
  - cmd: /mayhemit
    libfuzzer: true
1
2
3
4
5
6
7
image: $MAYHEM_DOCKER_REGISTRY/forallsecure/c-libfuzzer:latest
duration: 90
project: mayhem-examples
target: c-libfuzzer
cmds:
  - cmd: /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:

c-libfuzzer-run

Congratulations! You just fuzzed a C libFuzzer target in Mayhem!

Real World Exercise: Compiling and Testing a C++ Target with LibFuzzer Instrumentation

Now that you're familiar with the steps for compiling and testing a C target with libFuzzer instrumentation, let's see if you can do the same but for a C++ target with libFuzzer instrumentation and an index out-of-bounds defect!

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

Instructions:

  • Modify the mayhemit.c source code and add the corresponding index out-of-bounds defect:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int mayhemit(char *buf, unsigned len)
{
  if (len >= 3 && len < 5)
    if(buf[0] == 'b')
      if(buf[1] == 'u')
        if(buf[2] == 'g') {
          printf("You've got it!");
          return buf[10];
        }
  return 0;
}
  • Rebuild the Dockerfile using the docker build command and tag the resulting Docker image as <DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-bounds.
  • Push the <DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-bounds Docker image to the public Docker Hub registry using the docker push command.
  • Fuzz the <DOCKERHUB_USERNAME>/cpp-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 cpp-libfuzzer-mayhemit-out-of-bounds.
  • Push the cpp-libfuzzer-mayhemit-out-of-bounds Docker image to the private Mayhem Docker registry using the docker push command.
  • Test the cpp-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! Compiling and Testing a LibFuzzer C++ Target

Solution

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

Okay let's take a look at how you did! First things first, let's inspect the source code for the cpp-libfuzzer-mayhemit-out-of-bounds target:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

int mayhemit(char *buf, unsigned len)
{
  if (len >= 3 && len < 5)
    if(buf[0] == 'b')
      if(buf[1] == 'u')
        if(buf[2] == 'g') {
          printf("You've got it!");
          return buf[10];
        }
  return 0;
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
{
  mayhemit((char *) Data, Size);
  return 0;
}

One difference you may have noticed when comparing against the source code of the c-libfuzzer target is the use of the extern "C" keyword. The extern "C" keyword makes a function-name in C++ have C linkage so that client C code can link to (use) your function using a C compatible header file that contains just the declaration of your function.

Meanwhile, the associated Dockerfile for the cpp-libfuzzer-mayhemit-out-of-bounds target indicates that to compile the binary, the clang++ C++ compiler is used in conjunction with the fsanitize parameter to compile the mayhemit executable and link the libFuzzer library.

1
2
3
4
5
6
7
FROM fuzzers/libfuzzer:12.0
COPY mayhemit.cpp .
RUN clang++-12 -fsanitize=fuzzer,address -fno-inline mayhemit.cpp -o /mayhemit 

# Set to fuzz!
ENTRYPOINT []
CMD /mayhemit

Next, you needed to build, tag, and push the resulting cpp-libfuzzer-mayhemit-out-of-bounds Docker image to the Docker Hub Registry.

Next, you needed to build, tag, and push the resulting cpp-libfuzzer-mayhemit-out-of-bounds Docker image to the Mayhem Docker Registry.

docker build -f Dockerfile -t <DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-bounds .
docker push <DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-bounds
docker build -f Dockerfile -t $MAYHEM_DOCKER_REGISTRY/cpp-libfuzzer-mayhemit-out-of-bounds .
docker push $MAYHEM_DOCKER_REGISTRY/cpp-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 needed to execute a Mayhem run on the uploaded <DOCKERHUB_USERNAME>/cpp-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 needed to execute a Mayhem run on the uploaded cpp-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>/cpp-libfuzzer-mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: cpp-libfuzzer-mayhemit-out-of-bounds
cmds:
  - cmd: /mayhemit
    libfuzzer: true
1
2
3
4
5
6
7
image: $MAYHEM_DOCKER_REGISTRY/cpp-libfuzzer-mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: cpp-libfuzzer-mayhemit-out-of-bounds
cmds:
  - cmd: /mayhemit
    libfuzzer: true

Your final Run page should have looked like the following:

cpp-mayhemit-out-of-bounds-run

Congratulations! Mayhem found the index out-of-bounds defect! You just built a libFuzzer C++ target from scratch and used Mayhem to detect the bug that you added!

✏️ Summary and Recap

In this lesson, you learned how to compile and fuzz C/C++ targets instrumented with libFuzzer!


I learned how to...

1. Compile and test a C libFuzzer target with an improper input validation defect.
  • The source code should contain the following defect:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    int mayhemit(char *buf, unsigned len)
    {
      if (len >= 3)
        if(buf[0] == 'b')
          if(buf[1] == 'u')
            if(buf[2] == 'g') {
              printf("You've got it!");
              abort(); // Defect: SIGABRT.
            }
      return 0;
    }
    

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

    1
    2
    3
    4
    5
    6
    7
    FROM fuzzers/libfuzzer:12.0
    COPY mayhemit.c .
    RUN clang-12 -fsanitize=fuzzer,address mayhemit.c -o /mayhemit 
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD /mayhemit
    

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/c-libfuzzer:latest
    duration: 90
    project: mayhem-examples
    target: c-libfuzzer
    cmds:
      - cmd: /mayhemit
        libfuzzer: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/c-libfuzzer:latest
    duration: 90
    project: mayhem-examples
    target: c-libfuzzer
    cmds:
      - cmd: /mayhemit
        libfuzzer: true
    
2. Compile and fuzz a C++ libFuzzer target with 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
    int mayhemit(char *buf, unsigned len)
    {
      if (len >= 3 && len < 5)
        if(buf[0] == 'b')
          if(buf[1] == 'u')
            if(buf[2] == 'g') {
              printf("You've got it!");
              return buf[10];
            }
      return 0;
    }
    

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

    1
    2
    3
    4
    5
    6
    7
    FROM fuzzers/libfuzzer:12.0
    COPY mayhemit.cpp .
    RUN clang++-12 -fsanitize=fuzzer,address -fno-inline mayhemit.cpp -o /mayhemit 
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD /mayhemit
    

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