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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
# 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.
## Post-build-order: Burp/ZAP/Caido parity pass
Build order 1-7 is done. Researched what those three actually offer
(features and basic UI/UX) and triaged the gap into "should build soon"
/ "worth considering" / "skip" - see commit history for the full list;
tracking what's shipped vs. deferred here.
Shipped: vi-modal editing for the raw request textareas (table and
viewport already had vi nav by default - this was specifically about
textarea/textinput, which don't); a persistent status bar and a '?'
keybinding reference; display-only response JSON pretty-printing;
structured search filters (status:, source:, flagged:) alongside the
existing FTS5 text search; a flagged marker (★) for "revisit this" -
deliberately simpler than full free-text notes/comments, which would
need their own text-input overlay for comparatively modest extra value
over a boolean; noted as a real follow-up, not dropped silently; a
Comparer tool - mark an entry with 'c' (from history list or detail
view), 'c' again on a different entry opens a unified diff (git-diff
style, colored) of either side's request or response. Unified rather
than Burp's side-by-side: a two-column layout fights terminal width for
anything but a narrow window, and unified reuses the same scrollable-
viewport pattern already used everywhere else in the TUI. CRLF is
normalized to LF before diffing (display-only, same reasoning as the
JSON pretty-printer) so an HTTP/1.1 exact capture doesn't show every
line as changed from an invisible trailing \r; a standalone Decoder
tool ('d') - URL/Base64/Hex/HTML encode and decode, live output as you
type, tab to cycle transforms. Deliberately single-transform, not
chained/pipelined like Burp's Decoder - v1 scope, and pipeline-building
UI is real added complexity for a feature that's already useful without
it. Base64 decode tries standard/URL-safe/padded/unpadded variants in
turn rather than making the user pick, since real pasted data is as
likely to be one as the other. URL encode/decode uses strict RFC 3986
percent-encoding (space <-> %20), not Go's url.QueryEscape's form-
encoding behavior (space <-> '+'), since "URL encode" for a pentester
almost always means the former.
Still open from "worth considering": multiple concurrent Repeater tabs,
Intruder payload processing (encoding/case rules) and grep-match/
grep-extract on results, CA install UX per OS, multiple proxy listeners
and upstream proxy chaining. None of these are started yet.
Skipped deliberately (from the research, matches this tool's stated
scope): active/passive vulnerability scanning, plugin marketplace,
Collaborator/OAST, team collaboration, CI integration, client TLS
(mutual-TLS) certs, invisible/non-proxy-aware proxying.
|