Network-Based Fuzzing
- Network-Based Fuzzing
- Breaking a parser by feeding it garbage
- Choosing a Tool
- Fuzzing Fundamentals
- Boofuzz
- AFL-net
- Scapy as a Fuzzer
- Sulley / SPIKE
- Peach Fuzzer
- Defensics (Black Duck)
- Mu Dynamics / Spirent CyberFlood
- Automotive and ICS Protocol Fuzzers
- ICS/SCADA: Aegis (Wurldtech)
- Tool Comparison
- Methodology
- Key takeaways
- References
Breaking a parser by feeding it garbage
Fuzzing is the practice of sending large volumes of malformed, unexpected, or randomly-mutated input to a target and observing whether it misbehaves — crashes, hangs, leaks data, or accepts input it should reject. Network fuzzing applies this to protocol implementations: the target is a server (or client) and the fuzzer drives the network interface rather than a file or command-line argument.
Network fuzzing is one of the most productive techniques for finding vulnerabilities in closed-source devices — routers, embedded systems, IoT firmware, and proprietary protocols — where you have no access to source code and static analysis is limited.
⚠️ Fuzzers crash, hang, and disrupt whatever they point at, and they trip IDS/IPS signatures. Only ever fuzz an isolated target in a lab — never a production service or a network you don’t own. The crashes you’re hunting for are exactly the memory-corruption bugs an attacker would weaponize, which is why this lives alongside exploit mitigations.
Choosing a Tool
Not sure which fuzzer to use? Quick decision guide:
| I want to… | Use |
|---|---|
| Fuzz a stateful protocol (FTP, SMTP, custom) from scratch | Boofuzz |
| Fuzz a local server I compiled with instrumentation | AFL-net |
| Quickly mutate one packet type without a full framework | Scapy fuzz() |
| Test ICS/SCADA equipment for certification | Peach or Aegis |
| Broad commercial coverage with no setup | Defensics |
See the full tool comparison table at the bottom of this page for more detail.
Fuzzing Fundamentals
Generation vs. Mutation
| Strategy | How it works | Best for |
|---|---|---|
| Generation-based | Builds packets from a protocol grammar or template | Well-specified protocols (HTTP, SIP, Modbus) |
| Mutation-based | Takes a valid seed and flips/truncates/repeats bytes | Protocols without a public spec |
| Coverage-guided | Uses code coverage feedback to steer toward unexplored paths | Targets where you can instrument the binary (AFL++, libFuzzer) |
Most network fuzzers are generation- or mutation-based because you generally cannot instrument a remote target with coverage probes. Coverage-guided network fuzzing (AFL-net, Boofuzz with feedback) is possible when you control the binary.
What to Observe
A fuzzer is only as useful as its oracle — the mechanism that decides whether a response indicates a bug. Watch for:
- Crashes — connection reset, TCP RST, no response after previously responding
- Hangs — connection accepted but no response within a timeout
- Unexpected error codes — stack traces, debug messages, error strings the spec does not define
- Behavioral divergence — the server responds differently to structurally identical inputs
- Memory indicators — repeated responses that look like heap addresses or uninitialized memory
Lab Safety
Network fuzzers will crash, hang, or otherwise disrupt production services. Always fuzz against an isolated target in a lab environment. Many fuzzers will trigger IDS/IPS signatures and may be treated as an attack on a production network.
Boofuzz
Boofuzz is the maintained successor to Sulley and the de facto standard open-source network fuzzing framework. It provides:
- A Python DSL for defining protocol message structure
- Session management (connect, send, receive, reconnect on crash)
- Automatic crash detection and logging
- A web UI for monitoring progress at
http://localhost:26000
Install:
pip install boofuzz
Basic Fuzzer Structure
from boofuzz import *
def main():
session = Session(
target=Target(
connection=TCPSocketConnection("192.168.1.10", 80)
),
)
s_initialize("HTTP GET")
s_static("GET /")
s_string("index.html", name="path") # fuzz this field
s_static(" HTTP/1.1\r\nHost: ")
s_string("192.168.1.10", name="host") # fuzz this field
s_static("\r\n\r\n")
session.connect(s_get("HTTP GET"))
session.fuzz()
if __name__ == "__main__":
main()
s_string() generates a battery of known-bad strings: very long values, format string sequences, null bytes, negative integers encoded as strings, and more. s_static() fields are never mutated.
Defining a Custom Protocol
Boofuzz’s primitives map to protocol field types:
| Primitive | What it fuzzes |
|---|---|
s_string(default) |
Text field — length, encoding, special chars |
s_int(default, fmt, signed) |
Integer — boundary values, signedness |
s_bytes(default) |
Raw byte field |
s_size(block_name) |
Length field — auto-calculated, fuzzed for over/underflow |
s_block_start(name) / s_block_end(name) |
Group fields whose size is tracked by s_size |
s_delim(value) |
Delimiter — fuzzed for absence, repetition |
s_initialize("Modbus Read Holding Registers")
with s_block("PDU"):
s_byte(0x03, name="function_code") # Function code: Read Holding Registers
s_word(0x0000, name="start_address") # Starting address
s_word(0x000A, name="quantity") # Quantity of registers
Crash Detection and Restart
Boofuzz can restart the target after a crash using a ProcessMonitor (when you have access to the target process) or a custom restart_callback on the Session:
import subprocess
from boofuzz import *
def restart_target():
subprocess.run(["./restart_target.sh"])
session = Session(
target=Target(
connection=TCPSocketConnection("192.168.1.10", 502),
),
restart_callbacks=[restart_target],
crash_threshold_request=5,
crash_threshold_element=10,
)
AFL-net
AFL-net extends AFL (not AFL++) with network-protocol-aware mutations and coverage-guided feedback for server processes you can run locally. Unlike generic network fuzzers, AFL-net monitors code coverage on the server binary and steers mutations toward unexplored paths — dramatically improving efficiency.
Install:
git clone https://github.com/aflnet/aflnet
cd aflnet
make clean all
cd llvm_mode && make # required for afl-clang-fast
export AFLNET=$PWD/..
export PATH=$AFLNET:$PATH
Workflow:
- Compile the target server with AFL instrumentation:
CC=$AFLNET/afl-clang-fast ./configure make - Build a seed corpus of raw client-side protocol messages. The simplest way is to capture a valid session and manually extract the client payloads — one request message per file, in the order the protocol sends them:
# e.g., capture an RTSP session tcpdump -i lo -w session.pcap port 8554 # Then extract client messages manually; each file in corpus/ is one ordered messageAFL-net’s
aflnet-replaytool can replay a saved corpus against a live server to verify it works, but it does not auto-extract messages from pcaps. - Run AFL-net (RTSP example from the upstream README):
afl-fuzz -d -i corpus/ -o output/ \ -N tcp://127.0.0.1/8554 \ -P RTSP -D 10000 -q 3 -s 3 -E -K -R \ -- ./testOnDemandRTSPServer 8554Key flags:
-N tcp://host/port— target address and transport-P RTSP— protocol state machine (RTSP, FTP, DTLS12, DNS, DICOM, SMTP, SSH, SIP, DAAP-HTTP supported)-D 10000— microseconds to wait for the server to initialize before sending the first message-E— enable state-aware fuzzing (explores different server states)-K— send SIGTERM to server after each test case-R— enable region-level mutation, which respects protocol message boundaries
AFL-net is most useful when you have source access; for black-box targets, use Boofuzz or a mutation fuzzer.
Scapy as a Fuzzer
Scapy’s fuzz() function wraps any packet layer and randomizes its default, non-calculated fields (it leaves checksum and length fields alone — those are still auto-computed). This makes it a quick mutation fuzzer for any protocol Scapy supports at the packet/header level.
For protocols that run over UDP or raw layer-2, Scapy alone is sufficient. For TCP application-layer protocols (HTTP, FTP, SMTP), a bare send() with a fuzzed payload will never reach the application parser because there is no TCP handshake — use Boofuzz or write a script that establishes the connection first.
from scapy.all import *
# Fuzz all fuzzable fields of a TCP header (not the payload)
send(IP(dst="192.168.1.10") / fuzz(TCP(dport=80)), count=1000)
DNS fuzzing (UDP — works well with Scapy):
for _ in range(500):
send(IP(dst="192.168.1.1") / UDP(dport=53) / fuzz(DNS()))
DHCP request fuzzing:
for _ in range(200):
sendp(Ether(dst="ff:ff:ff:ff:ff:ff") /
IP(src="0.0.0.0", dst="255.255.255.255") /
UDP(sport=68, dport=67) /
fuzz(BOOTP()) /
fuzz(DHCP()),
iface="eth0")
Scapy fuzzing is excellent for quick exploration but offers no crash detection or session management. Pair it with tcpdump or Wireshark on the target to observe responses, and check target logs or run under a debugger for crash detection.
Sulley / SPIKE
SPIKE
SPIKE is one of the oldest network fuzzers, originally written by Dave Aitel. It uses C-based “spike scripts” (.spk files) to define protocol templates.
s_readline();
s_string_variable("HELO");
s_string(" ");
s_string_variable("victim.example.com");
s_string("\r\n");
s_string_variable() marks the field as fuzzable; s_string() sends fixed data. Using only s_string() would replay the same packet every time with no mutation.
Run against a target:
generic_send_tcp 192.168.1.10 25 smtp_helo.spk 0 0
SPIKE predates Boofuzz and lacks crash detection and logging. It is worth knowing for historical context and because many existing fuzzer corpora and scripts use its format, but Boofuzz is the better choice for new work.
Sulley
Sulley was the direct predecessor to Boofuzz. If you encounter Sulley scripts in older research, Boofuzz is largely API-compatible for basic cases. Prefer Boofuzz for any new work.
Peach Fuzzer
Peach Fuzzer is a generation-based framework that uses XML “Pit files” to describe protocol grammars. It supports both network and file fuzzing and is widely used in industrial control system (ICS/SCADA) and automotive security research.
Community edition:
git clone https://gitlab.com/peachtech/peach-fuzzer-community
Pit file example (partial — DNS query):
<DataModel name="DnsQuery">
<Number name="TransactionID" size="16"/>
<Number name="Flags" size="16" value="0x0100"/>
<Number name="Questions" size="16" value="1"/>
<Number name="AnswerRRs" size="16" value="0"/>
<Number name="AuthorityRRs" size="16" value="0"/>
<Number name="AdditionalRRs" size="16" value="0"/>
<Blob name="Query" valueType="hex" value="076578616d706c6503636f6d00"/>
<Number name="QType" size="16" value="1"/>
<Number name="QClass" size="16" value="1"/>
</DataModel>
Peach’s strength is protocol fidelity — complex, stateful protocols like DNP3, IEC 61850, and automotive CAN are better modeled in Peach’s XML than in Boofuzz’s Python DSL. The commercial Peach Enterprise adds smart fuzzing, coverage guidance, and pre-built Pit files for hundreds of protocols.
Defensics (Black Duck)
Defensics is the leading commercial network fuzzer. It supports over 300 protocols out of the box, including TLS, HTTP/2, gRPC, OPC-UA, automotive (UDS, DoIP, SOME/IP), and dozens of ICS protocols. It requires a commercial license.
Key features:
- Pre-built, continuously updated protocol test suites
- Instrumentation-free crash detection over the network
- Integration with CI/CD pipelines via REST API
- Detailed test case reproduction and minimization
Defensics is the standard in automotive (ISO 21434) and industrial security testing where protocol coverage breadth and audit-ready reporting matter more than flexibility.
Mu Dynamics / Spirent CyberFlood
Spirent CyberFlood (formerly Mu Dynamics) is a commercial appliance-based fuzzer oriented toward network equipment testing. It fuzzes at line rate, making it suitable for high-throughput targets (routers, firewalls, DPI appliances) that would not be stressed by a software fuzzer. Spirent also produces Avalanche, used for protocol conformance and security testing of network infrastructure.
Automotive and ICS Protocol Fuzzers
CANToolz / canfuzz
CANToolz is a modular framework for CAN bus analysis and fuzzing. Relevant for IoT/automotive targets.
python3 cantoolz.py -c examples/fuzz_example.py
AFL++ with network stubs
For ICS firmware that runs on emulated hardware (QEMU), AFL++ with persistent-mode or QEMU mode can fuzz the network parsing code directly, bypassing the need for a live network stack:
afl-fuzz -Q -i corpus/ -o output/ -- ./firmware_emulated @@
ICS/SCADA: Aegis (Wurldtech)
Aegis (now GE Vernova / Wurldtech Achilles) is the commercial standard for ICS protocol security testing, used to issue the Achilles certification found on industrial control equipment. It tests DNP3, IEC 60870-5-104, Modbus, EtherNet/IP, and proprietary protocols. It is used by vendors and utilities for pre-deployment testing, not typically by individual researchers.
Tool Comparison
| Tool | License | Protocol spec required? | Crash detection | Best for |
|---|---|---|---|---|
| Boofuzz | Open source (MIT) | Yes (Python DSL) | Yes (callbacks) | General network protocol research |
| AFL-net | Open source (Apache) | Seed corpus | Yes (local process crash/hang) | Locally-instrumented servers |
Scapy fuzz() |
Open source (GPL) | No | Manual | Quick protocol exploration |
| SPIKE | Open source | Yes (.spk files) | No | Legacy compatibility |
| Peach Community | Open source | Yes (XML Pit) | Limited | ICS/SCADA, stateful protocols |
| Peach Enterprise | Commercial | Yes (XML Pit) | Yes | ICS/SCADA enterprise |
| Defensics | Commercial | No (built-in) | Yes | Broad protocol coverage, audits |
| Spirent CyberFlood | Commercial | No (built-in) | Yes | High-throughput network gear |
| Aegis/Achilles | Commercial | No (built-in) | Yes | ICS/SCADA certification testing |
Methodology
A structured network fuzzing engagement follows this sequence:
- Reconnaissance — identify the protocol, port, and version. Capture a clean session with Wireshark to use as a seed corpus.
- Protocol understanding — read the RFC or standard if one exists. For proprietary protocols, perform static analysis of the implementation binary.
- Tool selection — use Boofuzz for stateful sessions; Scapy for quick one-shot mutation; AFL-net if you can instrument the binary.
- Session modeling — define the handshake sequence. Many servers require authentication before reaching the interesting code paths. Model the login exchange as
s_static()fields and fuzz only post-authentication messages. - Crash detection — establish a baseline response time. Flag any response that deviates significantly (timeout, RST, unexpected status code).
- Reproduction — Boofuzz logs the test case index of every crash. Replay the specific case and verify reproducibility before reporting.
- Minimization — reduce the crashing case to the smallest input that still triggers the bug. This makes root cause analysis much easier.
Key takeaways
- Fuzzing finds bugs by feeding a target malformed input and watching it misbehave — crash, hang, leak, or wrongly accept. It shines on closed-source targets (routers, IoT, proprietary protocols) where static analysis can’t reach.
- Know the three strategies: generation (build from a grammar), mutation (flip bytes of a valid seed), and coverage-guided (use code-coverage feedback) — most network fuzzers are generation/mutation because you usually can’t instrument a remote target.
- A fuzzer is only as good as its oracle: decide up front what counts as a bug (RST, timeout, undefined error string, memory-shaped responses) and baseline normal behavior first.
- Match the tool to the job: Boofuzz for stateful protocols from scratch, AFL-net when you can instrument a local binary, Scapy
fuzz()for quick one-shot UDP/L2 mutation, Peach/Defensics/Aegis for ICS/SCADA and broad commercial coverage. - Model the handshake: keep auth/setup messages
s_static()and fuzz only the post-authentication messages, or you never reach the interesting parser code. - Finish the loop: reproduce the logged crashing case, then minimize it to the smallest triggering input before reporting — and only ever do this against an isolated lab target.
References
- Boofuzz documentation. https://boofuzz.readthedocs.io/
- AFL-net repository. https://github.com/aflnet/aflnet
- Peach Fuzzer community edition. https://gitlab.com/peachtech/peach-fuzzer-community
- Black Duck Defensics — commercial protocol fuzzer. https://www.blackduck.com/fuzz-testing.html
- Spirent CyberFlood. https://www.spirent.com/products/cyberflood
- SPIKE source. https://github.com/guilhermeferreira/spikepp
- Zeller et al. — The Fuzzing Book (coverage-guided and grammar-based fuzzing). https://www.fuzzing-book.org/
- Sulley fuzzing framework (Boofuzz’s predecessor). https://github.com/OpenRCE/sulley
- GE/Wurldtech Achilles — ICS protocol security certification. https://www.ge.com/digital/applications/wurldtech-achilles
- Koziol et al., The Shellcoder’s Handbook, ch. 20 — network fuzzing techniques (print).
Related course pages: Wireshark · Capturing Packets with tcpdump · Suricata IDS/IPS
🛠️ Maintenance note: several tools here are aging — AFL-net builds on classic AFL (not AFL++) and its
llvm_modemay need patching against current LLVM; Peach Community and SPIKE are effectively unmaintained; Defensics is now a Black Duck product (Synopsys spun off its Software Integrity Group in 2024), and the links here point atblackduck.com. Boofuzz is the one to keep current — re-verify its API and the install command each term.