1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
# mitmux - Intercepting Proxy TUI (Burp/Caido replacement)
## Overview
Daily-driver intercepting proxy for manual pentest work, terminal-based.
Prior art to read before writing code: Cruster (Rust, built on
hudsucker) - same problem, worth studying even though this build is Go.
## Stack
- Language: Go - memory safety on hostile input matters here more than
in the other projects, since this parses attacker-adjacent traffic
- TLS interception: Go's own `crypto/tls` + a CA cert generator
(analogous to `rcgen`) for per-domain leaf certs
- Proxy core: `net/http` + manual `CONNECT` handling, or a MITM proxy
library if one fits without fighting Go's aggressive header
normalization. Upstream requests are round-tripped manually (write
the request, read the response off the same connection) rather than
through `http.Transport` - Transport's automatic HTTP/2 dispatch keys
off a literal `*tls.Conn` type assertion on the dialed connection,
which a raw-byte-capturing wrapper around that connection defeats
(found by testing: it silently parsed HTTP/2 framing as HTTP/1.1).
- Storage: SQLite in WAL mode - blob columns for raw request/response
bytes, FTS5 index for search across bodies
- UI: Bubble Tea + Lipgloss (TUI), same family as the packet analyzer's
Go sibling if that ever gets built
## Architecture sketch (important - don't skip this)
- Split proxy engine from TUI. Headless daemon owns the listening
socket and the DB; TUI is a client over a Unix socket. The proxy
keeps running when the UI restarts, and a web UI or CLI scanner can
be bolted on later without touching the engine.
- Store raw bytes as the source of truth. Parse into a display view,
never re-serialize for storage - request smuggling, header injection,
and parser-differential bugs depend on the original malformed framing
surviving. For Repeater specifically, write requests as raw bytes
over the socket rather than through a normalizing HTTP client.
## Build order
1. Proxy + CA cert generation + plaintext HTTP passthrough
2. TLS interception (per-host cert generation, install CA)
3. History view (SQLite storage, raw bytes preserved) in the TUI
4. Repeater (raw-byte send/resend, the feature used daily)
5. Search/filter (FTS5)
6. Match-and-replace rules
7. Intruder-equivalent (last, optional)
## Open questions
- HTTP/2: handle natively (decided) - full fidelity over MITM'd
connections rather than downgrading to HTTP/1.1. Adds complexity to
CONNECT handling, stream framing, and step 3 storage (multiplexed
streams over one connection need per-stream request/response
boundaries, not just per-connection ones).
- CA install UX per OS (Linux/macOS/Windows trust stores)
- Whether WebSocket interception is v1 or a later addition
- Step 6 match-and-replace shipped headers-only. Body rules are a
separate, harder problem: request-body capture currently relies on
streaming the body straight from the client connection to the
upstream write (that's what makes it exact, byte for byte); a body
rule needs to materialize, transform, and re-send it instead, which
means deciding what "exact" even means for a rule-modified request
before touching that path again. Also still single-line-text-field
limited in the TUI (bubbles/textinput can't hold a literal CRLF), so
even with header rules, injecting a brand-new header line via the
form isn't possible yet - only rewriting/removing existing ones. The
underlying engine (rules.ApplyHeaders) already supports arbitrary
text-block edits; it's specifically the form UI that's constrained.
- Step 7 (Intruder-equivalent) shipped Sniper only: one payload set,
one §marked§ position fuzzed at a time, every other marked position
held at its base value - the mode that covers most real Intruder
usage. Battering ram / pitchfork / cluster bomb aren't implemented.
Sequential sending only (no concurrency), capped at 1000 generated
requests as a fixed safety limit against an accidental huge wordlist
combined with several positions. Reuses the Repeater send primitive
(proxy.Server.sendRaw) directly - an attack is just that primitive
run in a loop with generated bytes - and results land in the same
history table tagged source="intruder", same as Repeater's
source="repeater", rather than a separate results store.
|