MITM can be pretty easy with Mitmproxy and Python
Introduction
First of all, I would like to link wiki MITM definition. If you are interested in my simple explanation, MITM is a hacker attack, in which there is a client, a server, and that man from the title in the middle. Man in the middle changes the original connection of the user and receives full access to his traffic with the ability to add or remove any stuff in requests and responses.
Mitmproxy — is your swiss-army knife for debugging, testing, privacy measurements, and penetration testing. This is about HTTP and HTPPS requests and touches on not only mobile (iOS/Android), but also OSX, Windows, and Linux apps. Especially nice that it is FREE.
In this note, I won’t go through the advantages of mitmproxy over other similar tools (Charles, scat!), but trust me, it would vanquish any tool out there almost single-handedly.
Install
brew install mitmproxy
A broad variety of UI
Mitmproxy has three interfaces for every taste:
-
Cli
mitmproxy
-
Web (I personally prefer this one)
mitmweb
-
Log dump
mitmdump
Usage
-
Connect to the same Wi-Fi on computer and device
-
Start mitmproxy on computer
-
Open Wi-Fi settings on the device and set up a proxy with laptop IP and mitmproxy port (8080 by default)
-
Open mitm.it on device browser and choose your platform
-
Then install the downloaded certificate and trust it
Now you can sniff your traffic via mitmproxy.
More information about installing the certificates
Ignore hosts
A useful option when we don’t have to sniff traffic from some hosts, but if we sniff them we can receive a lot of problems. For example, if we don’t ignore:
- android.clients.google.com, we will have problems with signing into the Play Store
- init.itunes.apple.com and itunes.apple.com, we will have problems with signing into the App Store
- ppq.apple.com, we will get this pop-up for some apps
So, let’s ignore these hosts. In order to do it we should set up a little regex at the startup of mitmproxy:
mitmweb --ignore-hosts '^(?:(?!android.clients.google.com|appldnld.apple.com|mesu.apple.com|ppq.apple.com).)*$'
What Python brings?
Python is the game-changer and the killer feature at the same time.
The main things to consider for the quick start are:
- method
request
, if we want to modify our requests and/or create new responses - method
response
, if we want to modify server responses
from mitmproxy import http
def request(flow: http.HTTPFlow) -> None:
print("here will be request modification via flow.request and its params")
def response(flow: http.HTTPFlow) -> None:
print("here will be response modification via flow.response and its params")
We’re lucky guys ‘cause using these two methods we can control the whole traffic.
Attention! The following examples are really useful but don’t claim to be exhaustive, but only describe some of the possibilities of interaction with the mitmproxy API. Something heavier, and generally anything you want you can find in the official examples.
- kill requests (e.g.: kill iOS requests for update):
from mitmproxy import http
block_hosts = [ "appldnld.apple.com", "mesu.apple.com" ]
def request(flow: http.HTTPFlow) -> None:
if any(host in flow.request.host for host in block_hosts):
flow.kill
- change query/headers of requests/responses:
from mitmproxy import http
def request(flow: http.HTTPFlow) -> None:
flow.request.query["mitm"] = "proxy"
flow.request.headers["newheader"] = "foo"
def response(flow: http.HTTPFlow) -> None:
flow.response.query["proxy"] = "mitm"
flow.response.headers["newheader"] = "bar"
- change response code (e.g.: return 503 for offline emulation):
from mitmproxy import http
def response(flow: http.HTTPFlow) -> None:
flow.response.status_code = 503
- emulate slow connection:
from mitmproxy import http
import time
def response(flow: http.HTTPFlow) -> None:
time.sleep(5.0)
- redirect requests:
from mitmproxy import http
def request(flow: http.HTTPFlow) -> None:
flow.request.host = "127.0.0.1"
flow.request.scheme = "http"
flow.request.port = 4567
- create own responses:
from mitmproxy import http
def request(flow: http.HTTPFlow) -> None:
flow.response = http.Response.make(
204,
'{"foo":"bar"}',
{"Content-Type": "application/json"}
)
Don’t take seriously
What if we want to flip all the images in the server responses?
import io
from PIL import Image
from mitmproxy import http
def response(flow: http.HTTPFlow) -> None:
if flow.response.headers.get("content-type", "").startswith("image"):
s = io.BytesIO(flow.response.content)
img = Image.open(s).rotate(180)
s2 = io.BytesIO()
img.save(s2, "png")
flow.response.content = s2.getvalue()
flow.response.headers["content-type"] = "image/png"
Conclusion
MITM attacks in real life can be a bit more complicated due to requirement of physical access to the device (hi https). The main thing that we should take from MITM is an analysis of ours, let me highlight, not another’s, but ours traffic. An interesting discovery may be the number of requests with our data flowing to other servers from our own devices and our favourite apps.
If you had any questions or clarifications after reading the note about the MITM or mitmproxy, I’ll be happy to answer them.
So, wish you happy Mondays (: