Documentation Index
Fetch the complete documentation index at: https://mintlify.com/AFLplusplus/AFLplusplus/llms.txt
Use this file to discover all available pages before exploring further.
Fuzzing network services requires special handling because AFL++ is designed for single-execution file-based fuzzing. This guide covers proven techniques for adapting network targets.
Why Network Fuzzing is Different
Direct network fuzzing faces several challenges:
- 10-20x slowdown due to network stack overhead
- Difficult parallelization - binding to ports conflicts
- Stateful protocols require multi-packet exchanges
- Connection setup/teardown overhead per execution
Fuzzing over real network sockets is extremely inefficient. The recommended approach is to modify the target to read from files or use socket emulation.
Recommended Approaches
1. Modify Source to Read from File/Stdin (Best)
The most effective method provides a 100x performance improvement over network fuzzing.
Create a Fuzzing Harness
Write a new main() function that reads input from a file instead of the network:#include "server.h"
#include <unistd.h>
__AFL_FUZZ_INIT();
int main(int argc, char **argv) {
// Initialize server components (skip network setup)
init_server_state();
__AFL_INIT();
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
while (__AFL_LOOP(10000)) {
int len = __AFL_FUZZ_TESTCASE_LEN;
// Call protocol parser directly
process_network_packet(buf, len);
// Reset state between iterations
reset_server_state();
}
return 0;
}
Compile with Instrumentation
afl-clang-fast -o server-fuzz fuzz_harness.c server.c -I.
Fuzz at Maximum Speed
afl-fuzz -i input -o output -- ./server-fuzz
You now have persistent mode + shared memory testcases = x10-100 faster than network fuzzing!
This approach combines persistent mode with direct function calls, eliminating all network overhead. It’s the gold standard for network service fuzzing.
2. Socket Emulation with Preload Libraries (No Source Modification)
If you can’t modify source code, use a preload library to intercept socket calls.
Using AFL++ libaflppdesock
AFL++ includes a socket desocketing library:
cd utils/libaflppdesock
make
Usage:
# Set environment variables
export AFL_PRELOAD=/path/to/libaflppdesock.so
export AFL_DESOCK_MODE=1
# Fuzz the service
afl-fuzz -i input -o output -- ./network-service
The library intercepts:
socket(), bind(), listen(), accept()
connect(), send(), recv()
read(), write() on socket descriptors
And redirects them to file I/O.
Using libdesock (Alternative)
libdesock is another option:
git clone https://github.com/fkie-cad/libdesock
cd libdesock
make
Usage:
LD_PRELOAD=./libdesock.so AFL_PRELOAD=./libdesock.so afl-fuzz -i input -o output -- ./server
Using Preeny
Preeny offers more socket mocking options:
git clone https://github.com/zardus/preeny
cd preeny
make
Usage:
# Desocket a server
LD_PRELOAD=preeny/x86_64-linux-gnu/desock.so afl-fuzz -i input -o output -- ./server
# Desocket with multiple connections
LD_PRELOAD=preeny/x86_64-linux-gnu/desockmulti.so afl-fuzz -i input -o output -- ./server
Socket emulation libraries may not work with all programs. Complex async I/O or epoll/select usage can break compatibility.
3. Stateful Protocol Fuzzing with AFLNet
For protocols requiring multi-packet exchanges (HTTP, FTP, SMTP, etc.), use AFLNet.
Features:
- State-aware fuzzing
- Protocol message ordering
- Multi-packet test cases
- Support for FTP, SMTP, SSH, TLS, and more
Installation:
git clone https://github.com/aflnet/aflnet
cd aflnet
make
Usage:
# FTP server fuzzing
aflnet-fuzz -i in -o out -N tcp://127.0.0.1/8021 \
-P FTP -D 10000 -q 3 -s 3 -E -K \
-m none ./ftp-server -p 8021
# HTTP server fuzzing
aflnet-fuzz -i in -o out -N tcp://127.0.0.1/8080 \
-P HTTP -D 10000 -q 3 -s 3 -E -K \
./http-server
See AFLNet Documentation for supported protocols.
4. Network Branch (Last Resort)
AFL++ has an experimental networking branch:
git clone -b networking https://github.com/AFLplusplus/AFLplusplus
cd AFLplusplus
make
Usage:
afl-fuzz -i input -o output -N tcp://127.0.0.1:8080 -- ./server
This branch is outdated and unmaintained. Expect 10-20x slowdown. Only use if all other methods fail.
Protocol-Specific Examples
HTTP Server
Option 1: Direct harness
#include "http_parser.h"
#include <unistd.h>
__AFL_FUZZ_INIT();
int main(void) {
http_parser parser;
http_parser_settings settings;
http_parser_init(&parser, HTTP_REQUEST);
__AFL_INIT();
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
while (__AFL_LOOP(10000)) {
int len = __AFL_FUZZ_TESTCASE_LEN;
http_parser_execute(&parser, &settings, (char *)buf, len);
// Reset for next iteration
http_parser_init(&parser, HTTP_REQUEST);
}
return 0;
}
Compile and fuzz:
afl-clang-fast -o http-fuzz http_fuzz.c http_parser.c
afl-fuzz -i http_inputs -o output -- ./http-fuzz
Option 2: Socket emulation
AFL_PRELOAD=libaflppdesock.so afl-fuzz -i input -o output -- ./http-server -p 8080
DNS Server
#include "dns.h"
#include <unistd.h>
__AFL_FUZZ_INIT();
int main(void) {
dns_context ctx;
dns_init(&ctx);
__AFL_INIT();
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
while (__AFL_LOOP(10000)) {
int len = __AFL_FUZZ_TESTCASE_LEN;
// Process DNS packet
dns_parse_query(&ctx, buf, len);
// Reset state
dns_reset(&ctx);
}
return 0;
}
TLS/SSL Server
#include <openssl/ssl.h>
#include <unistd.h>
__AFL_FUZZ_INIT();
int main(void) {
SSL_CTX *ctx = SSL_CTX_new(TLS_server_method());
SSL *ssl = SSL_new(ctx);
__AFL_INIT();
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
while (__AFL_LOOP(1000)) {
int len = __AFL_FUZZ_TESTCASE_LEN;
BIO *bio = BIO_new_mem_buf(buf, len);
SSL_set_bio(ssl, bio, bio);
SSL_accept(ssl); // Fuzz TLS handshake
SSL_clear(ssl);
}
SSL_free(ssl);
SSL_CTX_free(ctx);
return 0;
}
SSH Server
Use AFLNet for stateful SSH fuzzing:
aflnet-fuzz -i ssh_seeds -o out -N tcp://127.0.0.1/2222 \
-P SSH -D 10000 -q 3 -s 3 -E -K \
./sshd -p 2222 -d
Capture Real Traffic
Use tcpdump/Wireshark to capture legitimate protocol exchanges:
# Capture HTTP requests
sudo tcpdump -i any port 80 -w capture.pcap
# Extract payloads with tshark
tshark -r capture.pcap -T fields -e data > payloads.txt
# Convert hex to binary
xxd -r -p payloads.txt > seed_001.bin
Use Protocol Templates
Create minimal valid protocol messages:
HTTP Request:
GET / HTTP/1.1\r\n
Host: localhost\r\n
\r\n
DNS Query:
import struct
# DNS header: ID + flags + counts
header = struct.pack('>HHHHHH', 0x1234, 0x0100, 1, 0, 0, 0)
# Question: example.com A record
question = b'\x07example\x03com\x00\x00\x01\x00\x01'
with open('dns_seed.bin', 'wb') as f:
f.write(header + question)
Many network applications have protocol test suites:
# Find test data in source tree
find ./tests -name "*.dat" -o -name "*.bin" | xargs -I{} cp {} input/
Multi-Instance Fuzzing Setup
#!/bin/bash
export AFL_PRELOAD=/path/to/libaflppdesock.so
export AFL_TESTCACHE_SIZE=50
# Main instance
screen -dmS main -- afl-fuzz -M main-$HOSTNAME -i input -o output -- ./server
# CMPLOG instance
screen -dmS cmplog -- afl-fuzz -S cmplog -c ./server-cmplog -l 2AT -i input -o output -- ./server
# ASAN instance
screen -dmS asan -- afl-fuzz -S asan -m none -i input -o output -- ./server-asan
# Secondary instances
for i in {1..5}; do
screen -dmS sec$i -- afl-fuzz -S sec$i -i input -o output -- ./server
done
Network services often have complex initialization. Use AFL_ENTRYPOINT to skip it when fuzzing with QEMU mode.
Handling Persistent State
Many servers maintain state across requests. Reset it between fuzzing iterations:
void reset_server_state(void) {
// Close any open files
close_all_files();
// Free allocated memory
free_buffers();
// Reset global variables
memset(&global_state, 0, sizeof(global_state));
// Reinitialize data structures
init_hash_table();
}
while (__AFL_LOOP(10000)) {
process_request(buf, len);
reset_server_state(); // Critical!
}
Failing to reset state causes “state pollution” where previous inputs affect current execution. This reduces coverage and finds fewer bugs.
Debugging Network Fuzz Targets
Check if Socket Emulation Works
# Run target with preload
LD_PRELOAD=./libaflppdesock.so ./server < test_input
# Should process input without needing actual network
Test Harness Directly
# Run harness on a test case
./server-fuzz < input/seed_001.bin
# Should execute without crashing
Enable Debug Output
export AFL_DEBUG=1
export AFL_DEBUG_CHILD=1
afl-fuzz -i input -o output -- ./server
Verify Coverage
# Check that instrumentation is working
afl-showmap -o /dev/null -- ./server < input/seed_001.bin
# Should show >0 tuples captured
Use Persistent Mode
Combine socket emulation with persistent mode for maximum speed:while (__AFL_LOOP(10000)) {
len = __AFL_FUZZ_TESTCASE_LEN;
buf = __AFL_FUZZ_TESTCASE_BUF;
process_packet(buf, len);
reset_state();
}
Skip Unnecessary Initialization
Don’t bind sockets, create threads, or load configs in the fuzzing harness:int main(void) {
// Skip: socket(), bind(), listen()
// Skip: load_config(), init_threads()
// Only initialize parsing components
init_parser();
// Start fuzzing
__AFL_INIT();
// ...
}
Use Shared Memory
Let AFL++ pass data via shared memory instead of files:__AFL_FUZZ_INIT();
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
Minimize State Reset
Only reset what’s necessary between iterations:// BAD: Reinitialize everything
reset_server_state();
init_parser();
load_config();
// GOOD: Reset only modified state
parser.offset = 0;
memset(buffer, 0, buffer_size);
Common Pitfalls
Forking servers - Servers that fork for each connection confuse AFL++. Disable forking or use a harness that calls the handler directly.
Multi-threading - AFL++ doesn’t handle multi-threaded targets well. Use single-threaded mode or disable threading in the fuzzing build.
Alarm/timeout handlers - Disable alarm() calls that could interfere with AFL++‘s timeout detection.
File descriptor leaks - Close all file descriptors in persistent mode, or you’ll hit the ulimit quickly.
Real-World Examples
Fuzzing nginx
# 1. Build with instrumentation
export CC=afl-clang-fast
export CXX=afl-clang-fast++
./configure --with-cc-opt="-fsanitize=address"
make
# 2. Use socket emulation
AFL_PRELOAD=libaflppdesock.so afl-fuzz -m none -i http_seeds -o output \
-- ./nginx -g 'daemon off;'
Fuzzing OpenSSH
# Build with AFL++
CC=afl-clang-fast ./configure --with-sandbox=no
make
# Create harness for key exchange
# (See examples/network_fuzzing/ in AFL++ repo)
afl-fuzz -i ssh_seeds -o output -- ./ssh-fuzz
Next Steps