This commit adds AFL++ (American Fuzzy Lop) fuzzing support to help find bugs, crashes, and security vulnerabilities in the URL parser. Changes: - Add fuzz_afl.c: AFL++ fuzz target that tests all parser functions - url_parse() with different flag combinations - url_parse_ipv4() and url_parse_ipv6() - url_serialize(), url_percent_decode(), url_remove_white_space() - Add build_afl.sh: Build script with AFL++ compiler detection - Add run_afl.sh: Automated fuzzing script with result reporting - Add url.dict: Dictionary file to guide AFL++ mutations - Add corpus/: Seed inputs covering various URL patterns - Basic URLs, IPv4/IPv6, percent-encoding, relative paths - Edge cases and special schemes (file://, mailto:, etc.) - Add FUZZING.md: Comprehensive documentation - Installation instructions - Quick start guide - Advanced usage (parallel fuzzing, sanitizers) - Troubleshooting tips - Add fuzz.c: Standalone mutation-based fuzzer (no AFL++ required) - Useful for quick testing without installing AFL++ - Implements various mutation strategies
5.2 KiB
Fuzzing the URL Parser
This directory contains fuzzing infrastructure for the URL parser using AFL++ (American Fuzzy Lop).
What is Fuzzing?
Fuzzing is an automated testing technique that feeds malformed, unexpected, or random data to a program to find bugs, crashes, and security vulnerabilities. AFL++ is a coverage-guided fuzzer that intelligently mutates inputs to maximize code coverage.
Prerequisites
Install AFL++
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install afl++
From source:
git clone https://github.com/AFLplusplus/AFLplusplus
cd AFLplusplus
make
sudo make install
Quick Start
-
Build the fuzz target:
cd tests/ ./build_afl.sh -
Run fuzzing:
./run_afl.shOr run for a specific duration:
./run_afl.sh 60 # Run for 60 minutes -
View results:
- Crashes:
findings/default/crashes/ - Hangs:
findings/default/hangs/ - Interesting inputs:
findings/default/queue/
- Crashes:
Directory Structure
tests/
├── fuzz_afl.c # AFL++ fuzz target
├── build_afl.sh # Build script
├── run_afl.sh # Run script
├── corpus/ # Seed inputs
│ ├── basic.txt
│ ├── complex.txt
│ ├── ipv4.txt
│ ├── ipv6.txt
│ └── ...
└── findings/ # AFL++ output (created when running)
└── default/
├── crashes/ # Inputs that caused crashes
├── hangs/ # Inputs that caused timeouts
└── queue/ # All interesting inputs found
What Gets Tested
The fuzz target tests multiple parser functions:
url_parse()- Main URL parser (with different flag combinations)url_parse_ipv4()- IPv4 address parserurl_parse_ipv6()- IPv6 address parserurl_serialize()- URL serializationurl_percent_decode()- Percent-decodingurl_remove_white_space()- Whitespace removal
Analyzing Results
Reproducing Crashes
If AFL++ finds a crash, you can reproduce it:
./fuzz_afl < findings/default/crashes/id:000000,*
Or use a debugger:
gdb ./fuzz_afl
(gdb) run < findings/default/crashes/id:000000,*
Minimizing Crash Inputs
AFL++ can minimize crash inputs to their smallest form:
afl-tmin -i findings/default/crashes/id:000000,* -o minimized.txt -- ./fuzz_afl
Viewing Statistics
While fuzzing is running, AFL++ displays real-time statistics:
- Cycles done: Number of times the fuzzer went through the queue
- Corpus count: Number of interesting test cases found
- Crashes: Number of unique crashes found
- Exec speed: Fuzzing throughput (executions per second)
Advanced Usage
Parallel Fuzzing
Run multiple AFL++ instances in parallel for better coverage:
# Terminal 1 (master)
afl-fuzz -i corpus/ -o findings/ -M fuzzer1 -- ./fuzz_afl
# Terminal 2 (secondary)
afl-fuzz -i corpus/ -o findings/ -S fuzzer2 -- ./fuzz_afl
# Terminal 3 (secondary)
afl-fuzz -i corpus/ -o findings/ -S fuzzer3 -- ./fuzz_afl
Using Different Modes
AFL++ supports various mutation modes:
# Explore mode (default)
afl-fuzz -i corpus/ -o findings/ -- ./fuzz_afl
# Exploitation mode (focus on found crashes)
afl-fuzz -i corpus/ -o findings/ -p exploit -- ./fuzz_afl
# COE (Continue on Error) mode
afl-fuzz -i corpus/ -o findings/ -c -- ./fuzz_afl
With AddressSanitizer (ASAN)
For better bug detection, rebuild with AddressSanitizer:
AFL_USE_ASAN=1 ./build_afl.sh
./run_afl.sh
Note: ASAN significantly slows down fuzzing but can detect memory issues that wouldn't cause crashes.
With UndefinedBehaviorSanitizer (UBSAN)
To detect undefined behavior:
AFL_USE_UBSAN=1 ./build_afl.sh
./run_afl.sh
Performance Tips
-
Disable CPU frequency scaling:
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor -
Increase system limits:
sudo sysctl -w kernel.core_pattern=core sudo sysctl -w kernel.sched_child_runs_first=1 -
Use persistent mode (requires code changes to fuzz target)
-
Use dictionary files for better mutations:
afl-fuzz -i corpus/ -o findings/ -x url.dict -- ./fuzz_afl
Continuous Fuzzing
For long-term fuzzing campaigns:
# Run in screen/tmux
screen -S fuzzing
./run_afl.sh
# Detach: Ctrl+A, D
# Reattach: screen -r fuzzing
Or use a systemd service for continuous fuzzing.
Troubleshooting
"No instrumentation detected"
Make sure you built with an AFL++ compiler (afl-gcc, afl-clang-fast, etc.)
"Suboptimal CPU scaling governor"
Run as suggested by AFL++ or set AFL_SKIP_CPUFREQ=1
Low execution speed
- Close other programs
- Use afl-clang-fast instead of afl-gcc
- Consider using QEMU mode for uninstrumented binaries
No new paths found
- Add more diverse seed inputs to corpus/
- Try different mutation strategies
- Consider parallel fuzzing