SEED Lab 02 - Writing Programs to sniff Packets
Environment Set Up
We need to set promiscuous mode on two virtual machines so that they can listen to and capture all the traffics on the network. To set the promiscuous mode on Virtual machine:
- If you already launch the VMware program, shutdown all VMware hosts, and close the VMware program.
- Locate the .vmx file in the SEEDUbuntu image folder (for example, SEEDUbuntu-16.04-32bit.vmx)
- Edit the file and locate the Ethernet section. Add the following new entry for each Ethernet you want to be in promiscuous mode.
ethernet%d.noPromisc = "FALSE"
Replace %d with the ethernet number, for example, ethernet0.noPromisc = "FALSE". - Locate the .vmx file in the Kali Linux image folder (for example, Kali-Linux-2020.3-vmware-amd64.vmx), Repeat the step 3.
- Save and close the .vmx files.
Attacker Machine
- Launch SEEDUbuntu VM.
- Connect to SEEDUbuntu by using the PuTTY program.
- Change the working directory into ~/sniffspoof directory.
- Enter sudo ifconfig and you should see the output like the above message:
seed@VM:~/sniffspoof$ ifconfig
The red text is the name of the network interface. Observation your output and write your interface name and IP address
ens33 Link encap:Ethernet HWaddr 00:11:22:33:44:55
inet addr:192.168.247.129 Bcast:192.168.247.255 Mask:255.255.255.0
inet6 addr: fe80::aaaa:bbbb:cccc:1111/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8336 errors:0 dropped:0 overruns:0 frame:0
TX packets:5382 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:999419 (999.4 KB) TX bytes:934832 (934.8 KB)
Interrupt:19 Base address:0x2000
Network Interface Name:
Attacker IP Address:
Victim Machine:
- Launch Kali Linux VM, and check its IP address:
Victim IP Address: - Enter sudo service vsftpd status command. If you got response message as Unit vsftpd.service could not be found. You have to install FTP service by following the steps:
- Enter sudo apt-get install vsftpd command.
- Configure the FTP service by entering sudo nano /etc/vsftpd.conf command and uncommenting the following:
local_enable=YES
write_enable=YES - Also, set the following parameters:
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd.chroot_list
allow_writeable_chroot=YES - To secure your server, disable anonymous access if not required:
anonymous_enable=NO
- Now create the user listed file by entering sudo nano /etc/vsftpd.chroot_list command and add the local users you want allow to connect to the FTP server.
For example:kali
- Start service and test connections:
sudo service vsftpd start
- Enter sudo service inetd startcommand to startup telnet service.
Windows Host Machine:
- Check your Windows IP address that is the same IP segment as the Virtual Machine.
Host IP Address:
2.1 Understanding How a Sniffer Works
- In the below C code, we write a sniffer function using pcap APIs. It will capture all ICMP packets using pcap. In the SEEDUbuntu, enter nano Sniffing.c command and type the following code.
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <pcap.h> #include <ctype.h> #include <netinet/in.h> #include <arpa/inet.h> // default snap length (maximum bytes per packet to capture) #define SNAP_LEN 1518 // Ethernet addresses are 6 bytes #define ETHER_ADDR_LEN 6 #define ETHER_IPV4 0x800 void got_packet(u_char *args, const struct pcap_pkthdr *hander, const u_char *packet); int main() { pcap_t *handle; // packet capture handle char errbuf[ETHER_ADDR_LEN]; // error buffer struct bpf_program fp; // compiled filter program (expression) char filter_exp[] = "ip proto \\icmp"; // filter expression bpf_u_int32 net; // IP // 1. Open live pcap session on NIC with interface name handle = pcap_open_live("ens33", SNAP_LEN, 1, 1000, errbuf); if (handle == NULL) { printf("Could not open device : %s\n", errbuf); exit(EXIT_FAILURE); } // 2. Compile filter_exp into BPF pseudo-code if (pcap_compile(handle, &fp, filter_exp, 0, net) == PCAP_ERROR) { printf("Could not parse filter %s: %s\n", filter_exp, pcap_geterr(handle)); exit(EXIT_FAILURE); } if (pcap_setfilter(handle, &fp) == PCAP_ERROR) { printf("Could not install filter %s: %s\n", filter_exp, pcap_geterr(handle)); exit(EXIT_FAILURE); } // 3. Capture packets pcap_loop(handle, -1, got_packet, NULL); pcap_close(handle); // Close the handle return 0; } // Ethernet header typedef struct sniff_ethernet { uint8_t ether_dhost[ETHER_ADDR_LEN]; // destination host address uint8_t ether_shost[ETHER_ADDR_LEN]; // source host address uint16_t ether_type; // protocol type (IP, ARP, RARP, etc) } sniff_ethernet; // IP header typedef struct sniff_ip { uint8_t ip_vhl:4, // IP header length ip_ver:4; // IP version uint8_t ip_tos; // type of service uint16_t ip_len; // total length uint16_t ip_id; // identification uint16_t ip_flag:3, // fragmentation flags ip_offset:13; // flags offset #define IP_RF 0x8000 // reserved fragment flag #define IP_DF 0x4000 // don't fragment flag #define IP_MF 0x2000 // more fragments flag #define IP_OFFMASK 0x1fff // mask for fragmenting bits uint8_t ip_ttl; // time to live uint8_t ip_protocol; // protocol type uint16_t ip_chksum; // checksum struct in_addr ip_src,ip_dst; // source and dest address } sniff_ip; #define IP_HL(ip) (((ip)->ip_vhl) & 0x0f) #define IP_V(ip) (((ip)->ip_vhl) >> 4) // TCP header typedef struct sniff_tcp { uint16_t tcp_srcport; // source port uint16_t tcp_dstport; // destination port uint32_t tcp_seq; // sequence number uint32_t tcp_ack; // acknowledgment number uint8_t tcp_offx2; // data offset, rsvd #define TCP_OFF(tcp) (((tcp)->tcp_offx2 & 0xf0) >> 4) uint8_t tcp_flags; #define TCP_FIN 0x01 #define TCP_SYN 0x02 #define TCP_RST 0x04 #define TCP_PUSH 0x08 #define TCP_ACK 0x10 #define TCP_URG 0x20 #define TCP_ECE 0x40 #define TCP_CWR 0x80 #define TCP_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR) uint16_t tcp_win; // window uint16_t tcp_chksum; // checksum uint16_t tcp_urp; // urgent pointer } sniff_tcp; void got_packet(u_char *args, const struct pcap_pkthdr *hander, const u_char *packet) { static uint32_t count = 1; // packet counter uint16_t sizeTcp; uint16_t sizeIp; uint16_t sizePayload; // Declare pointers to packet headers sniff_ethernet *eth; // The Ethernet header sniff_ip *ip; // The IP header sniff_tcp *tcp; // The TCP header uint8_t *payload; // Packet payload printf("Got a packet (%d):\n", count); count++; // Define Ethernet header eth = (sniff_ethernet *) packet; if (ntohs(eth->ether_type) == ETHER_IPV4) { ip = (sniff_ip *)(packet + sizeof(sniff_ethernet)); sizeIp = IP_HL(ip) * 4; if (sizeIp < 20) { printf(" * Invalid IP header length: %u bytes\n", sizeIp); return; } printf(" From: %s\n", inet_ntoa(ip->ip_src)); printf(" To: %s\n", inet_ntoa(ip->ip_dst)); } }
- Modify the code ens33 at line 28 to your network interface name. (such as ether0)
- Compile and build the code by entering gcc -o sniff Sniffing.c -lpcap.
- If there are no errors, execute the code by entering sudo ./sniff command. The code will sniff all the ICMP packets on the network.
- In the Kali Linux, enter ping 8.8.8.8 -c 2 command to send ICMP packets.
- Check the output in the PuTTY terminal of SEEDUbuntu, record the output message:
Write Filters
In this section, we capture all ICMP packets between two hosts.
- Modify the pcap filter of the sniffer code at line 24 as below. It will allow us to capture ICMP packets between the victim and the host machine.
char filter_exp[] = "proto \\icmp and (host <victimIpAddress> and <HostIpAddress>)"; // filter expression
Replace <victimIpAddress> and <HostIpAddress> to their IP address that you recorded at the begin of the lab, for example:"proto \\icmp and (host 192.168.247.131 and 192.168.247.1)"
- Compile and build the code by entering gcc -o sniff Sniffing.c -lpcap. If there are no errors, execute the code by entering sudo ./sniff command.
- On the Kali Linux, enter ping 8.8.8.8 -c 2 and observe the output in the SEEDUbuntu terminal. What is your observation?
- On the Kali Linux, enter ping <HostIpAddress> -c 2 and observe the output in the SEEDUbuntu terminal. What is your observation?
- On the Windows, launch a Command Prompt application, enter ping <VictimIpAddress> -n 2, and observe the output in the SEEDUbuntu terminal. What is your observation?
Capture the TCP packets that have a destination port range from to port 10 ~ 100
- Modify the pcap filter of the sniffer code at line 24 as below:
char filter_exp[] = "proto \\TCP and (host <victimIpAddress> and <HostIpAddress>) and dst portrange 20-100"; // filter expression
Replace <victimIpAddress> and <HostIpAddress> to their IP address that you recorded at the begin of the lab, for example:"proto \\TCP and (host 192.168.247.131 and 192.168.247.1) and dst portrange 20-100"
- Add the following code to the got_packet() function at line 133:
// Add following code to got_packet() function tcp = (sniff_tcp *)(packet + sizeof(sniff_ethernet) + sizeIp); sizeTcp = TCP_OFF(tcp) * 4; if ( sizeTcp < 20){ printf("Invalid TCP header length: %u bytes", sizeTcp); return; } printf(" Source Port: %d\n", ntohs(tcp->tcp_srcport)); printf(" Destination Port: %d\n", ntohs(tcp->tcp_dstport)); switch (ip->ip_protocol){ case IPPROTO_ICMP: printf(" Protocol: ICMP\n"); break; case IPPROTO_TCP: printf(" Protocol: TCP\n"); break; case IPPROTO_UDP: printf(" Protocol: UDP\n"); break; case IPPROTO_IP: printf(" Protocol: IP\n"); break; default: printf(" Protocol: Others\n"); }
- Compile and build the code by entering gcc -o sniff Sniffing.c -lpcap. If there are no errors, execute the code by entering sudo ./sniff command.
- Now, we send FTP (runs over TCP) packets to the destination machine. On the Windows Command Prompt console, enter ftp <VictimIPAddress> command. (You do not need to login to FTP service.)
- You should be able to capture all the packet sent with destination port 21. Record the first captured message here:
Sniffing Password
The objective of this section is to sniff passwords using the sniffer program. We will connect to a telnet server (running in our VM) and get the password of the user.
- Add the following function definitions into the sniff code before the main() function:
void print_hex_ascii_line(const uint8_t *payload, int len, int offset); void print_payload(const uint8_t *payload, int len);
- Add the following highlighting code into got_packet() function:
switch (ip->ip_protocol){ case IPPROTO_ICMP: printf(" Protocol: ICMP\n"); break; case IPPROTO_TCP: printf(" Protocol: TCP\n"); break; case IPPROTO_UDP: printf(" Protocol: UDP\n"); break; case IPPROTO_IP: printf(" Protocol: IP\n"); break; default: printf(" Protocol: Others\n"); } // define/compute tcp payload (segment) offset payload = (uint8_t *)(packet + sizeof(sniff_ethernet) + sizeIp + sizeTcp); // compute tcp payload (segment) size sizePayload = ntohs(ip->ip_len) - (sizeIp + sizeTcp); // Print payload data; it might be binary, so don't just treat it as a string. if (sizePayload > 0) { printf(" Payload: %d bytes\n", sizePayload); print_payload(payload, sizePayload); } } }
- Then, add the following functions at the end of the sniff code:
// print data in rows of 16 bytes: offset hex ASCII // 00000 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a GET / HTTP/1.1.. void print_hex_ascii_line(const uint8_t *payload, int len, int offset) { int i; int gap; const uint8_t *ch; // offset printf(" %05d ", offset); // hex ch = payload; for(i = 0; i < len; i++) { printf("%02x ", *ch); ch++; // print extra space after 8th byte for visual aid if (i == 7) printf(" "); } // print space to handle line less than 8 bytes if (len < 8) printf(" "); // fill hex gap with spaces if not full line if (len < 16) { gap = 16 - len; for (i = 0; i < gap; i++) printf(" "); } printf(" "); // ASCII (if printable) ch = payload; for(i = 0; i < len; i++) { if (isprint(*ch)) printf("%c", *ch); else printf("."); ch++; } printf("\n"); } // print packet payload data (avoid printing binary data) void print_payload(const uint8_t *payload, int len) { int len_rem = len; int line_width = 16; // number of bytes per line int line_len; int offset = 0; // zero-based offset counter const uint8_t *ch = payload; if (len <= 0) return; // data fits on one line if (len <= line_width) { print_hex_ascii_line(ch, len, offset); return; } // data spans multiple lines while(1) { // compute current line length line_len = line_width % len_rem; // print line print_hex_ascii_line(ch, line_len, offset); // compute total remaining len_rem = len_rem - line_len; // shift pointer to remaining bytes to print ch = ch + line_len; // add offset offset = offset + line_width; // check if we have line width chars or less if (len_rem <= line_width) { // print last line and get out print_hex_ascii_line(ch, len_rem, offset); break; } } }
- Compile and build the code by entering gcc -o sniff Sniffing.c -lpcap. If there are no errors, execute the code by entering sudo ./sniff command.
- On the Windows Command Prompt console, enter telnet <VictimIPAddress> command, and then enter your login ID and password.
- The below screenshot shows that on running the sniffer program, it sniffs all the telnet packets and we can see the password which the user types to login to the server. The password is "kali".
Got a packet: (24):
From: 192.168.247.1
To: 192.168.247.131
Source Port: 64888
Destination Port: 23
Protocol: TCP
Payload: 1 bytes
00000 6b k
Got a packet: (25):
From: 192.168.247.1
To: 192.168.247.131
Source Port: 64888
Destination Port: 23
Protocol: TCP
Payload: 1 bytes
00000 61 a
Got a packet: (26):
From: 192.168.247.1
To: 192.168.247.131
Source Port: 64888
Destination Port: 23
Protocol: TCP
Payload: 1 bytes
00000 6c l
Got a packet: (27):
From: 192.168.247.1
To: 192.168.247.131
Source Port: 64888
Destination Port: 23
Protocol: TCP
Payload: 1 bytes
00000 69 i - Now, enter exit to log off the telnet connection.
- Enter ftp <VictimIPAddress> command, and then enter your login ID and password.
- Observe the output screen of the SEEDUbuntu, find the captured packets which are your password, and record the packet here: