Skip to content

About

A tactical PCAP dissection and traffic replay engine with protocol-aware mutation for network testing

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

16 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Packet Replay Forge

Tactical PCAP dissection and traffic replay engine with protocol-aware mutation for network testing.

python typescript fastapi react scapy license

Packet Replay Forge is a web-based tool for security researchers and network engineers who need to dissect captured traffic, surgically mutate it at any protocol layer, and replay the rewritten packets — without touching Wireshark for the analysis half and tcpreplay for the send half.

Upload a PCAP, browse flows reconstructed from 5-tuples, inspect every field of every layer the way Wireshark renders them, then apply one of four mutation strategies (field replace, regex substitute on payloads, byte fuzz, bit flip) to a single packet, a whole flow, or the entire capture. The mutated packets are rebuilt through Scapy so lengths and checksums recompute automatically; you can then stream them on loopback with live per-packet progress over SSE, or download the result as a new PCAP.


Why this exists

Stock tools split this workflow awkwardly:

  • Wireshark is read-only — you can stare at your packets but you can't rewrite them.
  • tcpreplay sends bytes at you verbatim. If you want to change a TTL, a TCP flag, or an HTTP header you're writing a tcprewrite pipeline by hand.
  • Scapy scripts give you full control, but no flow view, no diff preview, no history — and nothing you can hand to a teammate.

Packet Replay Forge collapses all three into a single UI:

  1. Dissect — Scapy walks every layer; the frontend renders a Wireshark-style collapsible field tree plus a hex dump.
  2. Mutate — pick a target (packet / flow / all), pick a strategy, set the parameters. You get a diff preview of the raw frame and a list of every Scapy field that changed (including the checksums the engine recomputed for you).
  3. Replay — stream live to loopback with rate limiting and loop count, or export as a new .pcap for use with any downstream tool.

Screenshot

dashboard

Three-pane dark UI: flow list on the left, packet inspector + D3 charts (flow timeline, protocol donut, size histogram) in the center, mutation editor and replay engine on the right.


Architecture

┌─────────────┐   HTTP/SSE   ┌──────────────────────────────┐
│  React SPA  │ ───────────▶ │        FastAPI backend        │
│ (Vite/TS/D3)│ ◀─────────── │                               │
└─────────────┘  JSON/event  │  ┌──────────┐  ┌────────────┐ │
                             │  │  Parser  │→ │ FlowEngine │ │
                             │  │ (Scapy)  │  └────────────┘ │
                             │  └──────────┘        │        │
                             │        │             ▼        │
                             │        │      ┌────────────┐  │
                             │        │      │  Mutation  │  │
                             │        │      │   Engine   │  │
                             │        │      └────────────┘  │
                             │        ▼             │        │
                             │  ┌────────────────────────┐   │
                             │  │  In-memory SessionStore │  │
                             │  │  (PCAP → dissected AST) │  │
                             │  └────────────────────────┘   │
                             │              │                │
                             │              ▼                │
                             │       ┌────────────┐          │
                             │       │   Replay   │          │
                             │       │   Engine   │          │
                             │       └────────────┘          │
                             └──────────────────────────────┘

Data flow

  1. User drops a PCAP on the frontend → POST /sessions (multipart).
  2. pcap/parser.py calls rdpcap, walks each packet's layer chain, and turns every Scapy field into a JSON-friendly node (bytes → utf-8 / hex, FlagValue → human string, EDecimal → float, etc.).
  3. pcap/flows.py groups packets by a normalized 5-tuple so that A→B and B→A collapse into a single bidirectional flow.
  4. The parsed session (raw Scapy packets + summary metadata + flow list) is stored in an in-memory SessionStore keyed by a random 12-char UUID; no database.
  5. Inspection endpoints (/flows, /packets, /packets/{idx}, /stats) serve the UI with typed Pydantic DTOs.
  6. Mutation endpoint (POST /sessions/{id}/mutate) accepts a MutationSpec, applies it through mutation/engine.py, stores the rewritten packet list under a mutation id, and returns a diff preview with per-field change annotations.
  7. Replay endpoint (GET /sessions/{id}/replay/download) streams a freshly written PCAP back to the browser; /replay/stream yields SSE progress events while sending live packets on loopback.

Features

Parsing & inspection

  • Upload .pcap / .pcapng / .cap files up to 100 MB
  • Per-packet dissection across Ethernet / IPv4 / IPv6 / TCP / UDP / ICMP / DNS / Raw
  • Bidirectional 5-tuple flow reconstruction with bytes, packets, duration
  • Wireshark-style layered field tree + hex+ascii dump
  • Session stats: protocol byte breakdown, packet size histogram

Mutation strategies

  • field_replace — set a single Scapy field on a named layer. Lengths and checksums recompute on rebuild.
  • regex_sub — regex replacement on any Raw payload. Great for HTTP headers, DNS labels, plaintext protocols.
  • byte_fuzz — seeded random byte rewrite over a [offset, offset+len) range.
  • bit_flip — flip a single bit at any byte/bit offset.

Each mutation targets a scope: a single packet, every packet in a flow, or every packet in the session. Diff previews include the hex-encoded before/after frames and a list of which Scapy fields actually changed.

