Skip to content

Ryad2/Fuzzing

Repository files navigation

CS-412 libpng AFL++ Fuzzing Lab

Ryad Aouak · Amir Ammar · Jon Kuçi · Pascal J. Kuschkowitz

The public repository is located at: https://github.com/Ryad2/Fuzzing

This repo contains the libpng 1.6.34 fuzzing setup, run wrappers, and report artifacts for the lab. The final report-facing branch is feat/campaigns-no-ubsan. The main report-facing runs are:

  • asan_fork
  • qemu
  • nosan_fork
  • asan_persistent
  • synthetic_bug

Quick Start

Build the Docker image:

docker build -t cs412-libpng-fuzz .

Open a shell in the container from the repo root:

docker run --rm -it --user "$(id -u):$(id -g)" -v "$PWD:/work" -w /work cs412-libpng-fuzz

# On Windows
docker run --rm -it -v "${PWD}:/work" -w /work cs412-libpng-fuzz

Inside the container, run one of the wrappers:

bash runs/asan_fork.sh
bash runs/qemu.sh
bash runs/nosan_fork.sh
bash runs/asan_persistent.sh
bash runs/synthetic_bug.sh

Each script resumes an existing AFL++ session automatically if findings/<run>/default/ exists. Pass --force to wipe all previous data for that run, do a clean rebuild, and start from scratch:

bash runs/asan_fork.sh --force

--force removes seeds_raw/, seeds/, crash_candidates/, showmap/, and findings/ for the run, then runs make clean && make build before launching AFL++.

Folder Overview

.
├── Dockerfile
├── Makefile
├── report.tex
│
├── runs/
│   ├── common.sh              # shared logic: seed gen, build, cmin, showmap, resume/force
│   ├── asan_fork.sh           # 12h primary campaign (ASAN + fork server)
│   ├── qemu.sh                # 5.7h binary-only campaign (no instrumentation, no ASAN)
│   ├── nosan_fork.sh          # short run for Q8 throughput baseline (no ASAN)
│   ├── asan_persistent.sh     # short run for Q8 persistent-mode throughput number
│   └── synthetic_bug.sh       # 60s validation run with the injected heap overflow
│
├── src/
│   ├── harness.c              # main fork-mode harness (used by asan_fork and nosan_fork)
│   ├── harness_persistent.c   # persistent-mode harness (__AFL_LOOP)
│   ├── harness_plain.c        # uninstrumented binary for QEMU mode
│   └── generate_libpng_seeds.py  # generates the 30 startup-safe PNG seeds
│
├── patches/
│   ├── libpng-nocrc.patch     # disables CRC checks so mutations reach the parser
│   └── libpng-synthetic-bug.patch  # injects a one-byte heap overflow for Q5 validation
│
├── results/
│   ├── fuzzing_campaign.md    # campaign summary and artifact index
│   ├── q8_speed_comparison.md # throughput table and edge counts for Q8
│   ├── asan_fork/
│   │   ├── screenshots/
│   │   │   └── 12h_run.png
│   │   ├── plots/
│   │   │   ├── edges.png
│   │   │   ├── exec_speed.png
│   │   │   ├── high_freq.png
│   │   │   └── low_freq.png
│   │   └── raw/
│   │       ├── fuzzer_stats
│   │       ├── plot_data
│   │       └── queue/         # full corpus snapshot (~2161 entries)
│   ├── qemu/
│   │   ├── screenshots/
│   │   │   └── qemu_5h.png
│   │   ├── plots/
│   │   │   ├── edges.png
│   │   │   └── exec_speed.png
│   │   └── raw/
│   │       ├── fuzzer_stats
│   │       ├── plot_data
│   │       └── queue/
│   ├── nosan_fork/
│   │   ├── nosan_fork.md
│   │   ├── screenshots/
│   │   │   └── nosan_8_min.png
│   │   └── raw/
│   │       ├── fuzzer_stats
│   │       └── plot_data
│   ├── asan_persistent/
│   │   ├── asan_persistent.md
│   │   ├── screenshots/
│   │   │   └── persistent_5min.png
│   │   └── raw/
│   │       ├── fuzzer_stats
│   │       └── plot_data
│   ├── synthetic_bug/
│   │   ├── screenshots/
│   │   │   ├── synthetic_afl_crash_found.png
│   │   │   └── 60_sec_1_crash.png
│   │   ├── crash_input/       # the single crashing input AFL++ saved
│   │   ├── triage/
│   │   │   ├── asan_trace.txt
│   │   │   ├── asan_trace_verbose.txt
│   │   │   └── run_stdout.txt
│   │   └── findings/          # full AFL++ output from the 60s synthetic run
│   │       └── default/
│   │           ├── fuzzer_stats
│   │           ├── crashes/   # contains the saved crashing input
│   │           └── queue/
│   └── q8_libpng_pngtest/
│       ├── README.md
│       └── raw/
│           └── fuzzer_stats   # total_edges=7449 (library-only proxy for Q8)
│
├── findings/                  # live AFL++ output dirs (not for the report)
│   └── <run_name>/
│       └── default/
│           ├── fuzzer_stats
│           ├── plot_data
│           ├── queue/
│           ├── crashes/
│           └── hangs/
│
├── seeds/                     # minimized corpora (post afl-cmin), one dir per run
├── seeds_raw/                 # raw generated PNGs before minimization, one dir per run
├── showmap/                   # afl-showmap edge snapshots (edges.all, edges.unique)
│
├── crash_candidates/          # seeds that crash the target at startup, quarantined here
│   └── <run_name>/            # per-run subfolder; kept out of AFL++ corpus calibration
│
├── sure_crash/
│   ├── info.md                # explains the CVE-2018-13785 file and why it's excluded
│   └── 01_rgb8_row_factor_divzero.png  # exact crashing input for the historical bug
│
└── triage/
    ├── triage_notes.md
    ├── check_crashes/
    │   └── crashes.<timestamp>/
    │       ├── summary.csv
    │       └── id_XXXX/       # per-crash: CASE.txt, png_info.txt, sha256, gdb traces
    │           ├── CASE.txt
    │           ├── png_info.txt
    │           ├── gdb_nosan_interrupt_bt.txt
    │           ├── gdb_qemu_interrupt_bt.txt
    │           ├── nosan_short/
    │           ├── nosan_long/
    │           ├── qemu_short/
    │           └── qemu_long/
    └── check_hangs/
        └── hangs.<timestamp>/
            └── id_XXXX/
                ├── CASE.txt
                ├── nosan/
                └── qemu/

Notes

All campaigns were run on Windows (WSL2) with a Ryzen 5 5600 and 16 GB RAM. The live findings/ directories are AFL++ output; results/ holds the cleaned report-facing copies.

All real campaign builds apply libpng-nocrc.patch, which disables CRC32 chunk verification so AFL++ mutations reach the actual parser, decompressor, and row-processing code instead of being rejected at the checksum gate. The synthetic bug validation uses a completely separate build (make build-asan-synthetic) with libpng-synthetic-bug.patch applied on top — this build is never used for any real campaign run.

Overall we did more than 100 hours of fuzzing across multiple machines and we had a lot of fun :)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors