courses

Network-Based Fuzzing

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:

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:

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:

  1. Compile the target server with AFL instrumentation:
    CC=$AFLNET/afl-clang-fast ./configure
    make
    
  2. 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 message
    

    AFL-net’s aflnet-replay tool can replay a saved corpus against a live server to verify it works, but it does not auto-extract messages from pcaps.

  3. 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 8554
    

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

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:

  1. Reconnaissance — identify the protocol, port, and version. Capture a clean session with Wireshark to use as a seed corpus.
  2. Protocol understanding — read the RFC or standard if one exists. For proprietary protocols, perform static analysis of the implementation binary.
  3. Tool selection — use Boofuzz for stateful sessions; Scapy for quick one-shot mutation; AFL-net if you can instrument the binary.
  4. 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.
  5. Crash detection — establish a baseline response time. Flag any response that deviates significantly (timeout, RST, unexpected status code).
  6. Reproduction — Boofuzz logs the test case index of every crash. Replay the specific case and verify reproducibility before reporting.
  7. Minimization — reduce the crashing case to the smallest input that still triggers the bug. This makes root cause analysis much easier.

Key takeaways

References


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_mode may 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 at blackduck.com. Boofuzz is the one to keep current — re-verify its API and the install command each term.