Replay

  • Export a mutated packet list as a downloadable .pcap
  • Live loopback replay via Scapy sendp with configurable rate limit (pps), loop count, and interface
  • Server-Sent-Events stream of per-packet progress: sent, bytes, errors, elapsed
  • Safety rail: non-loopback interfaces are refused unless allow_unsafe=true is passed explicitly

UI

  • Dark three-pane layout, bespoke protocol palette
  • D3 flow timeline (horizontal bars colored by protocol)
  • D3 protocol byte donut with live legend and total label
  • D3 packet size histogram on a symlog scale
  • Toast notifications, drag-and-drop upload, SSE-driven progress bar

Quick start

You need Python 3.12+ and Node 20+.

1. Backend

cd backend
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
./run.sh   # uvicorn app.main:app on :8000

Health check:

curl http://127.0.0.1:8000/health
# {"status":"ok","service":"packet-replay-forge"}

2. Frontend

cd frontend
npm install
npm run dev   # vite on :5173 with /api → :8000 proxy

Open http://localhost:5173 and drag a PCAP onto the dropzone.

3. Try it end-to-end via curl

# upload
SID=$(curl -sf -F "file=@/path/to/capture.pcap" \
  http://localhost:8000/sessions | jq -r .session_id)

# inspect
curl -sf http://localhost:8000/sessions/$SID/flows | jq
curl -sf http://localhost:8000/sessions/$SID/stats | jq

# mutate: set IP.ttl = 1 on every packet in the session
MID=$(curl -sf -X POST http://localhost:8000/sessions/$SID/mutate \
  -H "Content-Type: application/json" \
  -d '{"strategy":"field_replace","target_layer":"IP","target_field":"ttl","value":1}' \
  | jq -r .mutation_id)

# download the rewritten pcap
curl -sf "http://localhost:8000/sessions/$SID/replay/download?mutation_id=$MID" \
  -o mutated.pcap

# stream a live replay on loopback (SSE)
curl -sN "http://localhost:8000/sessions/$SID/replay/stream?mutation_id=$MID&iface=lo0&pps=100"

Technical deep-dive

Field-aware mutation, not byte-aware

Most PCAP rewriting tools let you overwrite bytes at offsets, which means you have to manually fix up every length field, every checksum, every option list. Packet Replay Forge operates on Scapy's field tree:

def field_replace(pkt, layer_name, field, value):
    new_pkt = pkt.__class__(bytes(pkt))        # clone by re-parse
    target = find_layer(new_pkt, layer_name)
    setattr(target, field, coerce(value))
    _force_recompute(new_pkt)                  # drop cached len/chksum
    return new_pkt.__class__(bytes(new_pkt))   # rebuild → recompute

_force_recompute walks every layer in the chain and deletes any cached len, plen, or chksum field. When Scapy rebuilds the packet on bytes(new_pkt), it recomputes them from scratch — so when you change IP.ttl, the IP header checksum updates too; when you change Raw.load length, IP.len and the TCP checksum both update.

The Scapy import trap

Scapy registers link-layer types lazily — if you from scapy.layers.inet import IP without ever touching scapy.all, the DLT tables stay partially populated and wrpcap crashes with KeyError: <class 'scapy.packet.Raw'>. The parser and replay modules intentionally import scapy.all to force full registration. (I learned this the slow way; look for the comment in backend/app/pcap/parser.py.)

Streaming replay via SSE

FastAPI's StreamingResponse plus a tiny yield f"data: {json.dumps(ev)}\n\n" generator is enough to push live progress events to the browser. On the client, EventSource delivers them with automatic reconnect. Each tick is one packet send, so pps is enforced by sleeping for max(0, target_time - elapsed) after every send — simple drift-correcting rate limit, no jitter accumulator needed.

Flow reconstruction

5-tuples are normalized so A:x→B:y and B:y→A:x collapse into one flow:

a, b = (src, sport), (dst, dport)
if a > b:
    a, b = b, a
key = (proto, a[0], b[0], a[1], b[1], proto)

The display endpoints use the first-observed direction; the matching endpoints use the normalized form.


Project layout

backend/
  app/
    main.py              FastAPI entrypoint + CORS + router wiring
    models.py            Pydantic DTOs shared with the frontend
    storage.py           In-memory SessionStore
    pcap/parser.py       Scapy → layered JSON dissector
    pcap/flows.py        5-tuple flow grouping
    mutation/strategies  field_replace, bit_flip, byte_fuzz, regex_sub
    mutation/engine.py   Spec → packet list + diff
    replay/engine.py     write_pcap + live loopback sendp w/ rate limit
    routes/              sessions, inspect, mutate, replay
frontend/
  src/
    App.tsx              Three-pane layout + session state
    api.ts               Typed fetch client (proxied via Vite /api)
    types.ts             Mirrors of backend Pydantic models
    components/          Header, Upload, FlowList, PacketInspector,
                         MutationEditor, ReplayPanel, charts/*

Safety notes

  • Live replay defaults to lo0. Sending on a real interface requires allow_unsafe=true and adequate privileges (sudo), and the engine will surface BPF errors to you directly via the SSE stream rather than swallowing them.
  • The mutation engine never touches the original parsed packet list; it always clones via pkt.__class__(bytes(pkt)) before mutating.
  • There is no persistence. Sessions live in process memory and disappear when the server restarts — by design.

License

MIT

About

A tactical PCAP dissection and traffic replay engine with protocol-aware mutation for network testing

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages