Skip to content

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.

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
Terminal window
# pipx keeps it isolated (recommended):
pipx install mitmproxy
# or Homebrew on macOS:
brew install mitmproxy
# Verify:
mitmproxy --version

Pointing 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:

Terminal window
# For command-line tools, environment variables are the quickest route:
export HTTP_PROXY=http://127.0.0.1:8080
export HTTPS_PROXY=http://127.0.0.1:8080
# Now this request flows through mitmproxy:
curl -s https://example.com/ >/dev/null

For 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:

Terminal window
# 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/null

Intercepting, 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 i and 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 e to edit a component (method, URL, headers, body), then a to resume/accept the (possibly edited) flow.
  • Replay — press r on 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 in curl.

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 runs headless and is the right tool for scripting and running addons. Capture to a file and replay later:

Terminal window
# 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 proxy

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.

add_header.py
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}")
Terminal window
# Load the addon (works with mitmproxy, mitmweb, or mitmdump):
mitmdump -s add_header.py

Addons 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.

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 (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.

Terminal window
# Install:
sudo apt install nmap # Debian/Ubuntu
brew install nmap # macOS
nmap --version

Before scanning ports, find which hosts are up. -sn does a ping scan (host discovery only, no port scan):

Terminal window
nmap -sn 192.0.2.0/24 # which hosts in this range respond?

-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.

Terminal window
sudo nmap -sS -p 1-1000 scanme.nmap.org # SYN scan, ports 1-1000
nmap -sT --top-ports 100 scanme.nmap.org # connect scan, 100 most common ports
nmap -p 80,443,8080,8443 scanme.nmap.org # specific ports
nmap -p- scanme.nmap.org # all 65535 ports (slow)

-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.

Terminal window
nmap -sV scanme.nmap.org # service/version detection
sudo nmap -O scanme.nmap.org # OS detection
sudo nmap -A scanme.nmap.org # aggressive: -sV -O + default scripts + traceroute

-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.

Terminal window
nmap -T4 -sV scanme.nmap.org

-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.

Terminal window
nmap -sV -oA scan-results scanme.nmap.org
# writes scan-results.nmap, scan-results.xml, scan-results.gnmap
grep -i open scan-results.gnmap # quick look at open ports

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.

Terminal window
nmap -sV --script=default scanme.nmap.org # the default safe set
nmap --script=http-title,http-headers -p 80,443 scanme.nmap.org
nmap --script=ssl-cert -p 443 scanme.nmap.org # dump the TLS cert details
nmap --script "safe and http-*" -p 80,443 scanme.nmap.org # category + wildcard

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.

  • 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, mitmweb for a GUI, mitmdump for 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”: -sn to find hosts, -sS/-sT/-p/--top-ports for ports, -sV/-O for service and OS, -oA to save everything, --script for NSE.
  • Prefer gentle timing and safe/default NSE categories; never scan or run intrusive scripts against assets you’re not authorized to test.