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:

  1. If you already launch the VMware program, shutdown all VMware hosts, and close the VMware program.
  2. Locate the .vmx file in the SEEDUbuntu image folder (for example, SEEDUbuntu-16.04-32bit.vmx)
  3. 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".
  4. Locate the .vmx file in the Kali Linux image folder (for example, Kali-Linux-2020.3-vmware-amd64.vmx), Repeat the step 3.
  5. Save and close the .vmx files.

Attacker Machine

  1. Launch SEEDUbuntu VM.
  2. Connect to SEEDUbuntu by using the PuTTY program.
  3. Change the working directory into ~/sniffspoof directory.
  4. Enter sudo ifconfig and you should see the output like the above message:

    seed@VM:~/sniffspoof$ ifconfig
    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

    The red text is the name of the network interface. Observation your output and write your interface name and IP address
    Network Interface Name:
    Attacker IP Address:

Victim Machine:

  1. Launch Kali Linux VM, and check its IP address:
    Victim IP Address:
  2. 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:
    1. Enter sudo apt-get install vsftpd command.
    2. Configure the FTP service by entering sudo nano /etc/vsftpd.conf command and uncommenting the following:

      local_enable=YES
      write_enable=YES

    3. Also, set the following parameters:

      chroot_list_enable=YES
      chroot_list_file=/etc/vsftpd.chroot_list
      allow_writeable_chroot=YES

    4. To secure your server, disable anonymous access if not required:

      anonymous_enable=NO

    5. 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

    6. Start service and test connections:

      sudo service vsftpd start

  3. Enter sudo service inetd startcommand to startup telnet service.

Windows Host Machine:

  1. 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

  1. 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));
        }
    
    }
  2. Modify the code ens33 at line 28 to your network interface name. (such as ether0)
  3. Compile and build the code by entering gcc -o sniff Sniffing.c -lpcap.
  4. If there are no errors, execute the code by entering sudo ./sniff command. The code will sniff all the ICMP packets on the network.
  5. In the Kali Linux, enter ping 8.8.8.8 -c 2 command to send ICMP packets.
  6. 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.

  1. 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)"

  2. 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.
  3. On the Kali Linux, enter ping 8.8.8.8 -c 2 and observe the output in the SEEDUbuntu terminal. What is your observation?
  4. On the Kali Linux, enter ping <HostIpAddress> -c 2 and observe the output in the SEEDUbuntu terminal. What is your observation?
  5. 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

  1. 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"

  2. 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");
            }
    
  3. 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.
  4. 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.)
  5. 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.

  1. 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);
  2. 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);
            }
        }
    }
  3. 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;
            }
        }
    }
  4. 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.
  5. On the Windows Command Prompt console, enter telnet <VictimIPAddress> command, and then enter your login ID and password.
  6. 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

  7. Now, enter exit to log off the telnet connection.
  8. Enter ftp <VictimIPAddress> command, and then enter your login ID and password.
  9. Observe the output screen of the SEEDUbuntu, find the captured packets which are your password, and record the packet here: