コンテンツにスキップ

advanced

C/C++ Targets Instrumented with AFL/AFL++

logo google-logo aflpp-logo

Need to compile a C/C++ target with either Google's AFL instrumentation or the more enhanced AFL++ instrumentation? In this lesson, we'll walk you through how to compile C/C++ targets with AFL/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. Compile and fuzz a C target with AFL instrumentation.
  2. Compile and fuzz a C target with AFL++ instrumentation.
  3. Compile and fuzz a C++ target with AFL instrumentation.

Run through the lesson:

See prerequisites before beginning.

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

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

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

Compiling and Testing a C Target with AFL Instrumentation

File: c-afl-gcc.tgz

Download and extract the above c-afl-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
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <string.h>

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

int main(int argc, char *argv[])
{
  FILE *f;
  char buf[12];

  if(argc != 2){
    fprintf(stderr, "Must supply a text file\n");
    return -1;
  }
  f = fopen(argv[1], "r");
  if(f == NULL){
    fprintf(stderr, "Could not open %s\n", argv[1]);
    return -1;
  }
  if(fgets(buf, sizeof(buf), f) == NULL){
    fprintf(stderr, "Could not read from %s\n", argv[1]);
    return -1;
  }
  mayhemit(buf);
  return 0;
}

Note

If you're already familiar with the c-uninstrumented target, then you'll notice that the source code is the exact same! So what makes a C-AFL target different? The difference lies in how the program is compiled (at least for AFL).

Taking a look at the associated Dockerfile, we see the following operations:

1
2
3
4
5
6
7
8
FROM fuzzers/afl:2.52
COPY mayhemit.c .
RUN afl-gcc mayhemit.c -o /mayhemit
RUN mkdir /testsuite && echo seed > /testsuite/seed

# Set to fuzz!
ENTRYPOINT ["afl-fuzz", "-i", "/testsuite", "-o", "/out"]
CMD ["/mayhemit", "@@"]
  • Line 1: The fuzzers/afl:2.52 image is imported as the base image to the new Docker container.
  • Line 2: The mayhemit.c source code is copied over into the Docker container.
  • Line 3: The afl-gcc C compiler compiles the mayhemit.c and adds AFL instrumentation to become the mayhemit executable.
  • Line 8: The /mayhemit @@ command is set as the default executable for the resulting Docker container.

Note

The entrypoint code on line 7 is included to allow AFL to fuzz the mayhemit executable locally within the Docker container when the container is run (for debugging purposes). After building the Docker container, try the following command to see it in action docker run -it <container>. See Docker Entrypoint Support in Mayhem for more information on how to run entrypoint scripts in Mayhem specifically.

Now we just need to build and push the Docker image to a Docker registry as before:

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

And finally, search for the <DOCKERHUB_USERNAME>/c-afl-gcc Docker image and follow the create new run flow in the Mayhem UI to fuzz the new Docker image in Mayhem!

And finally, search for the forallsecure/c-afl-gcc Docker image and follow the create new run flow in the Mayhem UI to fuzz the new Docker image in Mayhem!

Make sure the underlying Mayhemfile for the run looks similar to the following:

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

Nice job! You just compiled and fuzzed a C target with AFL instrumentation!

c-afl-gcc-run

Compiling and Testing a C Target with AFL++ Instrumentation

File: c-aflpp-gcc.tgz

Let's now compile our previous mayhemit.c source code with AFL++ instrumentation instead. Since the source code is just about the same, we'll just need to use the afl-g++-fast compiler instead.

 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
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <string.h>

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

int main(int argc, char *argv[])
{
  FILE *f;
  char buf[12];

  if(argc != 2){
    fprintf(stderr, "Must supply a text file\n");
    return -1;
  }
  f = fopen(argv[1], "r");
  if(f == NULL){
    fprintf(stderr, "Could not open %s\n", argv[1]);
    return -1;
  }
  if(fgets(buf, sizeof(buf), f) == NULL){
    fprintf(stderr, "Could not read from %s\n", argv[1]);
    return -1;
  }
  mayhemit(buf);
  return 0;
}

