SharpFuzz Documentation

repository·master·Indexed 19 days ago

https://github.com/metalnem/sharpfuzz

A 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.

Tokens
4.3K
Snippets
14
Records
17
Agent score
66%

What's inside SharpFuzz

  1. Overview of SharpFuzz

    master
    SharpFuzz is a tool that brings the power of 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.
  2. Install afl-fuzz and SharpFuzz.CommandLine

    master

    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.CommandLine
  3. Minimize test cases using afl-tmin

    master

    You 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_assembly
  4. Use out-of-process fuzzing to handle crashes and timeouts

    master

    By 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:

    • Performance: Starting a new .NET process for every input is expensive. If your target throws many uncatchable exceptions or timeouts, performance will drop significantly.
    • Initialization: Static constructors and static initialization code will run again every time a new child process starts, which may negatively affect trace bits.
    • Recovery: If the in-process fuzzer crashes, you can recover the input that caused the exit from findings_dir/.cur_input.
    // Replace Fuzzer.Run with Fuzzer.OutOfProcess.Run
    Fuzzer.OutOfProcess.Run(...);
  5. Requirements for SharpFuzz

    master

    To use SharpFuzz with AFL, ensure your environment meets the following criteria:

    • Operating System: Linux or macOS. For Windows users, use Windows Subsystem for Linux (WSL). For native Windows support, use libFuzzer instead of AFL.
    • Build Tools: GNU make and a working compiler (gcc or clang) are required to compile afl-fuzz.
    • Runtime: .NET 8.0 or greater must be installed to instrument .NET assemblies.
    • Recommended: Installing PowerShell is recommended to simplify the fuzzing experience.
  6. Run a fuzzing session with fuzz.ps1

    master

    Once your project is instrumented, you can start fuzzing using the fuzz.ps1 script.

    Basic Usage

    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 Testcases

    Using Dictionaries for Improved Fuzzing

    For 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.dict

    Interpreting Results

    • Crashes: Input files that cause unhandled exceptions are saved in the findings/crashes directory.
    • Status: The total number of unique crashes is displayed in red on the afl-fuzz status screen.
    pwsh scripts/fuzz.ps1 Jil.Fuzz.csproj -i Testcases -x /usr/local/share/afl/dictionaries/json.dict
  7. Fuzzing .NET Core classes outside System.Private.CoreLib

    master

    To 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:

    1. Write the Fuzzer: Create a project and implement the fuzzing logic using Fuzzer.Run.
    2. Configure NuGet: Create a NuGet.Config to include the dotnetcore-feed source.
    3. Update Project File: Add a reference to Microsoft.Private.CoreFx.NETCoreApp and configure PackageConflictPreferredPackages to resolve conflicts with the standard Microsoft.NETCore.App package.
    4. Publish: Publish the project as a self-contained application (e.g., dotnet publish -r linux-x64).
    5. Instrument: Use 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.
    6. Run AFL: Start fuzzing using 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) { }
      });
    }
  8. Configure NuGet and Project for .NET Core IL-only assemblies

    master

    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:

    1. Add the Microsoft.Private.CoreFx.NETCoreApp package.
    2. Add 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>
  9. Fuzzing .NET Core classes inside System.Private.CoreLib

    master

    Fuzzing core types like DateTime requires using an IL-only build of the CoreCLR.

    1. Write the Fuzzer: Implement the logic using Fuzzer.Run.
    2. Build CoreCLR: Build the CoreCLR repository using the ./build.sh script with specific flags to ensure an IL-only build (skiptests skipcrossgen skipnative release).
    3. Prepare Application: Publish your fuzzing project as a self-contained application and manually copy the IL-only System.Private.CoreLib.dll from the CoreCLR build output into your application's publish directory.
    4. Instrument with Class Selection: Because instrumenting the entire assembly is inefficient, you must pass specific class or namespace prefixes to sharpfuzz to limit instrumentation to relevant paths.
    5. Run AFL: Execute 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();
          }
        }
      });
    }
  10. Set up libFuzzer as a SharpFuzz engine

    master

    To use libFuzzer as a fuzzing engine on Linux or Windows, follow these three steps:

    1. Obtain the libfuzzer-dotnet binary: Download the latest release for your platform from the libfuzzer-dotnet releases page. Alternatively, you can compile it from source using clang:
      clang -fsanitize=fuzzer libfuzzer-dotnet.cc -o libfuzzer-dotnet
    2. Update your entry point: In your project's Main function, replace the standard Fuzzer.Run or Fuzzer.OutOfProcess.Run calls with Fuzzer.LibFuzzer.Run.
    3. Execute the fuzzing script: Use the fuzz-libfuzzer.ps1 script to orchestrate the process.
    // In your Main function
    Fuzzer.LibFuzzer.Run();
  11. Run fuzzing with afl-fuzz and dotnet

    master

    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