How Bugs Found by Fuzzing Can Be Reproduced?
The trivial answer is to re-run the fuzz tests. Since the inputs that trigger a crash are saved during fuzzing we can just re-run the fuzz test. All unique corpus files are saved during a run and re-used in subsequent runs.
However, sometimes it is desirable to debug a finding to understand the root of the bug and regard the environment and related variables. To debug a finding in VS Code you can just select a finding and click on "DEBUG" symbol.
This will automatically start the integrated debugger of VS Code and set breakpoints before the vulnerable function based on the stacktrace of the bug.
Unfortunately, debugging is only working if the target fuzz test can be executed locally. This means that the target application also need to be executed locally. In the most cases there are some dependencies which are only available in the build docker image. Thus, the execution of the fuzz target in many cases is only possible in the build docker image.
How to Reproduce a Bug Inside the Build Docker Image?
We can use docker run to derive a container and execute bash inside the container. In addition, it is necessary to mount the CI Fuzz installation directory and the fuzz build directory. The docker run command could be for example:
docker run -v /home/user/.local/share/code-intelligence/projects/:/projects -v /opt/ci-fuzz:/opt/ci-fuzz -it DOCKER_IMAGE bash
Hint: The fuzz build directory is not the project directory where the source code lies. The fuzz build directory usually is placed in $HOME/.local/share/code-intelligence/projects/PROJECT_NAME.
In the docker container change the directory to the location of the fuzz target.
The fuzz target is dynamically compiled. It is necessary to specify the location of shared object files. You can use ldd to check which shared object files are missing before executing your fuzz target:
In the above example libFuzzCIlighttpd.so and libdesock.so where not found during the execution of the fuzz_target. We can search for the missing shared object files with the find command line tool. In the build project directory. We can add them to the LD_LIBRARY_PATH variable and execute the fuzz_target afterwards.
The fuzz_target now found all shared_object files, fuzzing was started successfully, and a finding was found directly.
For every finding a test unit input is saved in the directory of the fuzz_target called crash-something. That test unit input file now can be used to reproduce the bug.
LD_LIBRARY_PATH=../../cifuzz-libs:./sconsbuild/static/build ./fuzz_target crash-c4488af0c158e8c2832cb927cfb3ce534104cd1e
Execution with the test unit input will force the fuzz_target to just do one execution with the test unit input without fuzzing other inputs.
We now have achieved bug reproduction inside the docker build container. To debug the finding we can now just use our favorite debugger e.g. gdb. However, gdb need to be installed in the docker container.
How to Reproduce a Network Socket Fuzz Bug Inside Docker Container?
The approach is similar to the approach before. The only difference is that a network socket fuzz usually has some additional run arguments such as a config file. Those arguments can be appended to the fuzz target after a "--".
./fuzz_target -- -D -f /home/user/git/lighttpd/lighttpd.conf