Building an SSL MITM Proxy in 100 Lines of Shell Script
Hook
One of the most powerful security testing techniques—SSL man-in-the-middle interception—can be implemented in a shell script short enough to fit on a single printed page.
Context
Understanding how SSL/TLS interception works is crucial for developers building secure applications, yet most MITM proxy implementations hide their mechanics behind thousands of lines of abstraction. Tools like mitmproxy, Burp Suite, and Charles Proxy are powerful but opaque—you configure them, click buttons, and traffic appears decrypted without understanding the underlying certificate manipulation and socket juggling that makes it possible.
The floyd-fuh/tiny-mitm-proxy project strips away all abstractions to reveal the bare minimum required for SSL interception. Built entirely with standard Unix tools—OpenSSL for certificate operations and encryption, netcat for TCP socket handling, and basic shell scripting for orchestration—this proxy demonstrates that you don't need specialized frameworks to perform MITM attacks. It's a teaching tool disguised as a functional proxy, showing exactly how an attacker (or penetration tester) can position themselves between a client and server to decrypt, inspect, and re-encrypt traffic in both directions.
Technical Insight
The architecture of tiny-mitm-proxy reveals the three-phase dance that every SSL MITM proxy must perform: terminate the client connection with a forged certificate, establish a separate connection to the real server, and shuttle plaintext between them while logging everything.
At its core, the proxy uses OpenSSL's s_server command to accept incoming SSL connections from clients. When a client connects thinking it's reaching the legitimate server, the proxy presents a self-signed certificate that masquerades as the target domain. Here's the essential server setup:
# Accept SSL connections on port 443 with a self-signed cert
openssl s_server -accept 443 \
-cert server.crt \
-key server.key \
-www \
| tee client-incoming.log \
| nc target-server.com 443 \
| tee server-response.log
This command chain does remarkable work in one line: s_server decrypts the client's SSL traffic, tee logs the plaintext to a file, nc (netcat) forwards it to the real server, and a second tee captures the server's response before it flows back to the client. The bidirectional nature requires more sophisticated handling with named pipes (FIFOs) to maintain separate read and write channels.
The certificate generation is equally revealing. The proxy must create a certificate that clients will trust, which in controlled environments means either adding the CA to the client's trust store or accepting browser warnings:
# Generate a self-signed certificate authority
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 365 -key ca.key -out ca.crt \
-subj "/CN=Tiny MITM Proxy CA"
# Generate a certificate for the target domain
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr \
-subj "/CN=target-domain.com"
openssl x509 -req -days 365 -in server.csr \
-CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
This certificate forgery is the linchpin of any MITM attack. The proxy presents a certificate claiming to represent the target domain, signed by an authority the client doesn't recognize. In a real attack scenario, the attacker would need to compromise the client's trust store or exploit a certificate validation bug. For penetration testing, you explicitly install the CA certificate in your browser.
The traffic flow demonstrates why SSL/TLS exists in the first place. Without encryption, the proxy's job would be trivial—just forward bytes. With SSL, it must decrypt, inspect, then re-encrypt. The actual implementation uses named pipes to coordinate bidirectional traffic:
# Create FIFOs for bidirectional communication
mkfifo /tmp/client-to-server
mkfifo /tmp/server-to-client
# Client-facing SSL server
openssl s_server -accept 8443 -cert server.crt -key server.key \
< /tmp/server-to-client > /tmp/client-to-server &
# Forward to real server and log both directions
cat /tmp/client-to-server | tee client-req.log | \
openssl s_client -connect real-server.com:443 -quiet | \
tee server-resp.log > /tmp/server-to-client &
This pattern reveals the proxy's split personality: it acts as a server to the client (using s_server) while simultaneously acting as a client to the real server (using s_client). The named pipes create a feedback loop where data flows through logging checkpoints in both directions.
The minimalist implementation exposes exactly what's missing in a production proxy. There's no HTTP parsing, so the proxy can't intelligently handle connection reuse, chunked encoding, or HTTP/2 multiplexing. There's no certificate validation when connecting to upstream servers—it blindly trusts whatever the target presents, making it vulnerable to recursive MITM attacks. Performance is abysmal because each connection spawns multiple subprocesses and pipes data through multiple stages of buffering. Yet this simplicity is the point: you can read every line, trace every byte, and understand exactly how SSL interception works without wading through framework code.
Gotcha
The most significant limitation isn't technical but legal and ethical. Running a MITM proxy against traffic you don't own or without explicit permission is illegal in most jurisdictions and violates computer fraud laws. Even in authorized penetration tests, the scope must explicitly include SSL interception. This tool makes MITM attacks trivially easy to understand and implement, but that accessibility doesn't change the legal landscape—use it only on networks and systems you control or have written permission to test.
On the technical side, tiny-mitm-proxy breaks with modern web traffic. HTTP/2 and HTTP/3 use multiplexed streams and binary protocols that the simple pipe-based architecture can't handle. WebSocket connections will fail because the proxy doesn't maintain proper bidirectional state. Many applications implement certificate pinning, where the client refuses any certificate not matching a hardcoded fingerprint—no amount of MITM trickery bypasses this defense. The shell-based implementation also has terrible performance characteristics: each connection spawns multiple processes, pipe buffering introduces latency, and there's no connection pooling or optimization. For actual traffic analysis or security testing, you'll hit these limitations immediately and need to reach for production-grade tools like mitmproxy.
Verdict
Use if: You're learning how SSL/TLS interception works at a fundamental level, teaching security concepts and want transparent code that demystifies MITM attacks, testing certificate validation in your own applications within a controlled lab environment, or need a quick proof-of-concept to demonstrate SSL vulnerabilities to stakeholders. Skip if: You need a reliable proxy for actual penetration testing, debugging, or traffic analysis—the performance and compatibility issues make it unsuitable for real work. Skip if you're dealing with modern protocols like HTTP/2, WebSockets, or any application with certificate pinning. Skip if you lack explicit authorization to intercept SSL traffic, as the legal risks far outweigh any technical benefits. For production security testing, use mitmproxy or Burp Suite; for learning, tiny-mitm-proxy is unmatched in clarity and simplicity.