Skip to content

advanced

Go Targets with LibFuzzer Instrumentation via Go-Fuzz

go-logo google-logo

Need a Go target with libFuzzer instrumentation? In this lesson, we'll walk through how to compile Go targets with libFuzzer instrumentation using Go-Fuzz!

Estimated Time: 15 minutes

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

  1. Compile and fuzz a Go target with libFuzzer instrumentation via go-fuzz for an reachable assertion defect.
  2. Compile and fuzz a Go target with libFuzzer instrumentation via go-fuzz for an index-out-of-bounds defect.

Run through the lesson:

See prerequisites before beginning.

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

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

    1
    2
    3
    4
    5
    image: <DOCKERHUB_USERNAME>/go-go-fuzz:latest
    project: mayhem-examples
    target: go-go-fuzz
    cmds:
      - cmd: /go/mayhemit.libfuzzer
    
    1
    2
    3
    4
    5
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/go-go-fuzz:latest
    project: mayhem-examples
    target: go-go-fuzz
    cmds:
      - cmd: /go/mayhemit.libfuzzer
    

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 Go target instrumented with go-fuzz! 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:

go-go-fuzz-run

Awesome! Now that you've seen Mayhem testing a libFuzzer instrumented Go target, let's now walk through how to compile and fuzz the go-go-fuzz target that you just executed a Mayhem run for!

Compiling and Testing a Go Target with LibFuzzer Instrumentation via Go-Fuzz

File: go-go-fuzz.tgz

Download and extract the above go-go-fuzz.tgz and take a look at the following bugged mayhemit.go program.

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

func mayhemit(bytes []byte) int {
    content := string(bytes)
    if len(content) >= 3 {
        if string(content[0]) == "b" {
            if string(content[1]) == "u" {
                if string(content[2]) == "g" {
                    panic("found a bug!")
                }
            }
        }
    }
    return 0
}

func Fuzz(data []byte) int {
    _ = mayhemit(data)
    return 0
}

Here we see that there are two functions of interest: Fuzz and mayhemit. The Fuzz function serves as the entrypoint for the go-fuzz fuzzer and invokes the mayhemit function on line 18 to check if the input test case reads "bug". If so, the program crashes via a reachable assertion error produced by the panic() function on line 9.

Let's now see how this program will end up being compiled:

1
2
3
4
5
6
7
8
FROM fuzzers/go-fuzz:1.2.0
COPY mayhemit.go .
RUN go-fuzz-build -libfuzzer -o mayhemit.a && \
    clang -fsanitize=fuzzer mayhemit.a -o mayhemit.libfuzzer

# Set to fuzz!
ENTRYPOINT []
CMD ["/go/mayhemit.libfuzzer"]
  • Line 1: The fuzzers/go-fuzz:1.2.0 base image is set as the base environment for the Docker container, providing access to go-fuzz and its dependencies.
  • Line 2: The mayhemit.go source code is copied over into the Docker container.
  • Line 3: The go-fuzz-build compiler is used to compile the mayhemit executable. In this particular case, the go-fuzz-build compiler outputs a libFuzzer archive file that can then be used with libFuzzer.
  • Line 8: The executable /go/mayhemit.libfuzzer is set as the default executable for the resulting Docker image.

Then, within the go-go-fuzz folder, execute the following docker commands to build and push the <DOCKERHUB_USERNAME>/go-go-fuzz Docker image to the Docker Hub registry.

Then, within the go-go-fuzz folder, execute the following docker commands to build and push the forallsecure/go-go-fuzz Docker image to the Mayhem Docker Registry.

docker build -f Dockerfile -t <DOCKERHUB_USERNAME>/go-go-fuzz .
docker push <DOCKERHUB_USERNAME>/go-go-fuzz
docker build -f Dockerfile -t $MAYHEM_DOCKER_REGISTRY/forallsecure/go-go-fuzz .
docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/go-go-fuzz

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.

Once the Docker image is successfully pushed, navigate to the Mayhem UI and select Docker Hub as the source to your new Mayhem run. Search for the <DOCKERHUB_USERNAME>/go-go-fuzz Docker image you just uploaded and confirm the Mayhemfile looks similar to the following:

Once the Docker image is successfully pushed, navigate to the Mayhem UI and select the Mayhem Docker Registry as the source to your new Mayhem run. Search for the forallsecure/go-go-fuzz Docker image you just uploaded and confirm the Mayhemfile looks similar to the following:

1
2
3
4
5
image: <DOCKERHUB_USERNAME>/go-go-fuzz:latest
project: mayhem-examples
target: go-go-fuzz
cmds:
  - cmd: /go/mayhemit.libfuzzer
1
2
3
4
5
image: $MAYHEM_DOCKER_REGISTRY/forallsecure/go-go-fuzz:latest
project: mayhem-examples
target: go-go-fuzz
cmds:
  - cmd: /go/mayhemit.libfuzzer

Then just click Next to the end of the Create New Run flow and hit the Click Start button to begin testing your new Go target! You should see a Run page similar to the following:

go-go-fuzz-run

Congratulations! You just fuzzed a Go target with go-fuzz instrumentation!

Real World Exercise: Building and Testing the mayhemit-out-of-bounds LibFuzzer Go Target via Go-Fuzz

