diff options
| author | srdusr <[email protected]> | 2026-02-18 21:55:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2026-02-18 21:55:00 +0200 |
| commit | 5200e412606c221fb618bd8e9a57a897553215d9 (patch) | |
| tree | f3d118728f6986bad065cd82f290350cdcb6ab4a /README.md | |
| parent | b8e5d5ea37cc64ffa05352c3fb3130ca59471935 (diff) | |
| download | mitmux-5200e412606c221fb618bd8e9a57a897553215d9.tar.gz mitmux-5200e412606c221fb618bd8e9a57a897553215d9.zip | |
Add README
the third explicit ask, alongside the Burp/ZAP/Caido gap
pass and vi bindings: user-facing documentation. Covers what mitmux is
and why it's two binaries (daemon owns the proxy and history, TUI is a
thin client - restarting or crashing the UI never interrupts capture),
install/build, a quick-start walkthrough (start daemon, trust the CA,
point a client at it, open the TUI), per-feature usage (History,
Repeater, Intruder, match-and-replace), the full search syntax
(free text, host:/status:/source:/flagged: filters, AND/OR/NOT), the
vi-modal command set for the Repeater/Intruder editors, an architecture
section (daemon/TUI split, the raw-bytes-as-source-of-truth capture
model and exact-vs-reconstructed distinction), package layout, dev
commands, and an honest known-limitations list matching the scope
decisions already tracked in PLAN.md rather than overselling anything.
Verified rather than just written: built both binaries with the exact
commands in the Install section, ran mitmuxd with no flags to confirm
the documented defaults (127.0.0.1:8080, ~/.config/mitmux) are actually
what ships, ran the exact curl command from Quick start against a real
site through the proxy, and opened the TUI to confirm the captured
request actually shows up - the full documented flow, end to end, not
assumed correct because it reads correctly.
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..70375c7 --- /dev/null +++ b/README.md @@ -0,0 +1,258 @@ +# mitmux + +A terminal-based intercepting proxy for manual pentest work - a +daily-driver alternative to Burp Suite, Caido, or OWASP ZAP that runs +entirely in your terminal, with vi-style modal editing for raw +requests. + +mitmux is two binaries: `mitmuxd`, a headless daemon that owns the +proxy listener and the SQLite history database, and `mitmux`, a Bubble +Tea TUI that talks to it over a Unix socket. The daemon keeps running +(and keeps capturing traffic) independently of the TUI - close the UI, +reopen it later, the proxy never stopped. + +For the build history, the architectural decisions behind it, and a +list of what's deliberately not implemented (and why), see +[`PLAN.md`](PLAN.md). + +## Features + +- **Intercepting proxy**: plaintext HTTP passthrough and TLS + interception (per-host leaf certificates signed by a locally + generated CA), with HTTP/1.1 and HTTP/2 handled natively and + independently on the client and upstream legs - a client that only + speaks HTTP/1.1 and an origin that prefers HTTP/2 both work correctly + in the same request. +- **History**: every request/response captured to SQLite. Raw wire + bytes are preserved byte-for-byte on HTTP/1.1 legs (what request + smuggling and parser-differential analysis actually needs); HTTP/2 + legs - which have no single "raw bytes" representation, being + multiplexed HPACK-compressed framing - are reconstructed and marked + as such, never silently presented as exact. +- **Search**: full-text search (FTS5) across headers and bodies, plain + text just works (`example.com`, `x-forwarded-for`, `192.168.1.1` - + no quoting needed), plus structured filters: `status:404`, + `status:4xx`, `status:>=400`, `source:repeater`, `flagged:true`, + column filters like `host:example.com`, and `AND`/`OR`/`NOT`. +- **Repeater**: edit and resend a raw request. What you type is what + goes on the wire - no normalization, no auto-fixed `Content-Length`, + no "helpful" reformatting. That's the point of a Repeater. +- **Intruder** (Sniper only): mark positions in a request template + with `§markers§`, supply a payload list, fuzz one position at a time + against a shared payload set. Results land in the same history table + as everything else, searchable the same way. +- **Match-and-replace**: header rewrite rules (add, remove, or modify) + for requests and/or responses, applied live as traffic passes + through. History still shows what was actually sent/received on each + side - match-and-replace transforms the wire, it doesn't rewrite the + audit trail. +- **Flagging**: mark an entry to revisit later (★), filterable via + `flagged:true`. +- **Vi-modal editing**: the raw request editors (Repeater, Intruder) + are real modal editors - normal mode by default, `i`/`a`/`o`/etc. to + insert, `hjkl`, `dd`/`yy`/`p`, word motions, `gg`/`G`. See + [Vi bindings](#vi-bindings) below. Everywhere else (history table, + read-only response views), standard vi navigation (`j`/`k`, `g`/`G`, + `ctrl+u`/`ctrl+d`) already works - that's the underlying TUI + library's default, not something layered on top. + +## Install / build + +Requires Go 1.26.5+ (see `go.mod`). + +```sh +git clone <this repo> +cd mitmux +go build -o bin/mitmuxd ./cmd/mitmuxd +go build -o bin/mitmux ./cmd/mitmux +``` + +## Quick start + +1. **Start the daemon.** By default it listens on `127.0.0.1:8080` and + stores its CA and history database under `~/.config/mitmux` (XDG + config dir): + + ```sh + ./bin/mitmuxd + ``` + + On first run it generates a root CA and prints where the cert + landed, e.g. `~/.config/mitmux/ca.pem`. + +2. **Trust the CA.** To intercept HTTPS without constant certificate + warnings, import `ca.pem` into whatever's making the requests - + your browser's certificate store, `curl --cacert`, a mobile device's + trusted-certificate settings, etc. (Automated per-OS trust-store + installation isn't implemented yet - see `PLAN.md`.) + +3. **Point a client at the proxy.** e.g.: + + ```sh + curl -x http://127.0.0.1:8080 --cacert ~/.config/mitmux/ca.pem https://example.com/ + ``` + + Or configure your browser's proxy settings to `127.0.0.1:8080`. + +4. **Open the TUI** (in another terminal - the daemon keeps running + independently): + + ```sh + ./bin/mitmux + ``` + +Both binaries take flags for non-default setups - `-listen`, `-socket`, +`-ca-dir`, `-db` on `mitmuxd`; `-socket` on `mitmux`. Run either with +`-h` for the full list. + +## Usage + +Press `?` from any screen in the TUI for the full, current keybinding +reference - it's generated from the same source as this document, so +it never drifts out of date the way a static list can. The summary +below is enough to get going. + +### History (the default view) + +| Key | Action | +|---|---| +| `↑`/`↓` or `j`/`k` | navigate (also `g`/`G` top/bottom, `ctrl+u`/`ctrl+d` half-page) | +| `enter` | view request/response detail | +| `r` | open in Repeater | +| `i` | open in Intruder | +| `f` | toggle flag | +| `/` | search | +| `m` | match-and-replace rules | +| `q` | quit | + +### Detail view + +`tab` switches request/response, `p` toggles pretty-printed JSON on the +response (display-only - never touches the stored or resent bytes), +`r`/`i` jump straight to Repeater/Intruder seeded from this entry, `esc` +back. + +### Search syntax + +Plain text searches headers and bodies on both sides of the exchange. +A handful of characters that are FTS5 syntax rather than literal text +(`.`, `-`, `/`, `@`, and more) are handled transparently - you don't +need to quote a domain name or an IP address for it to work. + +- `example.com`, `x-forwarded-for`, `192.168.1.1` - literal text, just works +- `host:example.com`, `AND`, `OR`, `NOT` - FTS5 syntax for column + filters and boolean queries +- `status:404` - exact status code +- `status:4xx` (also `2xx`/`3xx`/`5xx`) - status range shorthand +- `status:>=400`, `status:!=200` - status comparison operators +- `source:proxy` / `source:repeater` / `source:intruder` - where the + request came from +- `flagged:true` / `flagged:false` +- Combine freely: `admin status:200 source:repeater` + +### Repeater / Intruder - vi bindings + +The request editors start in **normal mode**, not insert mode - like +real vi. Press `i` (or `a`/`I`/`A`/`o`/`O`) to start typing, `esc` to +go back to normal mode. The mode indicator (`-- NORMAL --` / +`-- INSERT --`) is always visible while one of these editors is +focused. + +| Normal-mode key | Action | +|---|---| +| `h` `j` `k` `l` | left / down / up / right | +| `0` / `$` | line start / end | +| `w` / `b` | word forward / back | +| `x` | delete character | +| `i` `a` `I` `A` | insert: before cursor / after cursor / line start / line end | +| `o` / `O` | open a line below / above and insert | +| `dd` / `yy` | delete / yank the current line | +| `p` / `P` | paste below / above | +| `dw` `d$` `d0` | delete word / to end of line / to start of line | +| `gg` / `G` | top / bottom of the buffer | +| `esc` | (in insert mode) back to normal mode - never leaves the view | + +This is a genuine, if intentionally scoped, modal editor: it translates +these commands into the underlying text widget's own editing primitives +rather than reimplementing cursor and line manipulation. Not +implemented: registers beyond a single yank slot, visual mode, ex +commands, macros, and counts (`3dd`, `5j`). There's no undo, because +the underlying text widget doesn't have one either. + +`ctrl+r` sends (Repeater) or starts the attack (Intruder). `tab` +switches panes. In Intruder's template pane specifically, `ctrl+g` +inserts a `§` marker at the cursor if typing the character directly +isn't convenient on your keyboard/terminal. + +### Match-and-replace rules + +Press `m` from the history view. Rules match request or response +headers (`a` add, `enter`/`e` edit, `d` delete, `space` toggle +enabled). Matching is literal-substring by default, or regex if the +rule's Regex toggle is on. Rules operate on the raw header *block* as +text, not per-value substitution, so a rule can add or remove a header +entirely, not just rewrite an existing one. Currently headers only - +see `PLAN.md` for why body rules are a separate, harder problem. + +## Architecture + +`mitmuxd` owns the proxy listener and the SQLite database; `mitmux` is +a thin client that only ever talks to the daemon over a Unix socket +(`internal/ipc`). This split is deliberate: a web UI, a CLI scanner, or +any other client could be bolted on later without touching the proxy +engine, and the TUI restarting (or crashing) never interrupts capture. + +Capture fidelity is the other core design constraint: for HTTP/1.1 +traffic, request and response bytes stored in history are exactly what +was read off the wire - captured via a `net.Conn` wrapper that records +bytes as `net/http`'s own (memory-safe, battle-tested) parser consumes +them, rather than re-serializing a parsed representation. For HTTP/2, +which has no meaningful single "raw bytes" form, the stored +representation is a reconstruction, and every stored entry says which +kind it is (`request_exact`/`response_exact` in the database, "exact" +vs. "reconstructed" in the UI). Repeater and Intruder both write raw +bytes straight to the wire for the same reason - the whole point of a +Repeater is that a deliberately malformed request reaches the target +unmodified. + +Package layout: + +``` +cmd/mitmuxd/ daemon entrypoint +cmd/mitmux/ TUI entrypoint +internal/ca/ root CA + per-host leaf certificate generation +internal/proxy/ proxy engine: HTTP/CONNECT handling, capture, Repeater, Intruder +internal/rules/ match-and-replace engine +internal/store/ SQLite storage, FTS5 search +internal/ipc/ daemon <-> client protocol (JSON over a Unix socket) +``` + +## Development + +```sh +go build ./... +go vet ./... +gofmt -l . # should print nothing +go test ./... +``` + +There's no mock traffic layer - the test suite covers pure logic +(marker parsing, search-query parsing) that's worth locking down with +real tests rather than trusting by inspection. Everything that touches +the network, the daemon, or the TUI has been verified by actually +running it against real traffic during development; see commit +messages for what was checked and how. + +## Known limitations + +Deliberate scope decisions, not oversights - see `PLAN.md` for the +reasoning behind each: + +- Match-and-replace: headers only, no body rules yet +- Intruder: Sniper attack only (no battering ram / pitchfork / cluster + bomb), sequential sending, capped at 1000 requests per attack +- No automated CA installation into OS/browser trust stores +- No WebSocket interception +- No client (mutual-TLS) certificate support +- No active or passive vulnerability scanning, no plugin system - this + is a manual-testing tool, not a scanner |