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 package
command. - Execute a Mayhem run on the packaged
testme
target 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
testme
binary using themayhem package
command:mayhem package testme -o /tmp/testme-pkg
-
Configure the Mayhemfile for the
testme
binary: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
testme
binary using themayhem run
command.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/tutorial
Docker 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 package
command. - Configure the generated Mayhemfile for the run.
- Execute the mayhem run for the Mayhemfile with the
mayhem run
command.
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
uid
field as a top level configuration option in theMayhemfile
. For example, if your application needs to run asroot
, setuid: 0
in theMayhemfile
. -
Mayhem packages run inside of a Debian "Buster" Docker image by default. This means the package uses
libc
and other system libraries by Debian. You can change the base OS image with theimage
top-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 theenv
as a list. For example, to put/usr/local/bin
in 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
project
andtarget
Mayhemfile parameters and pointing thecmd
parameter 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-npd
binary using themayhem package
command. -
Set the
project
andtarget
values of theMayhemfile
totestme-npd
. Also, set theduration
of the to60
seconds. -
Provide a
seed.txt
test case file in thetestsuite
directory of thetestme-npd
package. -
Execute the Mayhem run using the
mayhem run
command.
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 package
command 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 run
command for the resultingMayhemfile
located 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
project
andtarget
Mayhemfile parameters and pointing thecmd
parameter 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.sh
script to automatically replace the previoustestme
binary 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
testsuite
directory to use as a seed or "jumpstart" to improve the speed and coverage of the Mayhem run. Each file within thetestsuite
directory represents an individual input test case along with it's contents. -
For example, if we take a look at the underlying
testme
code. For our Mayhem package located at/tmp/testme-pkg
, if we were to create aseed.txt
file and place it in thetestsuite
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 thetestme
binary (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; }