Now that you know how to build and test a Go target with libFuzzer instrumentation using go-fuzz, let's see if you can modify the source code to use an index out-of-bounds error instead of a reachable assertion defect!

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

Instructions:

  • Modify the mayhemit.go source code and add the array out-of-bounds defect:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    func mayhemit(bytes []byte) int {
        content := string(bytes)
        if len(content) >= 3 && len(content) < 5 {
            if string(content[0]) == "b" {
                if string(content[1]) == "u" {
                    if string(content[2]) == "g" {
                        var x = content[10]
                        fmt.Println(x)
                    }
                }
            }
        }
        return 0
    }
    
  • Rebuild the Dockerfile using the docker build command and tag the resulting Docker image as <DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds.

  • Push the <DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds Docker image to the public Docker Hub registry using the docker push command.
  • Fuzz the <DOCKERHUB_USERNAME>/go-go-fuzz-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/go-go-fuzz-mayhemit-out-of-bounds.
  • Push the $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds Docker image to the private Mayhem Docker registry using the docker push command.
  • Test the $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-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 Go Target via Go-Fuzz

Solution

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

First things first, you needed to add the max length constraint len(content) < 5 and the erroneous call for content[10] so that when the mayhemit function is fuzzed, the input test case "bug" will trigger the index 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
22
23
24
25
package test

import (
    "fmt"
)

func mayhemit(bytes []byte) int {
    content := string(bytes)
    if len(content) >= 3 && len(content) < 5 {
        if string(content[0]) == "b" {
            if string(content[1]) == "u" {
                if string(content[2]) == "g" {
                    var x = content[10]
                    fmt.Println(x)
                }
            }
        }
    }
    return 0
}

func Fuzz(data []byte) int {
    _ = mayhemit(data)
    return 0
}

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>/go-go-fuzz-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/go-go-fuzz-mayhemit-out-of-bounds.

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

Next, you had to tag and push the <DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds Docker image to the public Docker Hub registry.

Next, you had to tag and push the $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds Docker image to the private Mayhem Docker registry.

docker push <DOCKERHUB_USERNAME>/tutorial/go-go-fuzz-mayhemit-out-of-bounds
docker push $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds

Note

Here we uploaded the go-go-fuzz-mayhemit-out-of-bounds Docker image to our tutorial.forallsecure.com Mayhem deployment, specifically at the internal Docker registry located at port 5000. For your Docker image to have successfully uploaded, you would have had to adjust the URL for your Mayhem deployment Docker registry accordingly.

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>/go-go-fuzz-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 $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-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>/go-go-fuzz-mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: go-go-fuzz-mayhemit-out-of-bounds
cmds:
  - cmd: /go/mayhemit.libfuzzer
    libfuzzer: true
1
2
3
4
5
6
7
image: $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: go-go-fuzz-mayhemit-out-of-bounds
cmds:
  - cmd: /go/mayhemit.libfuzzer
    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 instrumented Go target using go-fuzz and added a bug that was detected in Mayhem!

✏️ Summary and Recap

In this lesson, you learned how to compile libFuzzer instrumented Go targets with go-fuzz and fuzz them in Mayhem!


I learned how to...

1. Compile and test a Go target with libFuzzer instrumentation via go-fuzz for a reachable assertion defect.
  • The source code should contain the following defect:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    func mayhemit(bytes []byte) int {
        content := string(bytes)
        if len(content) >= 3 {
            if string(content[0]) == "b" {
                if string(content[1]) == "u" {
                    if string(content[2]) == "g" {
                        panic("found a bug!")
                    }
                }
            }
        }
        return 0
    }
    

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

    1
    2
    3
    4
    5
    6
    7
    8
    FROM fuzzers/go-fuzz:1.2.0
    COPY mayhemit.go .
    RUN go-fuzz-build -libfuzzer -o mayhemit.a && \
        clang -fsanitize=fuzzer mayhemit.a -o mayhemit.libfuzzer
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD ["/go/mayhemit.libfuzzer"]
    

    1
    2
    3
    4
    5
    image: <DOCKERHUB_USERNAME>/go-go-fuzz:latest
    project: mayhem-examples
    target: go-go-fuzz
    cmds:
      - cmd: /go/mayhemit.libfuzzer
    
    1
    2
    3
    4
    5
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/go-go-fuzz:latest
    project: mayhem-examples
    target: go-go-fuzz
    cmds:
      - cmd: /go/mayhemit.libfuzzer
    
2. Compile and fuzz a Go target with libFuzzer instrumentation via go-fuzz 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
    func mayhemit(bytes []byte) int {
        content := string(bytes)
        if len(content) >= 3 && len(content) < 5 {
            if string(content[0]) == "b" {
                if string(content[1]) == "u" {
                    if string(content[2]) == "g" {
                        var x = content[10]
                        fmt.Println(x)
                    }
                }
            }
        }
        return 0
    }
    

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

    1
    2
    3
    4
    5
    6
    7
    8
    FROM fuzzers/go-fuzz:1.2.0
    COPY mayhemit.go .
    RUN go-fuzz-build -libfuzzer -o mayhemit.a && \
        clang -fsanitize=fuzzer mayhemit.a -o mayhemit.libfuzzer
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD ["/go/mayhemit.libfuzzer"]
    

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