Gathering information to determine unusual network traffic, (Thu, Nov 21st)

When working with threat intelligence, it’s vital to collect indicators of compromise to be able to determine possible attack patterns. What could be catalogued as unusual network traffic? This is all traffic that is not being seen normally in the network, meaning that after building a frequence table all IP addresses shown less than 1% are suspicious and should be investigated.

What do we need to build a frequence table? We could use a sniffer and then process the network capture to speed things.

Another alternative is to use libpcap to gather information about the incoming protocols and the timestamp that packets were seen. For this diary, we will show a C program using libpcap that will take timestamp and the following information from protocols:

Protocol Fields (CSV file) Log file
TCP year,month,day,hour,minute,second,source IP, source port tcplog.csv
UDP year,month,day,hour,minute,second,source IP, source port udplog.csv
ICMP year,month,day,hour,minute,second,source IP, ICMP Type number icmplog.csv

How to we build the program?

  • We need to define all protocol headers: ethernet, IP, TCP, UDP and ICMP

struct ethernetheader {

        unsigned char ether_dhost[ETHER_ADDR_LEN];    /* destination host address */

        unsigned char ether_shost[ETHER_ADDR_LEN];    /* source host address */

        unsigned short ether_type;                     /* IP? ARP? RARP? etc */

};

Check the ethernet frame. Same fields.

struct ipheader {

        unsigned char  ip_vhl;                 /* version << 4 | header length >> 2 */

        unsigned char  ip_tos;                 /* type of service */

        unsigned short ip_len;                 /* total length */

        unsigned short ip_id;                  /* identification */

        unsigned short ip_off;                 /* fragment offset field */

        #define IP_RF 0x8000            /* reserved fragment flag */

        #define IP_DF 0x4000            /* dont fragment flag */

        #define IP_MF 0x2000            /* more fragments flag */

        #define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */

        unsigned char  ip_ttl;                 /* time to live */

        unsigned char  ip_p;                   /* protocol */

        unsigned short ip_sum;                 /* checksum */

        struct  in_addr ip_src,ip_dst;  /* source and dest address */

};

Check the IP header. Same fields too.

/* TCP header */

typedef unsigned int tcp_seq;

struct tcpheader {

        unsigned short th_sport;               /* source port */

        unsigned short th_dport;               /* destination port */

        tcp_seq th_seq;                 /* sequence number */

        tcp_seq th_ack;                 /* acknowledgement number */

        unsigned char  th_offx2;               /* data offset, rsvd */

#define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)

        unsigned char  th_flags;

        #define TH_FIN  0x01

        #define TH_SYN  0x02

        #define TH_RST  0x04

        #define TH_PUSH 0x08

        #define TH_ACK  0x10

        #define TH_URG  0x20

        #define TH_ECE  0x40

        #define TH_CWR  0x80

        #define TH_FLAGS        (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)

        unsigned short th_win;                 /* window */

        unsigned short th_sum;                 /* checksum */

        unsigned short th_urp;                 /* urgent pointer */

};

Same goes for the TCP header.

struct udpheader {

unsigned short int udp_srcport;

unsigned short int udp_destport;

        unsigned short int udp_len;

        unsigned short int udp_chksum;

};

Same goes for UDP header.

struct icmpheader {

u_int8_t type; /* message type */

u_int8_t code; /* type sub-code */

u_int16_t checksum;

union {

struct {

      u_int16_t id;

      u_int16_t sequence;

        } echo; /* echo datagram */

        u_int32_t gateway; /* gateway address */

struct {

      u_int16_t __unused;

      u_int16_t mtu;

        } frag; /* path mtu discovery */

        } un;

};

Same goes for ICMP header.

  • We find the capture device:

dev = pcap_lookupdev(errbuf);

if (dev == NULL) {

fprintf(stderr, “Couldn’t find capture device: %s\n”,errbuf);

exit(EXIT_FAILURE);

}

  • We get the netmask for the capture device:

