Overview of SharpFuzz
masterafl-fuzz to the .NET platform. It enables AFL-based fuzz testing for .NET applications, allowing developers to find bugs and vulnerabilities in C# and other .NET languages using coverage-guided fuzzing techniques.repository·master·Indexed 19 days ago
https://github.com/metalnem/sharpfuzzA tool that brings coverage-guided fuzz testing from AFL (American Fuzzy Lop) to the .NET platform. SharpFuzz enables developers to find bugs and vulnerabilities in C# and other .NET languages by providing a CLI for assembly instrumentation and a library for defining fuzzing targets via Fuzzer.Run. It supports both in-process and out-of-process fuzzing, as well as integration with libFuzzer for native Windows support.
afl-fuzz to the .NET platform. It enables AFL-based fuzz testing for .NET applications, allowing developers to find bugs and vulnerabilities in C# and other .NET languages using coverage-guided fuzzing techniques.You can install both the afl-fuzz source and the SharpFuzz.CommandLine global .NET tool using the following shell script. This script downloads the latest AFL source, compiles and installs it, and then installs the SharpFuzz CLI tool.
Note: This script requires sudo privileges for the make install step.
#/bin/sh
set -eux
# Download and extract the latest afl-fuzz source package
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
tar -xvf afl-latest.tgz
rm afl-latest.tgz
cd afl-2.53b/
# Install afl-fuzz
sudo make install
cd ..
rm -rf afl-2.53b/
# Install SharpFuzz.CommandLine global .NET tool
dotnet tool install --global SharpFuzz.CommandLineYou can use the afl-tmin tool to reduce the size of a crashing input file while maintaining the crash or consistent instrumentation.
To use afl-tmin with SharpFuzz, you must first modify your fuzzing project to replace the standard Fuzzer.Run call with Fuzzer.RunOnce. This allows the minimizer to execute the target once per iteration with a specific input.
Usage Command:
afl-tmin -i test_case -o minimized_result \
dotnet path_to_assemblyBy default, SharpFuzz runs in-process. If a timeout occurs or an uncatchable exception (like AccessViolationException or StackOverflowException) is thrown, the entire fuzzing process terminates with the error: [-] PROGRAM ABORT : Unable to communicate with fork server (OOM?).
To prevent the fuzzer from stopping when these events occur, use the out-of-process fuzzer. This mode uses a master process to communicate with afl-fuzz and a child process for the actual fuzzing. If the child process dies, the master process automatically restarts it.
Note on performance and behavior:
findings_dir/.cur_input.// Replace Fuzzer.Run with Fuzzer.OutOfProcess.Run
Fuzzer.OutOfProcess.Run(...);To use SharpFuzz with AFL, ensure your environment meets the following criteria:
make and a working compiler (gcc or clang) are required to compile afl-fuzz.Once your project is instrumented, you can start fuzzing using the fuzz.ps1 script.
Run the script by providing the path to your .csproj file and a directory containing initial test cases (-i).
pwsh scripts/fuzz.ps1 Jil.Fuzz.csproj -i TestcasesFor structured formats like JSON, HTML, or SQL, you can significantly improve fuzzing efficiency by providing an AFL dictionary file using the -x flag. AFL dictionaries are typically located in /usr/local/share/afl/dictionaries/ after installation.
pwsh scripts/fuzz.ps1 Jil.Fuzz.csproj -i Testcases \
-x /usr/local/share/afl/dictionaries/json.dictfindings/crashes directory.afl-fuzz status screen.pwsh scripts/fuzz.ps1 Jil.Fuzz.csproj -i Testcases -x /usr/local/share/afl/dictionaries/json.dictTo fuzz .NET Core classes that are not part of the core library (e.g., XmlReader), you must use IL-only assemblies from the dotnet-blob feed to allow for instrumentation. Follow these steps:
Fuzzer.Run.NuGet.Config to include the dotnetcore-feed source.Microsoft.Private.CoreFx.NETCoreApp and configure PackageConflictPreferredPackages to resolve conflicts with the standard Microsoft.NETCore.App package.dotnet publish -r linux-x64).sharpfuzz to instrument the specific assembly containing your target type (e.g., System.Private.Xml.dll). You can find the assembly location using typeof(TargetType).Assembly.CodeBase.afl-fuzz pointing to the published application executable.// 1. Fuzzing function example
public static void Main(string[] args)
{
Fuzzer.Run(stream =>
{
try
{
using (var xml = XmlReader.Create(stream))
{
while (xml.Read()) { }
}
}
catch (XmlException) { }
});
}When fuzzing classes outside System.Private.CoreLib, you must use the dotnet-blob feed to obtain IL-only assemblies.
Create NuGet.Config in your project directory:
<configuration>
<packageSources>
<add key="dotnetcore-feed" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
</packageSources>
</configuration>Update your .csproj file:
Microsoft.Private.CoreFx.NETCoreApp package.PackageConflictPreferredPackages to handle conflicts (replace linux-x64 with osx-x64 on macOS).<ItemGroup>
<PackageReference Include="Microsoft.Private.CoreFx.NETCoreApp" Version="4.6.0-*" />
</ItemGroup>
<PropertyGroup>
<PackageConflictPreferredPackages>Microsoft.Private.CoreFx.NETCoreApp;runtime.linux-x64.Microsoft.Private.CoreFx.NETCoreApp;$(PackageConflictPreferredPackages)</PackageConflictPreferredPackages>
</PropertyGroup>Fuzzing core types like DateTime requires using an IL-only build of the CoreCLR.
Fuzzer.Run../build.sh script with specific flags to ensure an IL-only build (skiptests skipcrossgen skipnative release).System.Private.CoreLib.dll from the CoreCLR build output into your application's publish directory.sharpfuzz to limit instrumentation to relevant paths.afl-fuzz against the published application.// 1. Fuzzing function example for DateTime
public static void Main(string[] args)
{
Fuzzer.Run(text =>
{
if (DateTime.TryParse(text, out var dt1))
{
var s = dt1.ToString("O");
var dt2 = DateTime.Parse(s, null, DateTimeStyles.RoundtripKind);
if (dt1 != dt2)
{
throw new Exception();
}
}
});
}To prepare a .NET assembly for fuzzing, use the sharpfuzz CLI tool. This performs in-place instrumentation on the target DLL, modifying it so that it can be used with afl-fuzz.
Run the tool by passing the path to the target assembly as a parameter:
sharpfuzz path/to/your/assembly.dllTo use libFuzzer as a fuzzing engine on Linux or Windows, follow these three steps:
clang:clang -fsanitize=fuzzer libfuzzer-dotnet.cc -o libfuzzer-dotnetMain function, replace the standard Fuzzer.Run or Fuzzer.OutOfProcess.Run calls with Fuzzer.LibFuzzer.Run.fuzz-libfuzzer.ps1 script to orchestrate the process.// In your Main function
Fuzzer.LibFuzzer.Run();Once your project is instrumented and the fuzzing target is implemented, run the fuzzing process using afl-fuzz. You must point afl-fuzz to the dotnet executable followed by the path to your compiled project DLL.
-i <dir>: Directory containing initial test cases.-o <dir>: Directory where findings (crashes) will be saved.-t <ms>: Required. Set a timeout in milliseconds (e.g., 5000). Managed languages require explicit timeouts to prevent false crash reports caused by AFL's automatic timeout calculation.-x <file>: Path to an AFL dictionary file (e.g., for JSON or HTML) to improve fuzzing efficiency.-m <value>: Increase the memory limit (e.g., -m 10000) if you encounter crashes caused by low default memory limits in certain environments.# Basic fuzzing command
afl-fuzz -i Testcases -o Findings -t 5000 dotnet bin/Debug/netcoreapp2.1/Fuzzing.dll
# Fuzzing with a dictionary for better coverage
afl-fuzz -i Testcases -o Findings -t 5000 -x /usr/local/share/afl/dictionaries/json.dict dotnet bin/Debug/netcoreapp2.1/Fuzzing.dll
# Fuzzing with an increased memory limit
afl-fuzz -i testcases_dir -o findings_dir -t 5000 -m 10000 dotnet path_to_assembly