Network Sniffing

Created On 01. May 2022

Updated: 2022-05-01 23:46:34.787704000 +0000

Created By: acidghost

"To understand what is going on a networked computer, the only true way to understand is to sniff the network." - Laura Chappell

Table of contents

  • libcap
  • IPv4 & IPv6
    • IP Network Addressing
  • Data-Link Layer
    • Switched and Unswitched Networks
    • ARP
    • Spoofing
    • ARP poisoning
  • Sniffer Detection
    • ARP Cop
  • Tools

Sniffing refers to the act of capturing network packets. tcpdump is a linux tool that can show up different information:

$ sudo tcpdump -l -X 
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s3, link-type EN10MB (Ethernet), capture size 262144 bytes
00:59:33.867266 IP > wavezz.39442: Flags [.], ack 361, win 65535, length 0
	0x0000:  4500 0028 1570 0000 4006 997a 42cb 7d0c  E..(.p..@..zB.}.
	0x0010:  0a00 020f 01bb 9a12 0002 9c20 dabb f073  ...............s
	0x0020:  5010 ffff e0ce 0000 0000 0000 0000       P.............
00:59:34.845890 IP wavezz.34140 > Flags [.], ack 1, win 63791, length 0
	0x0000:  4500 0028 14f9 4000 4006 660a 0a00 020f  E..(..@.@.f.....
	0x0010:  8efb 24c3 855c 0050 30da 76a7 0cc5 8abf  ..$..\.P0.v.....
	0x0020:  5010 f92f bfe7 0000                      P../....

We see 2 requests sent from Mega provider to my machine and in the second one my machine is sending to Google and we can identify this by, which is a Google owned domain. Data from telnet, FTP and POP3 is unencrypted. Such data as usernames and passwords will be visible as plaintext.


A C library that works on multiple architecture called libpcap is used in in many programs, including tcpdump. It becomes useful when writing a simple own sniffer:

#include <pcap.h>

void dump(const unsigned char *data_buffer, const unsigned int length) {
	unsigned char byte;
	unsigned int i, j;
	for(i=0; i < length; i++) {
		byte = data_buffer[i];
		printf("%02x ", data_buffer[i]);  // display byte in hex
		if(((i%16)==15) || (i==length-1)) {
			for(j=0; j < 15-(i%16); j++)
				printf("   ");
			printf("| ");
			for(j=(i-(i%16)); j <= i; j++) {  // display printable bytes from line
				byte = data_buffer[j];
				if((byte > 31) && (byte < 127)) // outside printable char range
					printf("%c", byte);
			printf("\n"); // end of the dump line (each line 16 bytes)
		} // end if
	} // end for

void pcap_fatal(const char *failed_in, const char *errbuf) {
	printf("Fatal Error in %s\n", failed_in, errbuf);

int main() {
	struct pcap_pkthdr header;
	const u_char *packet;
	char errbuf[PCAP_ERRBUF_SIZE];
	char *device;
	pcap_t *pcap_handle;
	int i;

	device = pcap_lookupdev(errbuf);
	if(device == NULL)
		pcap_fatal("pcap_lookupdev", errbuf);

	printf("Sniffing on device %s\n", device);

	pcap_handle = pcap_open_live(device, 4096, 1, 0, errbuf);
	if(pcap_handle == NULL)
		pcap_fatal("pcap_open_live", errbuf);

	for (i=0; i<3; i++) {
		packet = pcap_next(pcap_handle, &header);
		printf("Got a %d byte packet\n", header.len);
		dump(packet, header.len);

Notice the dump function will parse the raw memory into hex bytes and printable format. Here is another example of such function pcap_lookupdev gives us the first device to sniff on and pcap_open_liveopens it for packet capturing, where it takes arguments for the device to sniff, the max packet size, promiscous flag, timeout value and the error buffer. pcap_next will grab the next packet and will dump it with the included dump function. pcap_close will close the capture interface. For this to work, the pcap library has to be installed with sudo apt-get install libpcap-dev. Then compile with gcc -o pcap pcap_sniffer.c -lpcap. On error, the program might return the error function:

$ ./pcap
Sniffing on device enp0s3
Fatal Error in pcap_open_live

Here is where we shouldn't forget sudo.

$ sudo ./pcap
Sniffing on device enp0s3
Got a 54 byte packet
52 54 00 12 35 02 08 00 27 31 02 68 08 00 45 00 | RT..5...'1.h..E.
00 28 00 b2 40 00 40 06 2b f1 0a 00 02 0f 59 2c | .(..@.@.+.....Y,
a8 f2 cf 0a 01 bb bc 71 e7 2e 07 eb 6c 09 50 10 | .......q....l.P.
f5 3c 0e 48 00 00                               | .<.H..

There are many sniffers around that use libpcap, since it does an excellent job at packet capturing and can run on multiple platforms. Sniffex is another more complex example that demonstrates how this library can be used. To get a little more quick insight on how packet capturing works, you can throw a look at my previous posts, TCP and UDP clients with Python, Raw sockets in Python and Sniffing with Scapy.

IPv4 & IPv6

To understand better how network sniffing works under the hood, it becomes important to get comfortable with the existing network protocols.
IPv4 (Internet Protocol Version 4) is the standard address format that allows machines to communicate with each other over the internet. IPv6 is a newer standard that is aimed for increasing the limitation of possible IP addresses from 4 billion to 340 trillion trillion trillion with a number of modifications.

The IPv4 packet header looks as following:

  0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   |Version|  IHL  |Type of Service|          Total Length         |
   |         Identification        |Flags|      Fragment Offset    |
   |  Time to Live |    Protocol   |         Header Checksum       |
   |                       Source Address                          |
   |                    Destination Address                        |
   |                    Options                    |    Padding    |

The IPv6 packet's fixed header is:

|Version| Traffic Class |           Flow Label                  |
|         Payload Length        |  Next Header  |   Hop Limit   |
|                                                               |
+                                                               +
|                                                               |
+                         Source Address                        +
|                                                               |
+                                                               +
|                                                               |
|                                                               |
+                                                               +
|                                                               |
+                      Destination Address                      +
|                                                               |
+                                                               +
|                                                               |

The IPv6 has only the most relevant information in its fixed header. Other information that might be needed will be part of Extension Headers. See
See more on the difference between IPv4 and IPv6 here

IP Network Addressing

An IPv4 address has 4 bytes divided by dots. A byte is equal to 8 bit and a number of an IPv4 address can be 256 maximum - starting with 0. Every IP Network node needs a netmask. The netmask defines how many bits of an IP address are reserved for the net and how many for the host, and it defines the size of the net - is the most common one. will have 1's in the first 24 bits in binary which will be 11111111.11111111.11111111.00000000. That is similar as /24 for the CIDR block.

Data-Link Layer

After hardware connections, the lowest level (according to the OSI model) is the data-link layer. This layer exists only on local networks, where every Ethernet device is assigned a unique hardware address (MAC). The Ethernet header contains the source and destination addresses of an Ethernet packet. An Ethernet header has a value that describes what kind of type is the packet. The types can be as following:

/* Ethernet frame types */
 ETHER_TYPE_IPv4 0x0800 /**< IPv4 Protocol. */
 ETHER_TYPE_IPv6 0x86DD /**< IPv6 Protocol. */
 ETHER_TYPE_ARP  0x0806 /**< Arp Protocol. */
 ETHER_TYPE_RARP 0x8035 /**< Reverse Arp Protocol. */
 ETHER_TYPE_VLAN 0x8100 /**< IEEE 802.1Q VLAN tagging. */
 ETHER_TYPE_1588 0x88F7 /**< IEEE 802.1AS 1588 Precise Time Protocol. */
 ETHER_TYPE_SLOW 0x8809 /**< Slow protocols (LACP and Marker). */
 ETHER_TYPE_TEB  0x6558 /**< Transparent Ethernet Bridging. */

The type of the Ethernet frame will be contained in the packet in the corresponding Ethernet frame type together with source and destination address. Also see

Switched and Unswitched Networks

In an unswitched network the Ethernet packet will pass through ever device on the network and will expect each device to look at the packets destined for its destination address. It is important to set a device to promiscous mode, which will instruct the kernel to read every network packet. Most programs including tcpdump will set promiscous mode by default and it can be enabled the following way:

$ ifconfig
 enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
$ sudo ifconfig  enp0s3 promisc
$ ifconfig  enp0s3
enp0s3: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST>  mtu 1500

In a switched network the packets will be sent only to the port they are destined for. The advantage is that devices are only sent packets that are meant for them and promiscous devices won't sniff any additional packets. However, even in a switched network environment there are ways to sniff other devices' packets, they just tend to be a bit more complex.


The Address Resolution Protocol (ARP) helps to associate IP addresses with a piece of hardware. It lives between Ethernet and IP layer and resolves MAC addresses to IP addresses. When a machine needs to know of another, it sends an ARP request to every host on the local subnet. It will be sent to a broadcast address, which is part of the Ethernet packet. The broadcast address has the IP and MAC addresses that are compared against the ARP request to find out the machine on the local network. The ARP cache stores the entries about the associated IP addresses to corresponding MAC of the machine. RFC 826, An Ethernet Address Resolution Protocol -- or -- Converting Network Protocol Addresses to 48.bit Ethernet Address for Transmission on Ethernet Hardware explains how ARP works in more detail. However note that the broadcast address exists only in IPv4 protocol. In IPv6 ARP has been replaced by NDP - Neighbor Discovery Protocol. You can start reading on reasons for this here


When the source address in a network packet is forged, a machine can acknowledge it, since most systems will expect it to be valid. An attacker usually will be masqueraded under a trusted identity, such as a website we know. This act is known as Spoofing. There are more types of spoofing. We will look into ARP poisoning and you can read more about another type of spoofing known as DNS Poisoning here and IP-Spoofing here

ARP poisoning

When ARP reply comes with an IP address that exists in the ARP cache, the system will overwrite the prior MAC address information with with the new one in the reply - unless the ARP entry isn't permanent. No state entry is kept about the ARP traffic and most systems will accept an ARP reply even if they did not send an ARP request.
Spoofing the ARP cache is also known as ARP cache poisoning. An attacker sends spoofed ARP replies to overwrite the ARP cache with attacker's data. This technique allows to sniff packets in a switched network environment.

An attacker would need to poison the ARP cache of a victim machine to make it believe that it's destination address is at attacker's MAC address. Then the attacker's machine simply has to forward these packets at their final destinations. Since a victim's machine wraps its own Ethernet headers on its packets based on its ARP cache, victim's traffic will be sent to attacker's MAC address. The switch filters only traffic based on the MAC address, so this will work fine in a switched network environment. Then the attacker's machine rewraps the IP packets with their proper Ethernet headers and sends them back to the switch, where they will be routed to their final destination.

One interesting moment is when trying to sniff the traffic from the default gateway. A gateway is a system that routes all the traffic from the local network to the internet. If the victim's machine is communicating with the gateway over a switch, the traffic will be restricted by MAC address. This traffic cannot be normally sniffed, even in promiscuous mode. In such situation the traffic has to be redirected. To redirect the traffic, first victim's and gateway machines' MAC addresses have to be determined. This can be done by simply pinging the hosts and this will write both MAC addresses in the attacker's ARP cache. The entries can be checked with arp -na. For this finally to work, the attacker's machine has to send spoofed ARP replies at regular intervals, where the gateway and victim machines will be told that their MAC addresses have the value of the attacker's. Spoofing and injection works with Nemesis, which is a tool directed towards crafting and packet injection. Let's see how this would work.

  • - Attacker's IP Address; 00:00:00:00:00:AA - Attacker's Mac Address
  • - Victim's IP Address; 00:00:00:00:00:BB - Victim's Mac Address
  • - Gateway's IP Address; 00:00:00:00:00:CC - Gateway's Mac Address

Below is an example how to inject an ARP packet, where the ARP reply from to will be spoofed and claim that gateway's MAC address is at the attacker's MAC address of 00:00:00:00:00:AA:

$ sudo nemesis arp -v -r -d eth0 -S -D -h 00:00:00:00:00:AA -m 00:00:00:00:00:CC -H 00:00:00:00:00:AA -M 00:00:00:00:00:CC

The same can be done for the victim's machine:

$ sudo nemesis arp -v -r -d eth0 -S -D -h 00:00:00:00:00:AA -m 00:00:00:00:00:BB -H 00:00:00:00:00:AA -M 00:00:00:00:00:BB

Sniffer Detection

First of all it is important to check if any network interface on the local network is set to promisc mode - ifconfig -a | grep PROMISC. As I've set my device above to promisc mode, it will show me that network interface. The kernel logs if a network interface is set to promisc mode. On some systems, this information can be found in /var/log/messages. Otherwise it depends on the syslog configuration of the following system.
There are few ways that can be applied when trying to detect a sniffer remotely. One way is to overflow the network with high traffic and ping all connected hosts. The hosts running a sniffer would respond slower due to higher resource usage that is required for decoding the traffic. However, this wastes a lot of resources and is not very reliable, since systems can have a high load to many other different reasons.
Another method is based on the idea that a remote sniffer on a system that is running in promisc mode, won't reject any packet and react to all. Here the attacker creates an ARP packet with a random MAC address and sends it to every host. A system that is not running in promisc mode will discard the packets that are not addressed for their MAC, but sniffing will give back a response. This technique is described more in detail in this paper It also persists as a Scapy function and can be implemented the following way:

import sys
from scapy.all import promiscping

if len(sys.argv) < 2:
    print sys.argv[0] + ""



Here you can find a nice demonstration how to set up an ARP cache listener, that will light up an Arduino led when the address in the ARP cache entry gets changed. As a trigger, it uses Ettercap's plugin "arp_cop". When an ARP entry gets written, it prints the string "(WARNING)". This can be monitored from Ettercap and the led will light up when it will receive such notification.
I rewrote few parts for it to work with python3. The python script is used to launch Ettercap with the arpcop plugin to monitor the traffic and upon discovering something sketchy it will start printing the warning in the console. This will be written to the serial device which in this case is an Arduino and it will react on the signal. For more details, see the video how such listener would work, however I observed that it is not always reliable and can sometimes give a false positive. The python script would be launched in this way, where the network interface comes as an argument followed by the path where the serial device is located:

sudo python3 en0 /dev/your_arduino


These are few tools that anyone can try out to get started with sniffing and network packet analysis.

  • tcpdump

As covered previously, this is a powerful command-line sniffer primarily aimed for system administration tasks, but with little skill anyone can use it to sniff the traffic efficiently.

  • Ettercap

A tool for initiating and analyzing Man-in-the-Middle-Attacks. It has many features and plugins that aid in sniffing, dissection of protocols and host analysis.

  • Wireshark

Likely the tool which should be anyone's first choice when we are dealing with sniffing and analyzing network packets.

  • Burp

Designed for various application penetration testing tasks, it allows to intercept and inspect all sent requests when interacting with a web application.

  • Nemesis

A command-line network packet crafting and injection utility that runs on libnet library. It can be used to craft spoofed packets and inject them.


Hacking the Art of Exploitation - Jon Erickson
Understanding Network Hacks - Bastian Ballmann
How to code Packet Sniffer in C with Sockets on Linux

Section: Web