Skip to content

sh3rp/pfuzz

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pfuzz

Raw socket packet fuzzer — craft and send packets at every layer without pcap.

Build

go build -o pfuzz ./cmd/pfuzz

Requires root (raw sockets).

Usage

pfuzz --dump -type <tcp|udp|icmp|arp|ethernet> [flags]

--dump shows the raw hex packet without sending. Drop it to actually transmit.

Packet types

Type Key flags
tcp --tcp-dport `--tcp-flags syn
udp --udp-dport --udp-payload <hex>
icmp --icmp-type --icmp-seq
arp --arp-src --arp-dst `--arp-op request
ethernet --eth-dst --eth-type 0x0806

Common flags

Flag Description Default
-type Packet layer tcp
-ip-src Source IP interface IP
-ip-dst Destination IP
-ip-ttl IP TTL 64
-ip-dscp DSCP value (0-63) 0
-ip-id IP identification (0=random) 0
-ip-df Don't Fragment flag true
-tcp-flags TCP flags (hyphen-separated) syn
-tcp-payload Payload as hex string
-udp-payload Payload as hex string
-count Packets to send (0=unlimited) 1
-delay Delay between sends 0
-iface Interface name auto
-hex Dump raw bytes before sending false
-dump Hex dump and exit (no send) false

Replay mode

Instead of constructing packets on the command line, you can drive pfuzz with a JSON config file that describes a full packet sequence. This is useful for scripted workflows like port scans, protocol state machines, or fuzzing campaigns.

CLI flags

Flag Description
--replay Path to a JSON replay config file
--dry-run Print packets as hex without sending
--vars key=value,key2=value2 — overrides config vars (CLI wins)

JSON schema

{
  "interface": "eth0",          // optional — auto-detected if omitted
  "pause": "50ms",              // global default pause between packets
  "dry_run": true,              // optional — same as --dry-run
  "vars": {                     // optional — template variables
    "SERVER": "10.0.0.1",
    "PORT": "443",
    "SUBNET": "192.168.1"
  },
  "packets": [
    {
      "type": "tcp",            // tcp | udp | icmp | ospf | arp | ethernet
      "ip_src": "${SUBNET}.1",  // template variable
      "ip_dst": "${SERVER}",
      "tcp_dport": "${PORT}",   // vars + env vars
      "tcp_flags": "syn",
      "ip_ttl": 64,
      "pause": "50ms",          // per-packet pause (overrides global)
      "repeat": 3               // send this packet N times (0 = infinite)
    }
  ]
}

All packet fields mirror the CLI flags but use JSON keys (snake_case):