Navigate to the c-aflpp-gcc folder and take a look at the associated Dockerfile for the c-aflpp-gcc target.

You'll notice that the process for building the c-aflpp-gcc target is much of the same as building the c-afl-gcc target, with the exception of line 1 importing the fuzzers/aflplusplus:3.12c base image containing the AFL++ dependencies, and line 3 compiling the mayhemit.c source code with the afl-gcc-fast compiler instead.

1
2
3
4
5
6
7
8
FROM fuzzers/aflplusplus:3.12c
COPY mayhemit.c .
RUN afl-gcc-fast mayhemit.c -o /mayhemit 
RUN mkdir /testsuite && echo seed > /testsuite/seed

# Set to fuzz!
ENTRYPOINT ["afl-fuzz", "-i", "/testsuite", "-o", "/out"]
CMD ["/mayhemit", "@@"]

Next, we'll need to just build and push the resulting mayhemit/c-aflpp-gcc Docker image to the Mayhem Docker registry.

Next, we'll need to just build and push the resulting mayhemit/c-aflpp-gcc Docker image to the Mayhem Docker registry.

docker build -t <DOCKERHUB_USERNAME>/c-aflpp-gcc .
docker push <DOCKERHUB_USERNAME>/c-aflpp-gcc
docker build -t $DOCKER_REGISTRY/forallsecure/c-aflpp-gcc .
docker push $DOCKER_REGISTRY/forallsecure/c-aflpp-gcc

Then, select the <DOCKERHUB_USERNAME>/c-aflpp-gcc Docker image as the source for a new Mayhem run and verify that the underlying Mayhemfile looks similar to the following:

Then, select the forallsecure/c-aflpp-gcc Docker image as the source for a new Mayhem run and verify that the underlying Mayhemfile looks similar to the following:

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

And lastly, fuzz the new <DOCKERHUB_USERNAME>/c-aflpp-gcc Docker image in the Mayhem UI!

And lastly, fuzz the new forallsecure/c-aflpp-gcc Docker image in the Mayhem UI!

c-aflpp-gcc-run

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

File: cpp-afl-gcc.tgz

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

Instructions:

  • Download the above cpp-afl-gcc.tgz and extract the cpp-afl-gcc folder.
  • Navigate to the cpp-afl-gcc folder and build and push the <DOCKERHUB_USERNAME>/cpp-afl-gcc target to Docker Hub.
  • Lastly, fuzz the newly uploaded <DOCKERHUB_USERNAME>/cpp-afl-gc Docker image using the Mayhem UI.

  • Download the above cpp-afl-gcc.tgz and extract the cpp-afl-gcc folder.

  • Navigate to the cpp-afl-gcc folder and build and push the forallsecure/cpp-afl-gcc target to Mayhem to be fuzzed.
  • Lastly, test the newly uploaded forallsecure/cpp-afl-gc Docker image using the Mayhem UI.

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

Solution

Let's see how you did! First things first, let's take a look at the mayhemit.cpp source code for the cpp-afl-gcc target:

 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
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <string.h>

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

int main(int argc, char *argv[])
{
  FILE *f;
  char buf[12];

  if(argc != 2){
    fprintf(stderr, "Must supply a text file\n");
    return -1;
  }
  f = fopen(argv[1], "r");
  if(f == NULL){
    fprintf(stderr, "Could not open %s\n", argv[1]);
    return -1;
  }
  if(fgets(buf, sizeof(buf), f) == NULL){
    fprintf(stderr, "Could not read from %s\n", argv[1]);
    return -1;
  }
  mayhemit(buf);
  return 0;
}

The source code looks just about the same as our previous c-afl-gcc and c-aflpp-gcc targets, with the exception of the C++ iostream header file.

Looking at the associated Dockerfile, we can see that to compile the mayhemit.cpp source file with AFL instrumentation, you needed to use the afl-g++ compiler.

1
2
3
4
5
6
7
8
FROM fuzzers/afl:2.52
COPY mayhemit.cpp .
RUN afl-g++ mayhemit.cpp -o /mayhemit
RUN mkdir /testsuite && echo seed > /testsuite/seed

