mitmproxy & nmap
Two tools earn a permanent place in a terminal-based tester’s kit: mitmproxy to see and rewrite the traffic between a client and a server, and nmap to discover what’s listening on a host. This page is a practical guide to both — installation, the core workflows, and the scope reminders that keep you on the right side of the rules.
mitmproxy: an intercepting proxy
Section titled “mitmproxy: an intercepting proxy”An intercepting proxy sits between a client (browser, mobile app, script) and the server. Instead of the client talking to the server directly, it talks to the proxy, which forwards the request, receives the response, and hands it back — logging and optionally modifying everything in between. For HTTPS this requires the proxy to present its own certificate, which is why you install its CA (see below).
mitmproxy is a free, open-source proxy that ships as three front-ends over the same engine:
| Command | Interface | Use it when |
|---|---|---|
mitmproxy |
Interactive terminal UI (TUI) | Live, hands-on inspection in a terminal |
mitmweb |
Browser-based UI | You prefer a clickable flow list |
mitmdump |
Non-interactive, tcpdump-style |
Scripting, CI, and running Python addons headless |
Installing
Section titled “Installing”# pipx keeps it isolated (recommended):pipx install mitmproxy# or Homebrew on macOS:brew install mitmproxy# Verify:mitmproxy --versionPointing a client at it and installing the CA cert
Section titled “Pointing a client at it and installing the CA cert”By default mitmproxy listens on port 8080. Configure your client to use it as an HTTP(S) proxy:
# For command-line tools, environment variables are the quickest route:export HTTP_PROXY=http://127.0.0.1:8080export HTTPS_PROXY=http://127.0.0.1:8080# Now this request flows through mitmproxy:curl -s https://example.com/ >/dev/nullFor HTTPS to work without errors, the client must trust mitmproxy’s CA. Start mitmproxy once (it
generates the CA on first run), then with the proxy active visit the magic host mitm.it in a
browser routed through it — that page serves the CA cert and per-OS install instructions. For curl
you can instead point at the generated cert directly:
# The CA is written here on first run:ls ~/.mitmproxy/mitmproxy-ca-cert.pem# Trust it for a single curl call:curl -s --proxy http://127.0.0.1:8080 \ --cacert ~/.mitmproxy/mitmproxy-ca-cert.pem https://example.com/ >/dev/nullIntercepting, inspecting, modifying, replaying
Section titled “Intercepting, inspecting, modifying, replaying”In the mitmproxy TUI, flows stream into a list. The core loop:
- Inspect — arrow to a flow and press Enter to open it; Tab switches between request and response; you see headers, body, and timing.
- Intercept — press
iand type a filter (e.g.~u /api/) to pause matching flows so you can edit them before they continue. - Modify — with a flow open, press
eto edit a component (method, URL, headers, body), thenato resume/accept the (possibly edited) flow. - Replay — press
ron a request to re-send it. This is the fast way to iterate on a single request — tweak a header, replay, compare — without rebuilding it incurl.
Filtering
Section titled “Filtering”mitmproxy’s filter language is the same in the TUI (f to set a view filter), in mitmdump, and in
addons. The common expressions:
| Filter | Matches |
|---|---|
~u regex |
URL matches regex |
~d domain |
Host/domain |
~m POST |
HTTP method |
~c 500 |
Response status code |
~q / ~s |
Requests / responses |
~t json |
Content-Type |
Combine with & (and), | (or), ! (not): ~m POST & ~u /api/ & !~d cdn.example.
mitmdump for automation
Section titled “mitmdump for automation”mitmdump runs headless and is the right tool for scripting and running addons. Capture to a file and
replay later:
# Record all flows matching /api/ to a file:mitmdump -p 8080 "~u /api/" -w api-flows.mitm# Later, read them back:mitmdump -r api-flows.mitm -n # -n = don't start a listening proxyWriting Python addons
Section titled “Writing Python addons”The real power is scripting transforms in Python. An addon is a module with hook functions;
mitmproxy calls request(flow) before a request goes out and response(flow) when a response comes
back. flow.request and flow.response are fully mutable.
from mitmproxy import http, ctx
def request(flow: http.HTTPFlow) -> None: # Automatically inject a header on every outgoing request: flow.request.headers["X-Test"] = "authorized-research" ctx.log.info(f"-> {flow.request.method} {flow.request.pretty_url}")
def response(flow: http.HTTPFlow) -> None: # Example transform: flag any response that reflects our marker. if b"zzqmarker123" in flow.response.content: ctx.log.warn(f"reflection in {flow.request.pretty_url}")# Load the addon (works with mitmproxy, mitmweb, or mitmdump):mitmdump -s add_header.pyAddons let you automate exactly the tedious transforms testing needs: strip a security header to
study behavior, rewrite an ID on every request to test IDOR at scale, or tag reflected input for XSS
triage. Because it’s just Python, flow.request.query, flow.request.headers, and
flow.response.content are all yours to read and rewrite.
Understanding mobile / app API traffic
Section titled “Understanding mobile / app API traffic”A huge reason to reach for mitmproxy is that mobile and desktop apps hide their APIs. Route the
device through the proxy (set the phone’s Wi-Fi proxy to your machine’s IP:8080, install the CA via
mitm.it) and the app’s REST/GraphQL calls appear as flows you can read and replay. That turns an
opaque app into the same terminal-friendly HTTP surface as a website.
nmap: mapping hosts and services
Section titled “nmap: mapping hosts and services”nmap (Network Mapper) discovers hosts on a network and figures out what services and versions they’re running. In authorized testing it answers “what’s actually exposed here?” before you spend time on any single service.
# Install:sudo apt install nmap # Debian/Ubuntubrew install nmap # macOSnmap --versionHost discovery
Section titled “Host discovery”Before scanning ports, find which hosts are up. -sn does a ping scan (host discovery only, no
port scan):
nmap -sn 192.0.2.0/24 # which hosts in this range respond?Port scanning
Section titled “Port scanning”-sS is a SYN scan (fast, needs root; sends a SYN and never completes the handshake). -sT is a
full TCP connect scan (no root needed, more visible). Choose ports with -p, or --top-ports
for the most common ones.
sudo nmap -sS -p 1-1000 scanme.nmap.org # SYN scan, ports 1-1000nmap -sT --top-ports 100 scanme.nmap.org # connect scan, 100 most common portsnmap -p 80,443,8080,8443 scanme.nmap.org # specific portsnmap -p- scanme.nmap.org # all 65535 ports (slow)Service/version and OS detection
Section titled “Service/version and OS detection”-sV probes open ports to identify the service and version (e.g. nginx 1.25.3). -O attempts
OS detection from TCP/IP fingerprints (needs root, less reliable). -A bundles version
detection, OS detection, default scripts, and traceroute.
nmap -sV scanme.nmap.org # service/version detectionsudo nmap -O scanme.nmap.org # OS detectionsudo nmap -A scanme.nmap.org # aggressive: -sV -O + default scripts + tracerouteTiming
Section titled “Timing”-T0 (paranoid) to -T5 (insane) trade speed for stealth and network friendliness. -T3 is the
default; -T4 is a common, faster choice on reliable networks. Slower templates are gentler on the
target.
nmap -T4 -sV scanme.nmap.orgOutput formats
Section titled “Output formats”-oN (normal), -oX (XML), -oG (greppable), and -oA writes all formats at once with a shared
basename — the practical default so you always have machine-readable output to grep or import later.
nmap -sV -oA scan-results scanme.nmap.org# writes scan-results.nmap, scan-results.xml, scan-results.gnmapgrep -i open scan-results.gnmap # quick look at open portsThe NSE scripting engine
Section titled “The NSE scripting engine”The Nmap Scripting Engine runs Lua scripts for deeper checks — banner grabbing, safe enumeration,
and vulnerability detection. --script selects scripts by name or category. Prefer the safe
and default categories; intrusive and vuln scripts can be noisy or disruptive.
nmap -sV --script=default scanme.nmap.org # the default safe setnmap --script=http-title,http-headers -p 80,443 scanme.nmap.orgnmap --script=ssl-cert -p 443 scanme.nmap.org # dump the TLS cert detailsnmap --script "safe and http-*" -p 80,443 scanme.nmap.org # category + wildcardInterpreting results
Section titled “Interpreting results”A port is reported as open (a service is listening), closed (reachable host, nothing
listening), or filtered (a firewall dropped the probe, so nmap can’t tell). filtered is
informative in itself — it usually means a firewall. Combine that with -sV output to build a picture:
open 443 running a specific nginx version, 22 filtered, 8080 open running a dev server that
shouldn’t be public. From there you move to the HTTP-layer testing in
core web vulnerabilities.
Key points
Section titled “Key points”- mitmproxy sits between client and server so you can inspect, modify, and replay HTTP(S); install its CA only on devices you own and remove it after.
- Use the TUI (
mitmproxy) for live work,mitmwebfor a GUI,mitmdumpfor scripting and Python addons that automate transforms. - Mobile/app APIs become testable by proxying the device — but cert pinning may block you, and defeating it may be out of scope.
- nmap answers “what’s exposed”:
-snto find hosts,-sS/-sT/-p/--top-portsfor ports,-sV/-Ofor service and OS,-oAto save everything,--scriptfor NSE. - Prefer gentle timing and
safe/defaultNSE categories; never scan or run intrusive scripts against assets you’re not authorized to test.