This post is an update to my 2019 page on Running a man-in-the-middle proxy on a Raspberry Pi 3, now revisited and rewritten to accommodate using a Raspberry Pi 4, the current version of mitmproxy (v8.0.0), Raspberry Pi OS (bullseye) as well as changes to how some of the software is installed and configured.
I have repeated much of the original content, especially the overview and explanations, so you do not need to refer back to the original 2019 post.
What is a Man-In-The-Middle Proxy
An man-in-the-middle proxy is a piece of software running on a device (e.g. a Wi-Fi access point or a network router) in between a client (your phone, your laptop) and the server you intend to communicate with. The proxy is able to intercept and parse the information being sent back and forth between the client and the server. It may even manipulate the request being sent, or modify the information coming back.
A MITM proxy need not be “malicious”, although I guess this depends on your view of information privacy and the implementation of IT security controls. The majority of large corporate organizations usually employ a MITM proxy to scan and filter digital traffic moving within their internal network and an outside network (such as the internet). This way they could detect someone moving sensitive information outside of the company’s controlled environment, and also attempt to prevent malware from being installed on staff machines.
An example of a truly malicious MITM proxy would be a Wi-Fi access point that you may connect to thinking it is trustworthy. Perhaps you are in a coffee shop and see on your phone that there is an access point called “CoffeeShop FREE Wi-Fi”. It may even have a “fake” portal with the same “correct” password that was given to you by a (legitimate) barista. The problem is this access point has nothing to do with the coffee shop, it’s actually running on a device in the pocket of a nearby hacker, and now any non-HTTPS traffic that is sent between your laptop and the websites you visit are visible to the hacker, and can be recorded for later inspection. With some effort it is also possible that the hacker can be snooping on your HTTPS traffic. In a variation of this scenario the hacker may have previously compromised the CoffeeShop’s real Wi-Fi access point and could be forwarding the traffic from the access point to his machine. In either case the hacker is able to inspect the traffic between your device and the intended destination.
Components
mitmproxy
The free and open-source proxy software that I have chosen to use is called simply “mitmproxy”. It has features such as both a command-line and a web interface, as a well as a Python API which can be used to write scripts which can read and modify the requests and responses as the flow through the mitmproxy.
Raspberry Pi 4
I’m using a Raspberry Pi 4 Model B, with the intended operation to be as simple as:
- Plug in a network cable to the Raspberry Pi
- Power on the Raspberry Pi
- Observe a new Wi-Fi access point is available.
- Connect laptops/phones to this Wi-Fi network.
- Observe/modify traffic on the Raspberry Pi as data is sent between the connected devices and the internet.
I could certainly install and configure mitmproxy to run on my laptop, but it would require me to manually manage the network configuration on my laptop between demos.
Using a Raspberry Pi 4 Model B instead of version earlier than a Pi 3 has the benefit that it contains two network interfaces: a wired Ethernet interface and an on-board wireless Wi-Fi transceiver. This will allow me to use the Pi’s on-board Wi-Fi as an access point to which client devices can connect. The traffic from these connected devices to the outside internet will then travel over the wired Ethernet connection. Previous versions of the Pi did not have the on-board Wi-Fi, and so a Wi-Fi dongle would have been required to add a second network interface.
Setup
What follows is a walk-through on how to set up and configure mitmproxy on a Raspberry Pi. I am using bits and pieces of various tutorials, blog posts, forum discussions and stack overflow answers. The primary source material for the DHCP and Wi-Fi setup is this Hackaday project from Grégory Paul: Raspberry Pi MITM. The major differences between that write-up and mine is that he is using a Raspberry Pi 2 Model B using 2x Wi-Fi dongles and no wired connection, running an older version of Raspbian, and using a custom script to inject pictures of unicorns in passing web traffic (haha!). My instructions below use updated hardware and OS image, as well as on-board wired and wireless network interfaces in place of his two dongles. In place of the unicorn script I have opted to automatically run the mitmproxy web interface from which I can view the intercepted traffic in a browser on another device.
I recommend that you follow these instructions with the Pi connected to your router with an Ethernet cable, and use a directly connected monitor, keyboard and mouse to enter in the commands and view the results. You can of course set up SSH access, but as you will be messing with network settings you may find yourself unable to connect to the Pi over the network and will need to run commands locally to sort yourself out. You will need internet access available on the Ethernet cable anyway for the MITM proxy operation.
If you browse to this blog post from your Pi it will be easier to copy and paste the commands as listed below.
Raspberry Pi Image Installation and Setup
Follow the getting started guide, which includes instructions to use the official Raspberry Pi Imager to download the latest Raspberry Pi OS and flash it to a micro SD card.
You may configure some operating system parameters before flashing by clicking the settings icon. I’ve opted to set the Pi’s hostname to “mitmproxy”, enable SSH, add my current public SSH key and create a new user “mitmuser”. Note that with the latest version of Raspberry Pi OS you will be prompted to change the username from the default “pi” on first boot if you do not update it here before flashing.
The instructions below were written using Raspbian GNU/Linux 11 (bullseye).
Once the flashing process is complete, pop the SD card into your Raspberry Pi 4, connect a monitor, keyboard and mouse and power it on using the micro USB port. As you will be downloading software I recommend you connect the Pi to the internet using an Ethernet cable. We will be adjusting the wireless settings and so the wireless connection may become unusable while we configure the access point capability.
The first boot takes a minute or two and it may restart automatically as it resizes the filesystem.
Next you need to enable the WiFi by running the following command in a terminal window:
sudo raspi-config
Select “System Options”, then select “Wireless LAN” and pick the country you are using the Pi in. When it prompts for an SSID name you can tab to Cancel and exit the configuration tool. It will ask if you wish to reboot the Pi and you may do so.
If you wish to update any further configuration values such as location and language settings you can do so using the same command.
Install required software packages
Open a terminal window and run the following command to update your current packages, and install the ones required for the rest of this walk-through:
sudo apt update && sudo apt upgrade -y && sudo apt install -y iptables hostapd isc-dhcp-server python3-pip python3-venv
You may see that the isc-dhcp-server
failed to start upon completion of the installation. That’s OK, it will be resolved once you complete the next steps.
Descriptions of the items we are installing:
- iptables : allows us to configure the network rules for routing traffic between our wired and wireless interfaces, as well as passing it along to the mitmproxy software.
- isc-dhcp-server : Responsible for assigning the devices that will be connecting to the access point IP addresses.
- hostapd : Responsible for managing the authentication of devices to the access point.
- python3-pip : allows us to install packages from the Python Packaging Authority.
- python3-venv : allows to to install Python packages in a local virtual environment.
Raspberry Pi Network Configuration
To turn the Pi into a Wi-Fi access point we will be using both the wired network interface and the on-board wireless network interface. On a Raspberry Pi 4 Model B running Raspberry Pi OS/Raspbian Bullseye these will named as follows:
eth0
for the wired network interfacewlan0
for the wireless network interface
Note that in the settings below I will be specifying an address range of 192.168.42.*
for the custom wireless network. Feel free to use another one of the ranges reserved for private networks (see section 3 of RFC 1918).
Configure dhcpcd
“DHCP” stands for Dynamic Host Configuration Protocol and is responsible for managing device addresses on a network. There exists client and server DHCP software:
The DHCP client software handles how to configure a network interface once it has received a dynamically assigned address - or to force the network interface to use a fixed static address.
- You will be modifying the Pi’s existing DHCP client configuration to assign a static IP address to the
wlan0
interface, as this network interface will represent the address of the access point for all other devices that will be connecting to the custom Wi-Fi network.
- You will be modifying the Pi’s existing DHCP client configuration to assign a static IP address to the
The DHCP server software is responsible for handing out network addresses to clients who wish to join the network.
- You will need to install DHCP software on the Pi, as you will need to assign addresses to connecting clients when they want to join the custom Wi-Fi network (see below)
To modify the Pi’s current DHCP client (dhcpcd
) configuration, open up /etc/dhcpcd.conf
as root:
# I'm using nano here as it will be installed by default
# Feel free to use which ever text editor you are most comfortable with.
sudo nano /etc/dhcpcd.conf
Add the following lines at the bottom of the file:
interface wlan0
static ip_address=192.168.42.1/24
nohook wpa_supplicant
This tells the DHCP client to use a static IP address for the wireless network interface, as well as preventing the WPA supplicant hook from launching on this interface. The ever-useful Arch Wiki page for dhcpcd
has some more information on this.
Configure isc-dhcp-server
Open up the /etc/dhcp/dhcpd.conf
file:
sudo nano /etc/dhcp/dhcpd.conf
Remove the comment on the line stating authoritative;
:
# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
authoritative;
And then either just after authoritative;
or at the end of the file, add the following lines:
subnet 192.168.42.0 netmask 255.255.255.0 {
range 192.168.42.10 192.168.42.250;
option broadcast-address 192.168.42.255;
option routers 192.168.42.1;
option domain-name "local";
option domain-name-servers 8.8.8.8, 8.8.4.4;
}
These lines configure the DHCP server working on the wireless interface to hand out IP addresses beginning from 192.168.42.10
through to 192.168.42.250
, with a broadcast address of 192.168.42.255
, as well as specifying the router address to be 192.168.42.1
(note that this is the same static IP address as was configured on the wlan0
interface above).
Next you will need to open up the /etc/default/isc-dhcp-server
file:
sudo nano /etc/default/isc-dhcp-server
Change the “INTERFACESv4” line to contain wlan0
, and comment out the “INTERFACESv6”:
INTERFACESv4="wlan0"
#INTERFACESv6=""
Configure isc-dhcp-server systemd entry
After installation the isc-dhcp-server
will automatically run after boot, however if it attempts to start before the network interface is ready it will fail - and not attempt to start again. I obviously wasn’t the first one to come across this issue and found this StackOverflow answer which explains exactly what you need to do to resolve this issue. It is explained quite explicitly and so I won’t reproduce it here.
Configure hostapd
Next you will need to edit the hostapd
configuration file - but first we need to copy and extract an example configuration file within which we will make our changes:
cd /etc/hostapd/
sudo cp /usr/share/doc/hostapd/examples/hostapd.conf .
Open this file up in the text editor:
sudo nano /etc/hostapd/hostapd.conf
You will need to find the lines in the file which set the configuration variables below. In some cases you will need to modify the value and in others you will need to uncomment and modify the value:
interface=wlan0
driver=nl80211
ssid=mitmdemo
country_code=GB #change to your appropriate country code
hw_mode=g
channel=6
macaddr_acl=0
auth_algs=1
wmm_enabled=0
wpa=2
wpa_passphrase=mitmdemo2600
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
You can set your own name for the SSID and your own pass phrase - note that your passphrase needs to be between 8 and 63 characters
Next you need to open the /etc/default/hostapd
file:
sudo nano /etc/default/hostapd
And uncomment the line specifying DAEMON_CONF
and edit it such that it reads like this:
DAEMON_CONF="/etc/hostapd/hostapd.conf"
While re-running the installation of hostapd I noticed that the hostapd
service was not starting up as it was masked. Run the following commands to ensure that it is unmasked and enabled:
sudo systemctl unmask hostapd.service
sudo systemctl enable hostapd.service
mitmproxy Installation and Setup
To install mitmproxy we first need to install pipx, and then make sure that it is available on the path:
python3 -m pip install --user pipx
And then:
python3 -m pipx ensurepath
Logout or reboot your Pi at this stage. Log back in and then you can install mitmproxy:
pipx install mitmproxy
Configure mitmproxy to run on start-up
Inspiration taken from this forum post: https://discourse.mitmproxy.org/t/mitm-proxy-on-ubuntu-startup/943/2
The last bit of configuration for mitmproxy
is to set it up such that it runs at start-up. As I will be using mitmproxy
as part of a live demonstration of a man-in-the-middle proxy I will be running the bundled mitmweb application. mitmweb
runs a web app which can be viewed on a browser, from which a user can then view all the intercepted HTTP requests and responses.
To launch mitmweb
open up a text editor and save the following contents in a file at the location /home/mitmuser/start_mitmweb.sh
:
NOTE: you may have to double check the path to your mitmweb binary and update it in the script below. You can determine where your instance of mitmweb is installed by running “which mitmweb” in the terminal
#!/bin/bash
/home/mitmuser/.local/bin/mitmweb --mode transparent --web-port 9090 --web-host 0.0.0.0 &>> /var/log/mitmweb.log
Explanation for the command line arguments:
- --mode transparent : this runs mitmproxy in transparent proxy mode, which forwards the requests and responses on to their intended destinations after being inspected.
- --web-port 9090 : this tells the mitmweb application on which port to expose the web interface.
- --web-iface 0.0.0.0 : this tells the mitmweb application that the web interface should be visible and accessible on the Pi’s LAN IP address.
We will need to make this script executable by executing chmod a+x /home/mitmuser/start_mitmweb.sh
(assuming that you are using the user “mitmuser” - substitute the path for the correct on on your Pi).
Next we need to create a systemd service file…
sudo nano /etc/systemd/system/mitmweb.service
… with the following contents:
[Unit]
Description=mitmweb service
After=network.target
[Service]
Type=simple
User=root
ExecStart=/home/mitmuser/start_mitmweb.sh
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Enable this service to run automatically on start-up by running the following commands:
sudo systemctl daemon-reload
sudo systemctl enable mitmweb.service
Network traffic configuration
Modify iptables rules
To correctly intercept and forward traffic between the wireless interface and the wired interface you will need to issue the following commands to set up the “iptables” firewall rules:
sudo iptables -A FORWARD -i eth0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -t nat -A PREROUTING -i wlan0 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080
sudo iptables -t nat -A PREROUTING -i wlan0 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 8080
Next save the iptables configuration to a file:
sudo sh -c "iptables-save > /etc/iptables.up.rules"
Now we will modify a script to issue a command which will restore these rules at start-up. Open up the “rc.local” file:
sudo nano /etc/rc.local
And enter the following statement before the “exit 0” line at the end of the file:
iptables-restore < /etc/iptables.up.rules
Enable traffic forwarding
By default the operating system will not allow IP traffic to be forwarded from one network to another. To change this behavior run the following command:
sudo sysctl -w net.ipv4.ip_forward=1
And then to ensure that this setting is persisted between boot cycles, open the /etc/sysctl.conf
file:
sudo nano /etc/sysctl.conf
And find the commented line which says “#net.ipv4.ip_forward=1” and uncomment it:
...
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
Shutdown / Reboot
If you have made it this far you should be ready to shutdown or reboot the Pi - the next time it starts up it will bring up a wireless access point and run the transparent MITM proxy!
Demo
You will need a phone or a laptop to connect to the access point and another laptop to view intercepted traffic. I am using an iPhone to connect to the access point and my laptop to view the intercept traffic.
Make sure your laptop is on the same network as that where the Raspberry Pi will be plugged into via the Ethernet cable.
Observe HTTP traffic
Run an Ethernet cable from an available port on your router through to the Ethernet port on your Raspberry Pi 4.
Plug in the micro USB cable.
You should see the red (power) and green (activity) LEDs on the Pi light up. The lights in the LAN port should also soon start blinking.
Run
ping mitmproxy.local
in a terminal window from your laptop/PC. This should show you the current IP address of the Pi on your network.Open up a browser tab on your laptop and navigate to http://<ip address of RPi4>:9090/
- You should see the
mitmweb
application running in the browser.
- You should see the
Wait a few seconds, and then use your phone to search for a new Wi-Fi access point.
- You should see one called “mitmdemo”.
Connect to the “mitmdemo” access point and use the password “mitmdemo2600” to join the network.
You may see a warning that while you are connected to the access point, you have no internet connection. You may also notice a bunch of errors being displayed on your phone. This is because your phone and the apps on your phone are smart enough to see that something is trying man-in-the-middle your internet connectivity. This will be solved by installing the certificates as directed below.
Once connected, open up a web browser and navigate to http://www.example.com (make sure it is http and not https).
Observe the traffic in the
mitmweb
application running in the laptop browser tab.If you attempt to view an https website you may receive a warning in your browser.
Observe HTTPS traffic
While you are still connected to the “mitmdemo” access point, navigate in your phone’s browser to http://mitm.it
You will observe a website giving you a choice of links which will download the root certificate for the relevant platform.
Tap the icon which resembles the platform you are currently using. It will download a certificate which needs to be installed into your device’s trusted store, the instructions of which vary from platform to platform. See below some screenshots of the iOS experience.
- Note for iOS: After the certificate has been installed you will need to manually activate it by going to Settings -> About -> Certificate Trust Settings and toggling on the certificate for “mitmdemo”.
Once you have installed and trusted the certificate, you should be able to visit https websites and view the requests and responses without any warning on your device.
I recommend that you remove the certificate from your device’s trusted store once you have completed the demonstration.
Conclusion
I hope that you found this interesting and perhaps learned something from my experience in creating a portable and demo-friendly man-in-the-middle proxy! Let me know in the comments if you have any questions.