Skip to content

advanced

C/C++ Targets Instrumented with Honggfuzz

logo google-logo

Need to compile a C/C++ target with honggfuzz instrumentation? In this lesson, we'll walk you through how to compile C/C++ honggfuzz 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 honggfuzz target with an improper input validation defect.
  2. Compile and fuzz a C++ honggfuzz target with an improper input validation defect.

Run through the lesson:

See prerequisites before beginning.

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

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

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

Compiling and Testing a C Target with Honggfuzz Instrumentation

File: c-honggfuzz-gcc.tgz

Download and extract the above c-honggfuzz-gcc.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
22
23
24
25
26
#include <stddef.h>
#include <stdint.h>

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

extern int HF_ITER(uint8_t** buf, size_t* len);

int main(void) {
    for (;;) {
        size_t len;
        uint8_t *buf;

        HF_ITER(&buf, &len);

        mayhemit(buf, len);
    }
}

Here you'll notice that instead of a typical main function, the source code utilizes the HF_ITER 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.

Note

There are two types of fuzzer entrypoints when using honggfuzz: ASAN-style (LLVMFuzzerTestOneInput) or HF_ITER style. Check out the documentation on honggfuzz persistent fuzzing for more information.

Taking a look at the corresponding Dockerfile shows us the process for compiling our c-honggfuzz-gcc target:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
FROM fuzzers/honggfuzz:1.9
COPY mayhemit.c .
RUN hfuzz-gcc mayhemit.c -o /mayhemit

FROM debian:buster-slim
COPY --from=0 /mayhemit .
COPY --from=0 /usr/local/bin/honggfuzz /usr/local/bin/honggfuzz
COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind-ptrace.so.0 /usr/lib/x86_64-linux-gnu/libunwind-ptrace.so.0
COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind-x86_64.so.8 /usr/lib/x86_64-linux-gnu/libunwind-x86_64.so.8
COPY --from=0 /usr/lib/x86_64-linux-gnu/libbfd-2.31.1-system.so /usr/lib/x86_64-linux-gnu/libbfd-2.31.1-system.so
COPY --from=0 /usr/lib/x86_64-linux-gnu/libopcodes-2.31.1-system.so /usr/lib/x86_64-linux-gnu/libopcodes-2.31.1-system.so
COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind.so.8 /usr/lib/x86_64-linux-gnu/libunwind.so.8
RUN mkdir /testsuite && echo seed > /testsuite/seed

# Set to fuzz!
ENTRYPOINT ["honggfuzz", "-f", "/testsuite", "--"]
CMD ["/mayhemit"]
  • Line 1: The fuzzers/honggfuzz:1.9 base image is imported to provide the necessary honggfuzz dependencies.
  • Line 2: The mayhemit.c source file is copied over into the Docker container.
  • Line 3: The hfuzz-gcc C compiler is to used to compile our C target with honggfuzz instrumentation.
  • Line 5: The debian:buster-slim base image is imported and the compiled mayhemit binary along with the honggfuzz fuzzer (and its associated dependencies) are brought over from the previous build stage.
  • Line 17: The /mayhemit executable is set as the default executable for the Docker container.

Note

On lines 5-13, this is what's known as a multi-stage build and is done to optimize or slim down the size of the resulting Docker image. In addition, the honggfuzz driver must be the same version as the honggfuzz compiled binary. Therefore, by bringing over the honggfuzz executable from the previous build stage, we ensure that the same version of honggfuzz is used as the one that compiled the binary.

Next, we need to build the resulting <DOCKERHUB_USERNAME>/c-honggfuzz-gcc Docker image and push it to the Docker Hub registry:

Next, we need to build the resulting forallsecure/c-honggfuzz-gcc Docker image and push it to the Mayhem Docker registry:

docker build -f Dockerfile -t <DOCKERHUB_USERNAME>/c-honggfuzz-gcc .
docker push <DOCKERHUB_USERNAME>/c-honggfuzz-gcc
docker build -f Dockerfile -t $MAYHEM_DOCKER_REGISTRY/forallsecure/c-honggfuzz-gcc .
docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/c-honggfuzz-gcc

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 Docker Hub registry, create a new run via the Mayhem UI and search for the <DOCKERHUB_USERNAME>/c-honggfuzz-gcc 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-honggfuzz-gcc Docker image. Confirm that your Mayhemfile looks similar to the following:

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

Congratulations! You just tested a C honggfuzz target with Mayhem!

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

Now that you're familiar with the steps for compiling and testing a C target with honggfuzz instrumentation, let's see if you can do the same for a C++ target!

Files cpp-honggfuzz-gcc.tgz

