Setting up a double NAT MitM
Recently I had the opportunity to pentest some devices with the knowledge that these devices were very locked down from a user perspective and assigned static IP addresses. As part of the assessment, I was asked to look at network traffic for plaintext sensitive data or potential man-in-the-middle (MitM) scenarios.
So here is the start of a real testing scenario you might be faced with. If you are like me and primarily do something more in the vein of web-app testing day to day the first thought that pops into your brain when you hear man-in-the-middle is setting up a basic web proxy and going to work.
But almost immediately that thought breaks down. If you have limited access to the client (and for the sake of this article let’s just say you have none) you can’t just interact with a potential server endpoint from the client, you have to let the client do its own talking.
So the client needs to talk, and it needs to receive responses, and you have to be in the middle without hindering the conversation between client and server.
To address this scenario I wound up creating a double NAT setup using two virtual machines and two USB ethernet adapters. I don’t have a strong networking background and it took me some time to work out how to do this. Unfortunately, there doesn’t seem to be a lot of need for this type of setup normally and I found trying to search for help on the topic yielded very few results. So after putting together the pieces from reading up on iptables, and what it takes to get this to work in a bi-directional way, I decided to write it up here in case anyone else finds themselves needing to do something similar.
First things first, how to find an unknown static IP address.
Before we can set up our MitM we need to find the currently assigned static IP of our real client. To start we create an initial Linux VM for the client side of the MitM (I gave it the hostname CS-MitM
), making sure to provide it with 2 network adapters. One adapter should be an internal/virtual network to connect the two VMs (I named this virtual network, mitm_net
), and the other should be a bridged network adapter configured to one of your USB network dongles.
Configuring our VMs
Quick Tip: Depending on the distro (I generally use Xubuntu for VMs) you may need to set up a temporary bridged/nat network adapter for internet access to initially install some needed tools. Wireshark/tcpdump, BurpSuite, net-tools (If you prefer the older methods of config, ifconfig, etc)
Quick Tip: I like to replicate the last 2 characters of the mac address of the physical device in the MAC address assigned to the adapter by your virtualization software so it is easy to keep track of.
Once you have your VM configured you should see two network adapters without an IP assigned, for my demo these adapters are listed as enp0s8
the external adapter connected to the client, and enp0s9
the adapter we will use for the internal mitm_net
.
Using wireshark or tcpdump we can listen on our external interface enp0s8
for ARP requests to get the current static IP of the client.
sudo tcpdump -nnti enp0s8 arp
Now that we have the static IP (10.0.2.15
) of the real client we can configure our VMs accordingly. Keep in mind you may need to do some discovery for the default gateway, but typically this is the first address in your subnet. Using VirtualBox with a nat network for this demo makes my default gateway 10.0.2.2
.
To make things easy I wrote a client-side and a server-side shell script to quickly do setups for me in the future. You can find these scripts below.
Server Side VM (SS-MitM)
- IP Forwarding enabled
- iptables rules
- FORWARD ACCEPT on both interfaces
- NAT POSTROUTING MASQUERADE on both interfaces
- Updated nameservers in resolve.conf
- Network interface 1 (enp0s3)
- Connected to existing network
- Static IP: 10.0.2.15, mirroring the IP of the real client device.
- Default route configured for 10.0.2.2
- Network interface 2 (enp0s9)
- Connected to virtual mitm_net network
- Static IP to act as a gateway for mitm_net (10.0.0.1)
- 10.0.0.0/24 route configured for 10.0.0.2 over device enp0s9
Server side setup script (mitm_network_side.sh)
#!/bin/bash
# A: Wheatley
# ----------------------------------
# Colors
# ----------------------------------
NOCOLOR='\033[0m'
RED='\033[0;31m'
GREEN='\033[0;32m'
ORANGE='\033[0;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
LIGHTGRAY='\033[0;37m'
DARKGRAY='\033[1;30m'
LIGHTRED='\033[1;31m'
LIGHTGREEN='\033[1;32m'
YELLOW='\033[1;33m'
LIGHTBLUE='\033[1;34m'
LIGHTPURPLE='\033[1;35m'
LIGHTCYAN='\033[1;36m'
WHITE='\033[1;37m'
# ----------------------------------
# VARS
# ----------------------------------
# Settings are for network/server side
# External interface
EXTIFACE='enp0s3'
# Change to the device you are impersonating
EXTIFACEIP='10.0.2.15/24'
EXTROUTESCOPE='default'
EXTROUTETARGET='10.0.2.2'
# Internal VM network, change interface if needed.
INTIFACE='enp0s9'
INTIFACEIP='10.0.0.1/24'
INTROUTESCOPE='10.0.0.0/24'
INTROUTETARGET='10.0.0.2'
# Internal DNS if needed, or something like google '8.8.8.8' if not.
NAMESERVER='8.8.8.8'
# ----------------------------------
# Do Stuff
# ----------------------------------
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" 1>&2
exit 1
fi
echo -e "\n${WHITE}Setting up kernel IP forwarding${YELLOW}"
set -x
echo 1 > /proc/sys/net/ipv4/ip_forward
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Flushing IPTABLES rules${YELLOW}"
set -x
iptables -F
iptables -t nat -F
iptables -X
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Configuring network interface${YELLOW}"
set -x
ip a add $EXTIFACEIP brd + dev $EXTIFACE
ip a add $INTIFACEIP dev $INTIFACE
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Configuring routes${YELLOW}"
set -x
ip route add $EXTROUTESCOPE via $EXTROUTETARGET
ip route add $INTROUTESCOPE via $INTROUTETARGET dev $INTIFACE
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Setting IPTABLES forwarding${YELLOW}"
set -x
iptables -A FORWARD -i $EXTIFACE -j ACCEPT
iptables -A FORWARD -i $INTIFACE -j ACCEPT
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Setting IPTABLES postrouting${YELLOW}"
set -x
iptables -t nat -A POSTROUTING -o $EXTIFACE -j MASQUERADE
iptables -t nat -A POSTROUTING -o $INTIFACE -j MASQUERADE
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Modifying resolv.conf${YELLOW}"
set -x
sed -i "s/nameserver .*/nameserver ${NAMESERVER}/g" /etc/resolv.conf
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}DONE${NOCOLOR}\n"
Client Side VM (CS-MitM)
- IP Forwarding enabled
- iptables rules
- FORWARD ACCEPT on both interfaces
- NAT POSTROUTING MASQUERADE on both interfaces
- Updated nameservers in resolve.conf
- Network interface 1 (enp0s8)
- Connected to real client
- Static IP: 10.0.2.2, mirroring the IP of the existing network gateway.
- Network interface 2 (enp0s9)
- Connected to virtual mitm_net network
- Static IP to act as a normal client on mitm_net (10.0.0.2)
Client side setup script (mitm_client_side.sh)
#!/bin/bash
# A: Wheatley
# ----------------------------------
# Colors
# ----------------------------------
NOCOLOR='\033[0m'
RED='\033[0;31m'
GREEN='\033[0;32m'
ORANGE='\033[0;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
LIGHTGRAY='\033[0;37m'
DARKGRAY='\033[1;30m'
LIGHTRED='\033[1;31m'
LIGHTGREEN='\033[1;32m'
YELLOW='\033[1;33m'
LIGHTBLUE='\033[1;34m'
LIGHTPURPLE='\033[1;35m'
LIGHTCYAN='\033[1;36m'
WHITE='\033[1;37m'
# ----------------------------------
# VARS
# ----------------------------------
# Settings are for client-side
# External interface.
EXTIFACE='enp0s8'
EXTIFACEIP='10.0.2.2/24'
# Internal VM network, change interface if needed.
INTIFACE='enp0s9'
INTIFACEIP='10.0.0.2/24'
INTROUTESCOPE='default'
INTROUTETARGET='10.0.0.1'
# Internal DNS if needed, or something like google '8.8.8.8' if not.
NAMESERVER='8.8.8.8'
# ----------------------------------
# Do Stuff
# ----------------------------------
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" 1>&2
exit 1
fi
echo -e "\n${WHITE}Setting up kernel IP forwarding${YELLOW}"
set -x
echo 1 > /proc/sys/net/ipv4/ip_forward
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Flushing IPTABLES rules${YELLOW}"
set -x
iptables -F
iptables -t nat -F
iptables -X
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Configuring network interface${YELLOW}"
set -x
ip a add $EXTIFACEIP brd + dev $EXTIFACE
ip a add $INTIFACEIP dev $INTIFACE
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Configuring routes${YELLOW}"
set -x
ip route add $INTROUTESCOPE via $INTROUTETARGET dev $INTIFACE
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Setting IPTABLES forwarding${YELLOW}"
set -x
iptables -A FORWARD -i $EXTIFACE -j ACCEPT
iptables -A FORWARD -i $INTIFACE -j ACCEPT
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Setting IPTABLES postrouting${YELLOW}"
set -x
iptables -t nat -A POSTROUTING -o $EXTIFACE -j MASQUERADE
iptables -t nat -A POSTROUTING -o $INTIFACE -j MASQUERADE
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}Modifying resolv.conf${YELLOW}"
set -x
sed -i "s/nameserver .*/nameserver ${NAMESERVER}/g" /etc/resolv.conf
{ set +x; } 2>/dev/null
echo -e "\n${WHITE}DONE${NOCOLOR}\n"
Configuring burpsuite to be an invisible proxy
With each of these scripts applied to their respective virtual machine traffic should now pass unimpeded from the real client out to the network and back.
Now if we would like to MitM portions of this traffic we simply need to identify what we are after with wireshark or tcpdump and then add an iptables prerouting rule to forward the traffic to a proxy such as burpsuite using invisible proxying. (Burp -> proxy -> proxy settings -> edit proxy listener -> request handling -> check the ‘Support invisible proxying’ box)
Quick Tip: Make sure your proxy is listening on all interfaces to save you some trouble. Burp is configured to listen to only 127.0.0.1
by default. (Burp -> proxy -> proxy settings -> edit proxy listener and select ‘all interfaces’)
Making use of it all
Forward all TCP traffic from the client source to the burp proxy
sudo iptables -A PREROUTING -t nat -i enp0s8 -p tcp -s 10.0.2.15 -j REDIRECT --to-port 8080
To disable the rule
sudo iptables -D PREROUTING -t nat -i enp0s8 -p tcp -s 10.0.2.15 -j REDIRECT --to-port 8080
And that’s it, with the double NAT routing through the VMs both ends of client-server communications believe they are talking to each other and you are free to cherry-pick data with wireshark or redirect traffic as needed.