Field Description
type Packet type (required)
ip_src Source IP
ip_dst Destination IP
ip_ttl IP TTL
ip_id IP identification
ip_dscp DSCP value
ip_df Don't Fragment flag
ip_proto IP protocol
tcp_sport TCP source port
tcp_dport TCP destination port
tcp_flags TCP flags (e.g. "syn", "syn-ack")
tcp_seq TCP sequence number
tcp_ack TCP acknowledgment number
tcp_window TCP window size
tcp_payload Payload as hex string
udp_sport UDP source port
udp_dport UDP destination port
udp_payload Payload as hex string
icmp_type ICMP type (default 8 = echo request)
icmp_code ICMP code
icmp_id ICMP identifier
icmp_seq ICMP sequence number
icmp_payload Payload as hex string
ospf_type OSPF type: hello, dbd, lsr, lsu, lsa
ospf_rid OSPF Router ID
ospf_aid OSPF Area ID
arp_op ARP operation: request, reply
arp_src ARP sender IP
arp_dst ARP target IP
dst_mac Destination MAC
src_mac Source MAC (auto-detected if omitted)
eth_type EtherType (e.g. "0x0800", `"0x0806")
pause Wait duration before next packet
repeat Number of sends (0 = infinite)

Template expansion

The replay engine supports three kinds of template substitution:

  1. Variables${VAR_NAME} or ${VAR_NAME:default}. Values come from the config's vars map first, then environment variables, then the default.

  2. Environment variables${SOME_ENV} without a default will resolve from os.LookupEnv.

  3. Range expansion{min..max} expands into a cartesian product of values. For example, "ip_dst": "${SUBNET}.{1..5}" produces 5 packets, one per host. Multiple range fields on the same packet generate the full cartesian product.

{
  "vars": { "SUBNET": "192.168.1" },
  "packets": [
    {
      "type": "tcp",
      "ip_src": "${SUBNET}.1",
      "ip_dst": "${SUBNET}.{1..5}",     // → 5 packets: .1 through .5
      "tcp_dport": "{22..25}",          // → 4 ports: 22, 23, 24, 25
      "tcp_flags": "syn"
    }
  ]
}

This single packet generates 5 × 4 = 20 unique SYN probes.

Examples

Run a TLS handshake replay:

sudo ./pfuzz --replay examples/tls-handshake-replay.json

Dry-run a templated port scan:

sudo ./pfuzz --replay examples/templated-scan.json --dry-run

Override vars from the command line:

TARGET=10.0.0.5 PORT=8080 sudo ./pfuzz --replay examples/templated-scan.json \
  --vars "TARGET=${TARGET},PORT=${PORT}"

CLI Examples

# SYN scan to port 443
sudo ./pfuzz -type tcp --ip-src 192.168.1.100 --ip-dst 10.0.0.1 --tcp-dport 443 --tcp-flags syn

# UDP DNS query (hex payload)
sudo ./pfuzz -type udp --ip-src 192.168.1.100 --ip-dst 8.8.8.8 --udp-dport 53 --udp-payload 01000001000000000000

# Ping (ICMP echo)
sudo ./pfuzz -type icmp --ip-src 192.168.1.100 --ip-dst 10.0.0.1 --icmp-seq 1 --count 0 --delay 1s

# ARP request
sudo ./pfuzz -type arp --arp-src 192.168.1.100 --arp-dst 192.168.1.1 --arp-op request

# TCP with custom payload and flags
sudo ./pfuzz -type tcp --ip-src 192.168.1.100 --ip-dst 10.0.0.1 --tcp-sport 54321 --tcp-dport 80 --tcp-flags syn-ack --tcp-payload 48454c4c4f

# Raw Ethernet frame (no IP)
sudo ./pfuzz -type ethernet --eth-dst ff:ff:ff:ff:ff:ff --eth-src 00:11:22:33:44:55 --eth-type 0x0806

# Burst 100 SYN packets
sudo ./pfuzz -type tcp --ip-src 192.168.1.100 --ip-dst 10.0.0.1 --tcp-dport 80 --tcp-flags syn --count 100

Architecture

pfuzz/
├── cmd/pfuzz/main.go     # CLI flags, packet dispatch, hex dump
├── internal/packet/      # Ethernet, ARP, IPv4, TCP, UDP, ICMP, OSPF builders
├── internal/replay/      # JSON replay loader and session runner
└── internal/socket/      # AF_INET/SOCK_RAW + AF_PACKET/SOCK_RAW senders

Socket strategy

  • IP-layer packets (TCP/UDP/ICMP/OSPF): AF_INET + SOCK_RAW + IPPROTO_RAW (255). Kernel doesn't add an IP header — we supply the full IP header. IP_HDRINCL set via setsockopt.
  • ARP and raw Ethernet: AF_PACKET + SOCK_RAW, bound to the interface at layer 2. Sends the full Ethernet frame directly.
  • No pcap dependency. No gopacket. Pure stdlib + syscall.

Checksums

All checksums (IP, TCP, UDP, ICMP) are computed in userspace before sending. The raw socket sends the fully-formed frame with correct checksums already baked in.

About

Network packet crafting and injection

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors