beginner
Testing a Non-Docker Target with Mayhem¶
Have a normal binary not contained within a Docker image? In this lesson, we'll walk you through how to package and fuzz your non-Docker targets with Mayhem packages!
Estimated Time: 30 minutes
By the end of this lesson, you will be able to:
- Package a target binary using the
mayhem packagecommand. - Execute a Mayhem run on the packaged
testmetarget binary. - Set up and run regression testing via target replacement (no Mayhemfile modification).
- Seed test case files for a packaged target.
Run through the lesson:
See prerequisites before beginning.
-
Package the
testmebinary using themayhem packagecommand:mayhem package testme -o /tmp/testme-pkg -
Configure the Mayhemfile for the
testmebinary:1 2 3 4 5 6
project: testme target: testme duration: 90 advanced_triage: true cmds: - cmd: /root/tutorial/testme/v1/testme @@ -
Execute the Mayhem run on the
testmebinary using themayhem runcommand.mayhem run /tmp/testme-pkg
You will need the following:
- The Mayhem CLI installed and authenticated with the Mayhem server.
-
A Linux OS (macOS and Windows does not fully support
mayhem package).For this reason we recommend using the
forallsecure/tutorialDocker image as a means of using a Linux OS environment to follow along.docker pull forallsecure/tutorial:2.10 docker run -ti --privileged --rm forallsecure/tutorial:2.10
Terminal Recording¶
Using the mayhem package Command¶
If Docker is unavailable, users can use the mayhem package command to automatically package an application binary by statically inferring all of its associated dependencies from the local filesystem and building a mini-root filesystem containing the application along with its determined dependencies.
Note
Any associated dependencies that remain undetected will have to be manually added to the mini-root filesystem by the user to ensure that the target application can execute properly. This is why we recommend packaging targets using Docker due to the fact that the target binary is already bundled with its necessary environment, thereby avoiding potential dependency issues.
To use the mayhem package command, you will need the following parameters:
mayhem package <path_to_binary> -o <output_path>
The general workflow for packaging and testing a non-Docker target in Mayhem is as follows:
- Package the target binary using the
mayhem packagecommand. - Configure the generated Mayhemfile for the run.
- Execute the mayhem run for the Mayhemfile with the
mayhem runcommand.
Now that you're familiar with a brief overview of mayhem package, let's see how to use the mayhem package command for the testme application.
Packaging and Testing the testme Binary¶
In previous lessons we packaged and fuzzed the testme binary as a containerized application residing within a Docker container. However, if we were unable to use Docker but still had access to the testme binary, we would package and fuzz the testme binary as a Mayhem package instead.
Important
The mayhem package command only works within a Linux OS environment. Therefore macOS users should utilize the forallsecure/tutorial Docker image as a means of using a Linux OS environment to follow along. However, we also recommend existing Linux users to do the same as this lesson will assume you are working in the forallsecure/tutorial Docker image.
docker pull forallsecure/tutorial:2.10
docker run -ti --privileged --rm forallsecure/tutorial:2.10
For those without an Internet connection and therefore cannot pull the forallsecure/tutorial Docker image, download and extract the following: testme.zip
First, navigate to /root/tutorial/testme/v1 within the forallsecure/tutorial:2.10 Docker image.
1. Package the testme Binary¶
Next, we'll need to package the testme application binary. Execute the following in the current directory of the testme binary:
mayhem package ./testme -o /tmp/testme-pkg
This will create the following package and sub-contents located at /tmp/testme-pkg:
Mayhemfile: The configuration file for the Mayhem runtestsuite: The repository of test case files.root: The mini-root filesystem containing the application binary and associated dependencies.
Then, you'll need to configure the Mayhemfile before you can execute a Mayhem run for your packaged target.
2. Configure the Mayhemfile¶
The Mayhemfile generated from the mayhem package command is automatically configured using what Mayhem inferred from the mayhem package command; however, users may need to configure their Mayhemfile with more details if necessary in order to get their applications to run properly in Mayhem.
Tip
You should keep these helpful tips in mind when configuring the Mayhemfile for more complex applications:
-
Mayhem executes targets as an unprivileged user. If a target needs privileges, you should set the
uidfield as a top level configuration option in theMayhemfile. For example, if your application needs to run asroot, setuid: 0in theMayhemfile. -
Mayhem packages run inside of a Debian "Buster" Docker image by default. This means the package uses
libcand other system libraries by Debian. You can change the base OS image with theimagetop-level directive. For example, if your application runs on Alpine Linux, useimage: alpine. -
Mayhem runs packages with a very minimal user environment. If you need to set environment variables for a specific
cmd, set theenvas a list. For example, to put/usr/local/binin your application PATH, setenv: { "PATH": "/usr/local/bin:/usr/bin"}.
A mayhem package generated Mayhemfile will look similar to the following:
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 | |
Set the duration of the Mayhem run to 90 for the sake of this lesson (Mayhem runs are set to infinite duration by default). Here we see that the Mayhem run for the testme binary will take about 90 seconds and points to the location of the binary at /root/tutorial/testme/v1/testme, which corresponds to the root folder of the three assets (Mayhefile, testsuite, and root) that mayhem package creates. This Mayhem run uses a file input method for the testme binary indicated by @@.
Info
Your auto-generated cmd filepath may be differ from what you see above if you are not using the forallsecure/tutorial Docker image; the cmd filepath is auto-populated based on where the target application was originally packaged.
Once the Mayhemfile is properly configured, users can execute the mayhem run command pointing to the mayhem package directory containing the configured Mayhemfile.
3. Execute the Mayhem Run¶
Finally, execute the mayhem run command for the configured Mayhemfile:
mayhem run /tmp/testme-pkg
Info
You will need to use the mayhem login command to authenticate with the Mayhem server before you can execute your Mayhem run successfully. See Authenticating with the Mayhem Server for more information.
And that's it! Mayhem should be able to find the improper input validation defect for the testme binary in about 90 seconds. You can view the results in the Mayhem UI.
Regression Testing via Target Replacement¶
Previously, to perform regression testing you pointed a subsequent Mayhem run to a fixed/updated version of the testme binary via the cmd parameter and kept the same project and target Mayhemfile configuration values.
Note
Doing this effectively used previously generated test cases (from runs with the same project and target) in the new Mayhem run and compared previous (crashing) test cases with the updated (fixed) behavior of the new testme binary.
However, users can also perform regression testing by directly replacing the target binary specified in the Mayhemfile with an updated/fixed version of the application, thereby leaving the Mayhemfile configuration unmodified.
In particular there are two ways to set up regression tests for updated versions of a binary application:
- Re-using
projectandtargetMayhemfile parameters and pointing thecmdparameter to the location of an updated/fixed binary (seen here). - Replacing the previous binary with a fixed binary and keeping the Mayhemfile configuration the same.
Since we have direct access to the testme binary located within the /tmp/testme-pkg Mayhem package, let's see how we can set up regression testing doing a direct target replacement.
Info
Both Docker targets and non-Docker targets alike can use both methods for setting up regression tests; however, performing a direct replacement of the target binary will be more complicated for Docker targets as this requires a user to run the Docker image in interactive mode, replace the target binary, commit the change to the Docker image, and re-push the updated Docker image to either Docker Hub or the Mayhem Docker Registry. Hence, why the second method of target replacment for regression testing is shown in this lesson.
In the directory /root/tutorial/testme/v2, we've already provided a run.sh script to automatically replace the previous testme binary with the fixed version.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Info
The --regression parameter to the mayhem wait command waits until the regression test phase completes.
Here we see that the updated testme binary replaces the previously packaged testme binary at /tmp/testme-pkg/root/root/tutorial/testme/v1/testme and then regression testing is executed for the packaged Mayhemfile.
Now just run the following script in the /root/tutorial/testme/v2 directory:
sh run.sh
Looking at the associated run in the Mayhem UI, we see that the regression testing results show that previously crashing test cases and their associated defects have been marked as fixed.
And that's it! Now you know how to perform the two different types of workflows for setting up regression testing!
Testing testme with a Seeded Test Suite¶
Prior to executing a Mayhem run, Mayhem will also scan for pre-existing test cases within the testsuite directory to use as a seed or "jumpstart" to improve the speed and coverage of the Mayhem run. Each file within the testsuite directory represents an individual input test case along with it's contents.
Info
Check out the testsuite parameter for more information on configuring a Mayhemfile to seed the corresponding Mayhem run with a designated test suite folder containing pre-existing test cases. Seeded test suites can be set for both Docker targets and non-Docker targets alike.
For example, let's take another look at the underlying testme code. For our Mayhem package located at /tmp/testme-pkg, if we were to create a seed.txt file and place it in the testsuite folder containing the following contents:
bu
Then the seeded test case would effectively optimize the performance of the Mayhem run due to the fact that the bu test case provides deeper initial coverage of the testme binary (rather than something like aa).
1 2 3 4 5 6 7 8 9 10 | |
Therefore, place the seed.txt (containing the content bu) in the testsuite directory of our /tmp/testme-pkg folder:
cp seed.txt /tmp/testme-pkg/testsuite
Next, modify the Mayhemfile and change the target parameter value to testme-seed. This ensures that we start a fresh Mayhem run that will use our manually seeded test suite (Mayhem runs by default will seed the current run with the generated test suite from previous runs of the same <project>/<target>.)
1 2 3 4 5 6 | |
Now re-execute the mayhem run command to use the seed test suite for the testme binary!
$ mayhem run /tmp/testme-pkg
/tmp/tmpo2ifgn0f/testsuite.tgz 100% |#############| Time: 0:00:00 224.7 B/s
Syncing /tmp/testme-pkg/testsuite 100% |####################| Time: 0:00:01
Run started: testme/testme-seed/1
testme/testme-seed/1
The Mayhem run for the testme binary should now find the underlying defect much quicker compared to the previous un-seeded run. This is because Mayhem had to do less exploration of the program as the seed test case bu provided a relevant starting point for covering the if statements checking for the input "bug" that leads to the improper input validation defect.
⚡ Real World Exercise: Seeding and Testing the testme-npd Binary¶
Now that you know how to package binaries and seed a test suite, let's see if you can do it for the testme-npd binary from the previous lesson.
Note
If you're using the forallsecure/tutorial Docker image, you may need to use the docker cp command to move the testme-npd.zip into your corresponding Docker container. Check out the official documentation on docker cp for more information.
Instructions:
-
Download and extract the following: testme-npd
-
Package the
testme-npdbinary using themayhem packagecommand. -
Set the
projectandtargetvalues of theMayhemfiletotestme-npd. Also, set thedurationof the to60seconds. -
Provide a
seed.txttest case file in thetestsuitedirectory of thetestme-npdpackage. -
Execute the Mayhem run using the
mayhem runcommand.
Hint
This Mayhem run is only 60 seconds long! You'll want to speed up the discovery of the null pointer dereference bug, therefore think about what text might improve coverage to the null pointer dereference bug.
🔍 Review It! Seeding and Testing the testme-npd Binary¶
Solution
First things first, you needed to package the testme-npd binary using the mayhem package command.
mayhem package testme-npd -o /tmp/testme-npd-pkg
Then, you needed to set the project, target, and duration parameter of the Mayhemfile to their corresponding values.
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 | |
Next, you should have created a seed.txt file with the content "nul" and placed it in the testsuite directory. The seed.txt test case file should speed up the discovery of the null pointer dereference bug by providing a relevant starting point for covering the if statements leading to the null pointer dereference code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Lastly, upon executing the mayhem run command, you should have seen a run page similar to the following:
mayhem run /tmp/testme-npd-pkg
Well done! You're already building and optimizing fuzz targets all on your own!
✏️ Summary and Recap¶
In this lesson, you learned how to package up an application using the mayhem package command, configure the resulting Mayhemfile, execute your Mayhem run, and even seed a test suite to improve analysis and testing performance.
I learned how to...
1. Package a target binary using the mayhem package command.
-
Mayhem can use the mayhem package command to automatically package up an application binary by statically inferring all of its associated dependencies and building a mini-root filesystem containing the application along with the determined dependencies.
-
The
mayhem packagecommand follows the form:mayhem package <path_to_binary> -o <output_path> -
The package contents should look similar to the following:
├── /tmp/testme-pkg └── Mayhemfile - The configuration file for the Mayhem run └── testsuite - The repository of test case files. └── root - The mini-root filesystem containing the application binary.
2. Execute a Mayhem run on a packaged target.
- Upon packaging a target, users can use the
mayhem runcommand for the resultingMayhemfilelocated in the target package.
3. Set up and run regression testing via target replacement (no Mayhemfile modification).
-
In particular there are two ways to set up regression tests for updated versions of a binary application:
- Re-using
projectandtargetMayhemfile parameters and pointing thecmdparameter to the location of an updated/fixed binary (seen here). - Replacing the previous binary with a fixed binary and keeping the Mayhemfile configuration the same.
- Re-using
-
In the directory
/root/tutorial/testme/v2, we've already provided arun.shscript to automatically replace the previoustestmebinary with the fixed version.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#!/bin/sh # Copy over the new, fixed version into the package. # # Note that we are *overwriting* the old testme application. # Overwriting an old version allows you to use your existing # Mayhemfile cp testme /tmp/testme-pkg/root/root/tutorial/testme/v1/testme # Re-run mayhem. There is no need to edit the Mayhemfile. # The run ID is saved to $id id=$(mayhem run --regression /tmp/testme-pkg) # Wait for the run to finish mayhem wait $id --regression # Sync the test suite to the "testsuite" directory. mayhem sync /tmp/testme-pkg
4. Seed test case files for a packaged target.
-
Prior to executing a Mayhem run, Mayhem will also scan for pre-existing test cases within the
testsuitedirectory to use as a seed or "jumpstart" to improve the speed and coverage of the Mayhem run. Each file within thetestsuitedirectory represents an individual input test case along with it's contents. -
For example, if we take a look at the underlying
testmecode. For our Mayhem package located at/tmp/testme-pkg, if we were to create aseed.txtfile and place it in thetestsuitefolder containing the following contents:bu -
Then the seeded test case would effectively optimize the performance of the Mayhem run due to the fact that the
butest case provides deeper initial coverage of thetestmebinary (rather than something likeaa).1 2 3 4 5 6 7 8 9 10
int fuzzme(char *buf) { if(buf[0] == 'b') if(buf[1] == 'u') if(buf[2] == 'g') { printf("You've got it!"); abort(); // Defect: SIGABRT. } return 0; }


