Adversary-in-the-middle using Network Sniffing

diyinfosec
9 min readJan 18, 2022

A do-it-yourself guide to IP forwarding.

What is this article about?

This article is a step-by-step guide to try out an Adversary-in-the-middle (AITM) attack by sniffing network traffic. We will be trying this out using two Ubuntu hosts called Victim and Sniffer. The goal is to configure these two hosts such that the Sniffer is able to “get in the middle” and observe the Victim’s network activity.

MITRE References: Adversary in the Middle, Network Sniffing.

What are the key skills/tools used?

  1. Changing the routing table of a host using route
  2. Capturing traffic using tcpdump
  3. Modifying kernel-level parameters using sysctl
  4. Setting up Network Address Translation (NAT) using iptables

How do we go about this?

In addition to being a how-to guide, this is an outline of a thought process — how to get started, what challenges you will face along the way, and how to overcome them. Here’s an index to the rest of this article:

Step 1 — Create the Victim and Sniffer hosts

As mentioned earlier, we will be using two hosts: Victim and Sniffer. Let’s quickly create these hosts using multipass:

> multipass launch -n victim && multipass launch -n sniffer
Launched: victim
Launched: sniffer
> multipass ls
Name State IPv4 Image
sniffer Running 192.168.64.15 Ubuntu 20.04 LTS
victim Running 192.168.64.14 Ubuntu 20.04 LTS

From the above output, we can see that we have launched two Ubuntu 20.04 instances, sniffer (192.168.64.15) and victim (192.168.64.14). As an attacker, you have privileged access to both the hosts. Your end goal is to direct all outbound traffic from the victim through the sniffer host.

Side Note: The behavior of multipass networking differs based on the virtualization driver being used. This example has been tested on macOS using the hyperkit driver. More details here. You can get/set your virtualization driver using the below commands:

#- To get the current driver: 
> sudo multipass get local.driver
#- To set the current driver to hyperkit.
> sudo multipass set local.driver=hyperkit

Back to Index

Step 2— Sniffer — Enable IP Forwarding

Now let’s convert the Sniffer into a packet forwarder i.e. it should act as a conduit for unrelated packets to pass through. If you don’t set this up, the Sniffer will simply drop the traffic sent from the Victim!

#- Get a root shell into the sniffer
> multipass exec sniffer -- sudo -i
#- Edit /etc/sysctl.conf to add the following lines. This will ensure changes persist across reboots. #- Enable IPv4 forwarding
net.ipv4.ip_forward=1
#- [Optional] Disable Ipv6. Disabling IPv6 is not essential but network packets can take notorious paths and you might want to keep things simple.
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1
#- Load the config for the current session
sysctl -p

Back to Index

Step 3— Victim — Configure default route through Sniffer

Now let’s configure the Victim to send all traffic through the Sniffer host. The normal flow of traffic will be Victim → Default Gateway → Internet. We want to change it to Victim → Sniffer → Default Gateway → Internet. To achieve this we will make some changes to the Victim’s routing table:

#- Get a root shell into the Victim
> multipass exec victim -- sudo -i
#- List the routes. There are three routes defined.
root@victim:~# ip route
default via 192.168.64.1 dev enp0s2 proto dhcp src 192.168.64.14 metric 100
192.168.64.0/24 dev enp0s2 proto kernel scope link src 192.168.64.14
192.168.64.1 dev enp0s2 proto dhcp scope link src 192.168.64.14 metric 100
#- Add default route through the Sniffer host
ip route add default via 192.168.64.15 dev enp0s2
#- Delete the original three entries in the route table
ip route delete default via 192.168.64.1

ip route delete 192.168.64.0/24
ip route delete 192.168.64.1

Side Note: Changes to route tables do not persist across reboots. We don’t need it for this exercise. In case you are interested in persisting your route changes, refer to this article.

Back to Index

Step 4— Testing — Try to ping google.com from Victim

Now let’s try to ping google.com from the Victim and see if it works. As per our understanding, the ping packets should go from the Victim to the Sniffer, which will in turn forward it to google.com.

From Image 3.1 below, you can see that the ping is successful from the Victim and we get a response. In parallel, I ran tcpdump on the Sniffer just to make sure we are seeing all the packets tcpdump -n -i any 'icmp'. The output from the Sniffer is surprising (Image 3.2). The Sniffer, instead of capturing all the packets, sent an ICMP redirect to the Victim. What does this mean? The Sniffer is basically telling the Victim, “Hey, I know you want to send packets through me, but I would suggest a shorter route for you. You can send the packets directly to the Gateway (192.168.64.1) instead of through me!”.

Image 3.1: Victim is able to ping google.com through the sniffer
Image 3.2: Sniffer redirected Victim directly to the Gateway!

Now, we don’t want this behavior from the Sniffer. The Sniffer shouldn’t be suggesting better routes to the Victim. Even if there is a better route, we still want traffic from the Victim to only go through the Sniffer. How do we accomplish this? The solution is to disable ICMP redirects on the Sniffer.

Back to Index

Step 5— Sniffer — Disable ICMP Redirects

Let’s look at the Linux kernel documentation on Github to see how we can disable ICMP redirects. You will notice there is a kernel parameter called send_redirects that is set at the global (all) and interface (lo, enp0s2) levels. Let’s look at its current value on the Sniffer host:

