Sitemap

MITM Proxy Server from Python Program

3 min readNov 19, 2024

--

Press enter or click to view image in full size

The mitmproxy is a very robust, simple, free, open-source tool, to implement MITM proxy server. The tool has command line, web, and python library as interfaces. Here we shall see how we can use it from Python program. This can be useful if you want to create a piece of software based on the mitmproxy library.

MITM Proxy can be used to

  • Debugging, testing, privacy measurements, and penetration testing.
  • Intercept, inspect, modify and replay web traffic such as HTTP/1, HTTP/2, HTTP/3, WebSockets, or any other SSL/TLS-protected protocols.
  • You can prettify and decode a variety of message types ranging from HTML to Protobuf, intercept specific messages on-the-fly, modify them before they reach their destination, and replay them to a client or server later on.

So, let’s get started.

Step 1: Create Add-on Class

First, we have to create add-ons for our proxy server. Think of add-ons like, procedures you want to do on request/response. For example, add-on for logging, add-on for blocking request, etc.

class InterceptAddon:
def request(self, flow: http.HTTPFlow):
"""This method is called for every HTTP request."""

print(f"Intercepted request to: {flow.request.url}")

def response(self, flow: http.HTTPFlow):
"""This method is called for every HTTP response."""

print(f"Intercepted response from: {flow.request.url}")

Explanation,

  • The request method will be called for each request. You can modify the request object here, before sending to the server.
  • The response method will be called for each response. You can modify the response object here, before sending to the client.

Step 2: Create a run function

async def start_proxy():
opts = Options(listen_host="127.0.0.1", listen_port=8080)
proxy = DumpMaster(opts)
proxy.addons.add(InterceptAddon())

try:
print("Starting mitmproxy...")
await proxy.run() # Run inside the event loop
except KeyboardInterrupt:
print("Stopping mitmproxy...")
proxy.shutdown()

def main():
asyncio.run(start_proxy())


if __name__ == "__main__":
main()

Explanation

  • We want our proxy server to run at localhost on port 8080.
  • We create proxy object with DumpMaster, with port and host as options.
  • We also have added out InterceptAddon’s object in proxy.
  • Once our proxy object is set, we run it and await on it.

Step 3: Use Proxy Server

Let’s now try to run and use our proxy server. We will use curl to send the request

Get Sumeet’s stories in your inbox

Join Medium for free to get updates from this writer.

Just run the Python script. You should see the following message.

Starting mitmproxy...

Next, set the proxy env var for curl.

% export http_proxy=http://127.0.0.1:8080

The curl will use the http_proxy env var and will use it as proxy.

Now, let’s send the request. Note that it’s http and NOT https. We shall see how we can use HTTPS.

% curl http://example.com   

Curl will print the HTML response. We shall the following logs from the Python script.

Starting mitmproxy...
Intercepted request to: http://example.com/
127.0.0.1:56023: GET http://example.com/
<< 200 OK 1.2k
Intercepted response from: http://example.com/

Blocking the Request

Let’s say you want to block requests, if the request is being sent to example.com .

Change the request method in our add-on as follows.

    def response(self, flow: http.HTTPFlow):
"""This method is called for every HTTP response."""

print(f"Intercepted response from: {flow.request.url}")

if "example.com" in flow.request.url:
flow.response = http.Response.make(
418, # HTTP status code
b"This request has been blocked by mitmproxy.",
)

Restart the script.

Now, if we send the request, we the request will be blocked, and we will get 418 status, which we have set in our script.

% curl http://example.com -v                                                                                                                √
* Uses proxy env variable http_proxy == 'http://127.0.0.1:8080'
* Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080
> GET http://example.com/ HTTP/1.1
> Host: example.com
> User-Agent: curl/8.7.1
> Accept: */*
> Proxy-Connection: Keep-Alive
>
* Request completely sent off
< HTTP/1.1 418 I'm a teapot
< content-length: 43
<
* Connection #0 to host 127.0.0.1 left intact
This request has been blocked by mitmproxy.

Using proxy for HTTPS

The mitmproxy library adds CA certificates to the ~/mitmproxy directory. You can trust that CA, and the proxy will work for HTTPS connections.

ls ~/.mitmproxy
mitmproxy-ca-cert.cer mitmproxy-ca-cert.p12 mitmproxy-ca-cert.pem mitmproxy-ca.p12 mitmproxy-ca.pem mitmproxy-dhparam.pem

You have to trust mitmproxy-ca-cert.cer the certificate.

--

--

Sumeet
Sumeet

Written by Sumeet

Software Engineer, love Linux FOSS, Golang, Python.