Instructions:

  • Use the Dockerfile and the docker build command and tag the resulting Docker image as <DOCKERHUB_USERNAME>/cpp-honggfuzz-gcc.
  • Push the <DOCKERHUB_USERNAME>/cpp-honggfuzz-gcc Docker image to the Docker Hub registry using the docker tag and docker push commands.
  • Fuzz the <DOCKERHUB_USERNAME>/cpp-honggfuzz-gcc Docker image using either the Mayhem UI or Mayhem CLI. Make sure to set the associated Mayhemfile accordingly.

  • Use the Dockerfile and the docker build command and tag the resulting Docker image as forallsecure/cpp-honggfuzz-gcc.

  • Push the forallsecure/cpp-honggfuzz-gcc Docker image to the internal Mayhem Docker registry using the docker tag and docker push commands.
  • Test the forallsecure/cpp-honggfuzz-gcc Docker image using either the Mayhem UI or Mayhem CLI. Make sure to set the associated Mayhemfile accordingly.

🔍 Review It! Compiling and Testing a C++ Target with Honggfuzz Instrumentation

Solution

First things first, let's take a look at the mayhemit.cpp source code:

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

int mayhemit(char *buf, unsigned len)
{
  if(len >= 3)
    if(buf[0] == 'b')
      if(buf[1] == 'u')
        if(buf[2] == 'g') {
          return 1/0;
        }
  return 0;
}

extern "C" int HF_ITER(uint8_t** buf, size_t* len);

int main(void) {
  for (;;) {
    size_t len;
    uint8_t *buf;

    HF_ITER(&buf, &len);

    mayhemit(reinterpret_cast<char *>(buf), len);
  }
}

One difference you may have noticed when comparing against the source code of the c-honggfuzz-gcc 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.

Next up, let's take a look at the associated Dockerfile:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
FROM fuzzers/honggfuzz:1.9
COPY mayhemit.cpp .
RUN hfuzz-g++ mayhemit.cpp -o /mayhemit

FROM debian:buster-slim
COPY --from=0 /mayhemit .
COPY --from=0 /usr/local/bin/honggfuzz /usr/local/bin/honggfuzz
COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind-ptrace.so.0 /usr/lib/x86_64-linux-gnu/libunwind-ptrace.so.0
COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind-x86_64.so.8 /usr/lib/x86_64-linux-gnu/libunwind-x86_64.so.8
COPY --from=0 /usr/lib/x86_64-linux-gnu/libbfd-2.31.1-system.so /usr/lib/x86_64-linux-gnu/libbfd-2.31.1-system.so
COPY --from=0 /usr/lib/x86_64-linux-gnu/libopcodes-2.31.1-system.so /usr/lib/x86_64-linux-gnu/libopcodes-2.31.1-system.so
COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind.so.8 /usr/lib/x86_64-linux-gnu/libunwind.so.8
RUN mkdir /testsuite && echo seed > /testsuite/seed

# Set to fuzz!
ENTRYPOINT ["honggfuzz", "-f", "/testsuite", "--"]
CMD ["/mayhemit"]

Here we see that on line 1 we import the fuzzers/honggfuzz:1.9 base image as we've done before and now instead use the hfuzz-g++ compiler on line 3 to compile our C++ target with Honggfuzz instrumentation. Then, on line 5 and onward, the compiled mayhemit binary and honggfuzz fuzzer (with it's associated dependencies) are brought over to the next stage build under the debian:buster-slim base image. Thereby making this Dockerfile a multi-stage build to optimize disk space of the resulting Docker image.

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>/cpp-honggfuzz-gcc:

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 forallsecure/cpp-honggfuzz-gcc:

docker build -f Dockerfile -t <DOCKERHUB_USERNAME>/cpp-honggfuzz-gcc .
docker build -f Dockerfile -t $MAYHEM_DOCKER_REGISTRY/forallsecure/cpp-honggfuzz-gcc .

Next, you had to push the <DOCKERHUB_USERNAME>/cpp-honggfuzz-gcc Docker image to the Docker Hub registry:

Next, you had to push the forallsecure/cpp-honggfuzz-gcc Docker image to the private Mayhem Docker registry:

docker push <DOCKERHUB_USERNAME>/cpp-honggfuzz-gcc
docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/cpp-honggfuzz-gcc

Lastly, you needed to execute a Mayhem run on the uploaded <DOCKERHUB_USERNAME>/cpp-honggfuzz-gcc 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 forallsecure/cpp-honggfuzz-gcc 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-honggfuzz-gcc:latest
duration: 90
project: mayhem-examples
target: cpp-honggfuzz-gcc
cmds:
  - cmd: /mayhemit
    honggfuzz: true