#- View ICMP redirect settings on the sniffer
> multipass exec sniffer -- sudo sysctl -a | grep -i send_redirect
net.ipv4.conf.all.send_redirects = 1
net.ipv4.conf.default.send_redirects = 1
net.ipv4.conf.enp0s2.send_redirects = 1
net.ipv4.conf.lo.send_redirects = 1

How do we modify this value? Let’s look back into the documentation:

send_redirects for the interface will be enabled if at least one of
conf/{all,interface}/send_redirects is set to TRUE, it will be disabled otherwise

In order to disable redirects for the default interface (enp0s2) we will need to disable send_redirects at both all and interface levels:

#- Get a root shell into the sniffer
> multipass exec sniffer -- sudo -i
#- Edit /etc/sysctl.conf to add the following lines. This will ensure changes persist across reboots.#- Disable ICMP redirects on sniffer.
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.enp0s2.send_redirects = 0
#- Load the config for the current session
sysctl -p

Side Note: Once the Victim host receives an ICMP redirect suggestion it will change its route to the suggested gateway (192.168.64.1). For the next 5 minutes, this recommendation will remain valid. After 5 mins have elapsed, the Victim will again try to send packets through the Sniffer. This time our Sniffer will not suggest any redirects (because we disabled them!). So, if you don’t want to wait for 5 mins, you could just run ip route flush cache on the Victim. This will cause the victim to clear existing redirects and start sending packets to our Sniffer.

Back to Index

Step 6— Testing (again) — Try to ping google.com from Victim

If we try to ping google.com again from the Victim we’ll see that the ping is successful. And running tcpdump on the Sniffer shows me that there are no redirects (Image 5.1). However, this time we see a new issue. Only the ICMP request packets are seen by the Sniffer, the response packets are entirely missed!

Image 5.1 — Sniffer host does not show ICMP echo responses!

This issue is simple though. The flow of outgoing traffic is currently Victim → Sniffer → Gateway. However, we are not altering the Victim packets in any way. The source IP of the Victim packets is the IP address of the Victim itself (192.168.64.14). So an outbound packet passes through the Sniffer and goes out through the Gateway. When the return traffic comes back to the Gateway it, therefore, sends it directly to the original IP (Victim’s IP) instead of through the Sniffer. We don’t want this!

We want the return traffic to flow through the Sniffer before finally getting to the Victim. How can we achieve this? The answer is Network Address Translation (NAT).

Back to Index

Step 7— Sniffer — Configure SNAT

We will now configure the Sniffer to do a source-NAT (SNAT) i.e. modify the source IP of the Victim’s packet to the Sniffer’s IP address. This can be done easily using iptables . The beauty of iptables is that it automatically handles these two steps:

  1. Handling Outbound traffic from the Victim — The source IP of the packets coming from the Victim is modified to that of the Sniffer. This ensures that the return traffic is always sent back to the Sniffer. This is the SNAT operation.
  2. Handling Return traffic for the Victim — Since we have rewritten the source IP, the return traffic will always come back to the Sniffer. This traffic ultimately must be sent back to the Victim. So we need to do an un-SNAT i.e. replacing the destination IP with the IP of the Victim.

To achieve these two steps using iptables just add the below rule to the Sniffer:

sudo iptables -t nat -A POSTROUTING -s 192.168.64.14 -o enp0s2 -j SNAT --to-source 192.168.64.15

In plain English, this rule means: Append a rule to the NAT table’s POSTROUTING chain. The rule must match packets that have a source IP of 192.168.64.14 (Victim IP). Once matched, change the packet’s source IP to 192.168.64.15 (Sniffer IP). Send the modified packet out through the enp0s2 network interface.

Side Note: You could also create the rule such that traffic within the subnet need not be NAT’ed. Notice the ! -d 192.168.64.0/24 in the below rule that performs this exclusion. The inter-subnet traffic from the Victim will still go through the Sniffer, just that it will not get NAT’ed.

iptables -t nat -A POSTROUTING -s 192.168.64.14  ! -d 192.168.64.0/24 -o enp0s2 -j SNAT --to-source 192.168.64.15

Back to Index

Step 8— Testing (yet, again) — Try to ping google.com from Victim

Looking at images 7.1 and 7.2 we can see that:

  1. The ping from the Victim host is successful.
  2. We are able to capture both the ICMP request and response packets in the Sniffer.
Image 7.1 — Ping from Victim is successful
Image 7.2 — Sniffer is able to see both request and response packets

You can try other types of traffic too, e.g. curl google.com. You will see traffic always going through the Sniffer. On the Sniffer you could run any packet capturing tool like tcpdump to capture and log traffic that’s of interest to you (as an adversary). For example, if you are interested only in port 21 traffic you could do tcpdump -ni any 'tcp and port 21' -w out.pcap.

Back to Index

Step 9—Cleaning up

This is the easiest part :)

multipass delete sniffer victim && multipass purge

Back to Index

In Retrospect

It is one thing to read a technique in MITRE documentation and it is quite another to create and execute a related mini-project. This feels like a good use of my time. I learned quite a bit about Linux networking, especially how network routes worked, and how to write and debug iptables rules.

--

--