Man In The Middle Attack (MITM) Part 2 — Packet Sniffer
Tags: python, cybersecurity, tutorial, security
Published: 2024-02-27 01:08:19

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](https://computer.howstuffworks.com/question525.htm#pt0) 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](https://levelup.gitconnected.com/man-in-the-middle-attack-part-1-arp-spoofing-6f5b174dec59) 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.
[**Man In The Middle Attack (MITM) Part 1 — ARP Spoofing**](https://levelup.gitconnected.com/man-in-the-middle-attack-part-1-arp-spoofing-6f5b174dec59)

## Modules Used:

 1. [argparse](https://docs.python.org/3.4/library/subprocess.html): To understand what this does read my first article [here](https://medium.com/@dharmilch18/changing-mac-address-using-python-8a16fc4a3563).

 2. [Scapy](https://scapy.readthedocs.io/en/latest/extending.html): 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.

 3. [time](https://docs.python.org/3/library/time.html): 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 the sniffer function for every new packet that is sniffed. This checks whether the packet is of type HTTP Request or not. For this use case, we only need the packets which are of the HTTP Request type because the information we want is sent in this packet. It then uses get_url(packet) and get_credentials(packet) functions.

* get_url(packet) — A function that extracts the URL from the packet that was passed to it.

* get_credentials(packet) — A function that also takes packet 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**
{% gist https://gist.github.com/dharmil18/fbc7157b0921ccf0ab2f038ee794d3da.js %}
**Step 2: Writing the **get_args() **function**
{% gist https://gist.github.com/dharmil18/939e98aa5679413f07e88ac4fbf116dc.js %}
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](https://medium.com/@dharmilch18/changing-mac-address-using-python-8a16fc4a3563).

**Step 3: Writing the **sniffer(interface) **function**
{% gist https://gist.github.com/dharmil18/b6521a0e9b9fecac5d1ea0efadccaad9.js %}
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](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.
{% gist https://gist.github.com/dharmil18/a3b2868f14df08d26fee8692a4242ce4.js %}
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:

 1. Call get_url(packet) function to extract URL from the packet. It takes the packet as an input. This function returns a URL, we store this is in a variable called url and then we print the value of url.

 2. Then we call another function called get_credentials(packet). It also takes packet 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 named cred and then we print its value.
>  I’ll explain get_url(packet) and get_credentials(packet) functions in the later steps.

**Step 5: Writing the** get_url(packet) **function**
{% gist https://gist.github.com/dharmil18/ada68c6716d03eddc1f276e81f53a86b.js %}
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**
{% gist https://gist.github.com/dharmil18/0415f2cf5deaefaa74f8975762fd9e67.js %}
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.
{% gist https://gist.github.com/dharmil18/582f8a21cfe1f563154a9934194404b6.js %}
With this, we complete the entire script and all that's left to do is test it.

## Entire Script
{% gist https://gist.github.com/dharmil18/1cd165ff2c7bc7fb4c0987dbd72f6fa4.js %}
## 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 ](https://www.virtualbox.org/)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](https://www.techrepublic.com/article/how-to-create-multiple-nat-networks-in-virtualbox/).

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](http://testphp.vulnweb.com/login.php).

### 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**

![**Becoming Man In The Middle using ARP Spoofing**](https://cdn-images-1.medium.com/max/2000/1*MhXoV7VA7Q2Xu5CvbEnkvw.png)

![**Proof that we are the MITM**](https://cdn-images-1.medium.com/max/2000/1*DJ9TmYiF-82G3fu2WR4FUA.png)

MITM — Man In The Middle

![**Executing Packet Sniffer Script**](https://cdn-images-1.medium.com/max/2000/1*OyWMAQm_Q21HX-wwZfcUbQ.png)

![**Testing Form**](https://cdn-images-1.medium.com/max/2000/1*9UVcQytOQvBuZauigh8jFg.png)

![**Testing Form with credentials**](https://cdn-images-1.medium.com/max/2000/1*5UcVFqtMFSVFeAGufbW8mA.png)

![**Extracted URLs**](https://cdn-images-1.medium.com/max/2628/1*0Exhwo9fgveHL1hJG8zS-w.png)

![**Extracted Credentials**](https://cdn-images-1.medium.com/max/2702/1*ruKdfc3R8sd-6XtMbtTIvA.png)

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](https://github.com/dharmil18/Packet-Sniffer). Thank You 😃 for reading.