if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {

fprintf(stderr, “Couldn’t get netmask for device %s: %s\n”, dev, errbuf);

net = 0;

mask = 0;

}

  • We open the capture device:

handle = pcap_open_live(dev, SNAP_LEN, 1, 1000, errbuf);

if (handle == NULL) {

fprintf(stderr, “Couldn’t open device %s: %s\n”, dev, errbuf);

exit(EXIT_FAILURE);

}

  • We make sure we are capturing on an ethernet device:

if (pcap_datalink(handle) != DLT_EN10MB) {

fprintf(stderr, “%s is not an Ethernet\n”, dev);

exit(EXIT_FAILURE);

}

  • We implement the capture filter by compiling and installing it:

char filter_exp[] = “ip”;

if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {

fprintf(stderr, “Couldn’t parse filter %s: %s\n”,

filter_exp, pcap_geterr(handle));

exit(EXIT_FAILURE);

}

if (pcap_setfilter(handle, &fp) == -1) {

fprintf(stderr, “Couldn’t install filter %s: %s\n”, filter_exp, pcap_geterr(handle));

exit(EXIT_FAILURE);

}

  • We define the pcap_loop() function to invoke packet_process() to process packets:

pcap_loop(handle, 0, packet_process, NULL);

  • We define a switch-case code inside the packet_process() function to gather the information from packets and save it to the statistics log:

ethernet = (struct ethernetheader*)(packet);

ip = (struct ipheader*)(packet + SIZE_ETHERNET);

size_ip = IP_HL(ip)*4;

if (size_ip >= 20) {

switch(ip->ip_p) {

case IPPROTO_TCP:

tcplog=fopen(“tcplog.csv”,”a+”);

tcp = (struct tcpheader*)(packet + SIZE_ETHERNET + size_ip);

size_tcp = TH_OFF(tcp)*4;

if (size_ip >= 20){

snprintf(buffer,10240,”%i,%i,%i,%i,%i,%i,%s,%i\n”,local_time_values->tm_year+1900,local_time_values->tm_mon+1,local_time_values->tm_mday,local_time_values->tm_hour,local_time_values->tm_min,local_time_values->tm_sec,inet_ntoa(ip->ip_src),ntohs(tcp->th_sport));

fputs(buffer,tcplog);

}

fclose(tcplog);

return;

case IPPROTO_UDP:

     udplog=fopen(“udplog.csv”,”a+”);

udp = (struct udpheader*)(packet + SIZE_ETHERNET + size_ip);

snprintf(buffer,10240,”%i,%i,%i,%i,%i,%i,%s,%i\n”,local_time_values->tm_year+1900,local_time_values->tm_mon+1,local_time_values->tm_mday,local_time_values->tm_hour,local_time_values->tm_min,local_time_values->tm_sec,inet_ntoa(ip->ip_src),ntohs(udp->udp_srcport));

fputs(buffer,udplog);

fclose(udplog);

return;

case IPPROTO_ICMP:

icmplog=fopen(“icmplog.csv”,”a+”);

icmp = (struct icmpheader*)(packet + SIZE_ETHERNET + size_ip);

snprintf(buffer,10240,”%i,%i,%i,%i,%i,%i,%s,%i\n”,local_time_values->tm_year+1900,local_time_values->tm_mon+1,local_time_values->tm_mday,local_time_values->tm_hour,local_time_values->tm_min,local_time_values->tm_sec,inet_ntoa(ip->ip_src),icmp->type);

fputs(buffer,icmplog);

fclose(icmplog);

return;

default:

printf(“Protocol: unknown\n”);

return;

}

}

After executing the program, you will get the three log files:

Want to download the whole source code? Go to http://github.com/manuelsantander/packetprocess

Manuel Humberto Santander Peláez
SANS Internet Storm Center – Handler
Twitter: @manuelsantander
Web:http://manuel.santander.name
e-mail: msantand at isc dot sans dot org