Man In The Middle Attack (MITM) Part 2 — Packet Sniffer
Building a packet sniffer using Python 🐍 that extracts visited URLs and user credentials.
This is part 2 of Man In The Middle (MITM) attack. If you haven’t read part 1 then I strongly suggest you read that before reading part 2. You can find the link to Part 1 in the next section.
What is a Packet Sniffer?
A packet sniffer is a tool that is used to monitor networks and to diagnose any network problems. It is commonly used by network technicians. Hackers often use this to spy on user’s network traffic and for extracting passwords.
Packet sniffers log network traffic on a network interface that they have access to. It can see every packet that flows to and from an interface.
You’d be wondering that we are designing a packet sniffer but the title says Man In The Middle Attack. This is because in Part 1 we wrote a Python script (ARP Spoofing) that allows us to become a Man In The Middle. The purpose of the packet sniffer is to capture the victim’s (the user which has been attacked using ARP Spoofing) network traffic and extract visited URLs and credentials.
Modules Used:
- argparse: To understand what this does read my first article here.
- Scapy: Enables the user to send, sniff and dissect and forge network packets. This capability allows the development of tools that can probe, scan, or attack networks. It can forge or decode packets of a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more. It easily handles most tasks like scanning, tracerouting, probing, unit tests, attacks, or network discovery.
- time: We only use the time module to generate a delay of 2 seconds. To learn more about this module read the docs.
What functionality the Python 🐍 script must have?
get_args()
— A function to get command-line arguments. In this case, we need an input value from the user for the interface on which we want to sniff the packets.sniffer(interface)
— A function that takes the interface value as an input and sniffs the packets on that interface.process_packet(packet)
— A function that is called by thesniffer
function for every new packet that is sniffed. This checks whether the packet is of typeHTTP Request
or not. For this use case, we only need the packets which are of theHTTP Request
type because the information we want is sent in this packet. It then usesget_url(packet)
andget_credentials(packet)
functions.get_url(packet)
— A function that extracts the URL from thepacket
that was passed to it.get_credentials(packet)
— A function that also takespacket
as input and extracts credentials such as username and password.
Without further ado, let’s start writing the script in Python.
Python Script for Packet Sniffer
In this section, we’ll build the script step by step. I’ll explain everything in each step, so read carefully.
Step 1: Importing Necessary Modules
Step 2: Writing the get_args()
function
The above function adds functionality which allows the user to pass user input values as the command-line arguments. After the addition of the above code, the user will be able to pass the name of the interface
on which the packets are supposed to be sniffed in the same instruction that is used to run the script. For example,
root@kali:~# python3 sniffer.py -i interface_name
OR
root@kali:~# python3 sniffer.py --interface interface_name
To learn how exactly this function works, read the entire Step 2 from the article on how to change MAC Address of a device.
Step 3: Writing the sniffer(interface)
function
The above gist contains the definition of the sniffer(interface)
function. It takes an interface name as an input that is used inside it. It uses a method called sniff
provided by the scapy module. The methodsniff
requires an interface name iface
as an input. The next argument, store
tells scapy whether to store the packets in memory or not. store = False
tells scapy not to store the packets. The last argument is prn
, it tells scapy which function to be called each time a new packet is sniffed. In this case, process_packet
function is called for every new packet.
Step 4: Writing the process_packet(packet)
function
Before jumping into the function, let’s take a look at how a scapy packet looks like.
###[ Ethernet ]###
dst = 52:54:00:12:35:00
src = 08:00:27:35:21:2e
type = IPv4
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 441
id = 22651
flags = DF
frag = 0
ttl = 64
proto = tcp
chksum = 0xf1f9
src = 10.0.2.9
dst = 176.28.50.165
\options \
###[ TCP ]###
sport = 48556
dport = http
seq = 790303956
ack = 327206
dataofs = 5
reserved = 0
flags = PA
window = 64240
chksum = 0xf075
urgptr = 0
options = []
###[ HTTP 1 ]###
###[ HTTP Request ]###
Method = 'GET'
Path = '/login.php'
Http_Version= 'HTTP/1.1'
A_IM = None
Accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
Accept_Charset= None
Accept_Datetime= None
Accept_Encoding= 'gzip, deflate'
Accept_Language= 'en-US,en;q=0.5'
Access_Control_Request_Headers= None
Access_Control_Request_Method= None
Authorization= None
Cache_Control= 'max-age=0'
Connection= 'keep-alive'
Content_Length= None
Content_MD5= None
Content_Type= None
Cookie = None
DNT = None
Date = None
Expect = None
Forwarded = None
From = Non
Front_End_Https= None
HTTP2_Settings= None
Host = 'testphp.vulnweb.com'
If_Match = None
If_Modified_Since= None
If_None_Match= None
If_Range = None
If_Unmodified_Since= None
Keep_Alive= None
Max_Forwards= None
Origin = None
Permanent = None
Pragma = None
Proxy_Authorization= None
Proxy_Connection= None
Range = None
Referer = 'http://testphp.vulnweb.com/login.php
The above block shows the contents of the packet. The line that starts with ###[text]###
is called a layer
. The lines after that are the fields that come under that layer
until the next layer
is encountered. For instance, from the above block, ###[ Ethernet ]###
is a layer
and the following lines are the fields under the Ethernet. These fields contain information about Ethernet. Similarly, ###[ TCP ]###
, ###[ HTTP Request ]###
, etc. are layer
. Now, let's jump to the process_packet function.
This function needs the sniffed packet as an input. In this function, we are first checking if the packet has a HTTP Request
layer. This is done by using a method provided by scapy called haslayer()
. If the packet does not has the layer then it is not processed.
If it has the HTTP Request
layer then, we perform two operations:
- Call
get_url(packet)
function to extract URL from the packet. It takes thepacket
as an input. This function returns a URL, we store this is in a variable calledurl
and then we print the value ofurl
. - Then we call another function called
get_credentials(packet)
. It also takespacket
as an input. It checks the packet for possible credentials and if found any then it returns those credentials. We store it in a variable namedcred
and then we print its value.
I’ll explain
get_url(packet)
andget_credentials(packet)
functions in the later steps.
Step 5: Writing the get_url(packet)
function
The get_url(packet)
function also needs a packet as an input to extract the URL within the packet. The function extracts the contents of theHost
and Path
subfields of the HTTPRequest
layer of the packet. We then join both the parts and return the URL extracted from the packet. The following is an example of the contents present in Host
and Path
subfields.
Path = '/login.php'
Host = 'testphp.vulnweb.com'
The code packet[http.HTTPRequest]
allows us to access the layers and is possible because of Scapy.
Step 6: Writing the get_credentials(packet)
function
In the above code, you can see a Python tuple named keywords
. Before understanding why it is required let's understand the function itself.
This function also needs a packet to work with. Now, when we looked at the contents of the packet at the beginning of step 4, there was no such layer called Raw
. This layer is crucial for what we are trying to achieve because the additional information such as credentials is added to the Raw
layer. The below code block shows the Raw
layer. It also shows that the credentials are stored inside a subfield called load
.
###[ Raw ]###
load = 'uname=hdhd&pass=hshs'
Now back to the function. At first, the function checks whether the packet has the Raw
layer. If the packet has the layer, then it extracts the value of the load
subfield and stores it in a variable called field_load.
The next step is using a for loop to loop over the tuple we discussed earlier. The tuple contains the keywords that are potential names of the form fields used at the time of making an HTML Form. These keywords are derived by guessing common names given to HTML Form elements that are used to design HTML Signup and Login forms.
The for loop loops over each element in the tuple and checks whether any of the keywords is present the field_load variable. If anyone keyword matched then the field_load is returned to the process_packet(packet)
function to display potential credentials.
Step 7: Final Step
Now, all that's left to do is call get_args()
and sniffer(interface)
function.
With this, we complete the entire script and all that's left to do is test it.
Entire Script
Working Demo
Okay, before jumping to writing the code in Python I’ll tell you about the setup that I have: I am currently on Windows 10 and I have Virtualbox running with two VMs (1. Kali Linux and 2. Windows 10). I will execute the python script that we made in Part 1 — ARP Spoofing on my Kali Linux machine and attack my Windows 10 VM to become Man In The Middle. Then, I’ll execute the packet sniffer script from this part on my Kali Linux Machine. After that, I’ll log in to a website and see whether the sniffer script is able to extract the credentials or not.
Note: The VMs are configured to use NatNetwork in Virtualbox. When the VMs are set to use NatNetwork, the VMs thinks the host as the router (access point). To know more about NatNetwork and how to configure a VM to use it, read this.
Note: The packet sniffer only works on websites using the HTTP protocol. It does not work with HTTPS. To test, use this website — Test Vuln Login.
Kali Linux Machine
- IP Address = 10.0.2.9
- MAC Address = 08:00:27:35:21:2e
Windows 10
- IP Address = 10.0.2.15
- MAC Address = 08:00:27:e6:e5:59
Access Point or Default Gateway
- IP Address = 10.0.2.1
- MAC Address = 52:54:00:12:35:00
MITM — Man In The Middle
In the above image, you can see that we have extracted the captured username and password from the packet that was sniffed. In the testing form, we entered username = test_user
and password = test_user123
and in the above image, you can see that we are getting the same username and password value in the output.
Conclusion
We successfully wrote a Python 🐍 script to capture packets. We also tested it with Man In The Middle Attack — ARP Spoofing. The script works perfectly as we wanted it to.
You can find the entire script on my Github Repository. Thank You 😃 for reading.