1
2
3
4
5
6
7
image: $MAYHEM_DOCKER_REGISTRY/forallsecure/cpp-honggfuzz-gcc:latest
duration: 90
project: mayhem-examples
target: cpp-honggfuzz-gcc
cmds:
  - cmd: /mayhemit
    honggfuzz: true

After initiating the Mayhem run, you should have seen a Run page similar to the following:

cpp-honggfuzz-gcc-run

Congratulations! Mayhem found the improper input validation defect! You just built a honggfuzz C++ target from scratch and used Mayhem to detect the bug!

✏️ Summary and Recap

In this lesson, you learned how to compile C/C++ targets with Honggfuzz instrumentation and fuzz them in Mayhem!


I learned how to...

1. Compile and test a C honggfuzz 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
    int mayhemit(char *buf, unsigned len)
    {
      if(len >= 3)
        if(buf[0] == 'b')
          if(buf[1] == 'u')
            if(buf[2] == 'g') {
              abort(); // Defect: SIGABRT.
            }
      return 0;
    }
    

  • Then, to fuzz the C honggfuzz 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
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    FROM fuzzers/honggfuzz:1.9
    COPY mayhemit.c .
    RUN hfuzz-gcc mayhemit.c -o /mayhemit
    
    FROM debian:buster-slim
    COPY --from=0 /mayhemit .
    COPY --from=0 /usr/local/bin/honggfuzz /usr/local/bin/honggfuzz
    COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind-ptrace.so.0 /usr/lib/x86_64-linux-gnu/libunwind-ptrace.so.0
    COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind-x86_64.so.8 /usr/lib/x86_64-linux-gnu/libunwind-x86_64.so.8
    COPY --from=0 /usr/lib/x86_64-linux-gnu/libbfd-2.31.1-system.so /usr/lib/x86_64-linux-gnu/libbfd-2.31.1-system.so
    COPY --from=0 /usr/lib/x86_64-linux-gnu/libopcodes-2.31.1-system.so /usr/lib/x86_64-linux-gnu/libopcodes-2.31.1-system.so
    COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind.so.8 /usr/lib/x86_64-linux-gnu/libunwind.so.8
    RUN mkdir /testsuite && echo seed > /testsuite/seed
    
    # Set to fuzz!
    ENTRYPOINT ["honggfuzz", "-f", "/testsuite", "--"]
    CMD ["/mayhemit"]
    

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/c-honggfuzz-gcc:latest
    duration: 90
    project: mayhem-examples
    target: c-honggfuzz-gcc
    cmds:
      - cmd: /mayhemit
        honggfuzz: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/c-honggfuzz-gcc:latest
    duration: 90
    project: mayhem-examples
    target: c-honggfuzz-gcc
    cmds:
      - cmd: /mayhemit
        honggfuzz: true
    
2. Compile and fuzz a C++ honggfuzz 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
    int mayhemit(char *buf, unsigned len)
    {
      if(len >= 3)
        if(buf[0] == 'b')
          if(buf[1] == 'u')
            if(buf[2] == 'g') {
              abort(); // Defect: SIGABRT.
            }
      return 0;
    }
    

  • Then, to fuzz the C honggfuzz 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
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    FROM fuzzers/honggfuzz:1.9
    COPY mayhemit.cpp .
    RUN hfuzz-g++ mayhemit.cpp -o /mayhemit
    
    FROM debian:buster-slim
    COPY --from=0 /mayhemit .
    COPY --from=0 /usr/local/bin/honggfuzz /usr/local/bin/honggfuzz
    COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind-ptrace.so.0 /usr/lib/x86_64-linux-gnu/libunwind-ptrace.so.0
    COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind-x86_64.so.8 /usr/lib/x86_64-linux-gnu/libunwind-x86_64.so.8
    COPY --from=0 /usr/lib/x86_64-linux-gnu/libbfd-2.31.1-system.so /usr/lib/x86_64-linux-gnu/libbfd-2.31.1-system.so
    COPY --from=0 /usr/lib/x86_64-linux-gnu/libopcodes-2.31.1-system.so /usr/lib/x86_64-linux-gnu/libopcodes-2.31.1-system.so
    COPY --from=0 /usr/lib/x86_64-linux-gnu/libunwind.so.8 /usr/lib/x86_64-linux-gnu/libunwind.so.8
    RUN mkdir /testsuite && echo seed > /testsuite/seed
    
    # Set to fuzz!
    ENTRYPOINT ["honggfuzz", "-f", "/testsuite", "--"]
    CMD ["/mayhemit"]
    

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