# Set to fuzz!
ENTRYPOINT ["afl-fuzz", "-i", "/testsuite", "-o", "/out"]
CMD ["/mayhemit", "@@"]

Then, just build and push the Docker image to the public Docker Hub registry:

Then, just build and push the Docker image to the Mayhem Docker Registry:

docker build -t <DOCKERHUB_USERNAME>/cpp-afl-gcc .
docker push <DOCKERHUB_USERNAME>/cpp-afl-gcc
docker build -t $MAYHEM_DOCKER_REGISTRY/mayhemit/cpp-afl-gcc .
docker push $MAYHEM_DOCKER_REGISTRY/mayhemit/cpp-afl-gcc

Once pushed to Docker Hub, you simply needed to use the Mayhem UI to create a new run and choose your <DOCKERHUB_USERNAME>/cpp-afl-gcc Docker image as the source, with an underlying Mayhemfile looking similar to the following:

Once pushed to the Mayhem Docker Registry, you simply needed to use the Mayhem UI to create a new run and choose the forallsecure/cpp-afl-gcc Docker image as the source, with an underlying Mayhemfile looking similar to the following:

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

Finally, after initiating the Mayhem run, you should have seen a Run page similar to the following! Great job!

cpp-afl-gcc-run

✏️ Summary and Recap

In this lesson, we walked you through how to compile C/C++ targets with AFL/AFL++ instrumentation and fuzz them in Mayhem!


I learned how to...

1. Compile and test a C target with AFL instrumentation.
  • The source code should contain the following defect:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    int mayhemit(char *buf)
    {
      if(strlen(buf) >= 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 AFL 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
    FROM fuzzers/afl:2.52
    COPY mayhemit.c .
    RUN afl-gcc mayhemit.c -o /mayhemit
    RUN mkdir /testsuite && echo seed > /testsuite/seed
    
    # Set to fuzz!
    ENTRYPOINT ["afl-fuzz", "-i", "/testsuite", "-o", "/out"]
    CMD ["/mayhemit", "@@"]
    

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/c-afl-gcc:latest
    duration: 90
    project: mayhem-examples
    target: c-afl-gcc
    cmds:
      - cmd: /mayhemit @@
        afl: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/c-afl-gcc:latest
    duration: 90
    project: mayhem-examples
    target: c-afl-gcc
    cmds:
      - cmd: /mayhemit @@
        afl: true
    
2. Compile and fuzz a C target with AFL++ instrumentation.
  • The source code should contain the following defect:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    int mayhemit(char *buf)
    {
      if(strlen(buf) >= 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 AFL++ 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
    FROM fuzzers/aflplusplus:3.12c
    COPY mayhemit.c .
    RUN afl-gcc-fast mayhemit.c -o /mayhemit 
    RUN mkdir /testsuite && echo seed > /testsuite/seed
    
    # Set to fuzz!
    ENTRYPOINT ["afl-fuzz", "-i", "/testsuite", "-o", "/out"]
    CMD ["/mayhemit", "@@"]
    

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/c-aflpp-gcc:latest
    duration: 90
    project: mayhem-examples
    target: c-aflpp-gcc
    cmds:
      - cmd: /AFLplusplus/mayhemit @@
        afl: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/c-aflpp-gcc:latest
    duration: 90
    project: mayhem-examples
    target: c-aflpp-gcc
    cmds:
      - cmd: /AFLplusplus/mayhemit @@
        afl: true
    
3. Compile and fuzz a C++ target with AFL instrumentation.
  • The source code should contain the following defect:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    int mayhemit(char *buf)
    {
      if(strlen(buf) >= 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++ AFL 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
    FROM fuzzers/afl:2.52
    COPY mayhemit.cpp .
    RUN afl-g++ mayhemit.cpp -o /mayhemit
    RUN mkdir /testsuite && echo seed > /testsuite/seed
    
    # Set to fuzz!
    ENTRYPOINT ["afl-fuzz", "-i", "/testsuite", "-o", "/out"]
    CMD ["/mayhemit", "@@"]
    

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