Network Attack Detection
For this assignment we were to modify the scavenger program to read a trace file and detect what type of attack is taking place over the network (if any).
PA3 the assignment. (project files)
pa3.c my solution
/*
* Will Mernagh Mar 2007
* PA3
* Mar 2007
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
/* based on the background info and attack files these are the tresholds
* These numbers could be made better with more data
*/
#define ALLOWED_RST 100
#define MAX_ALLOWED_IP 70
#define MAX_ALLOWED_PORTS 75
#define RESET_COUNT 500
/* linked list of ports this is needed to count the number of different ports */
typedef struct ports port_list;
struct port_list{
int number;
int count;
struct port_list *next;
};
/* a linked list of hosts - for counting different hosts by IP */
typedef struct host_info host;
struct host{
char ip_addr[20];
struct port_list *port;
int num_of_ports;
int count;
struct host *next;
};
/* an event counter. */
typedef struct tcp_flag_event_counter tcp_event_counter;
struct tcp_event_counter{
int num_of_events;
struct host *source;
struct host *dest;
int num_of_source;
int num_of_dest;
int num_dest_ports;
int num_source_ports;
int num_of_packets_less_events; /* since first event num_of_packets_less_events*/
};
/* function prototypes */
struct tcp_event_counter * check_events(struct tcphdr *tcpheader,
struct ip *ipheader, struct tcp_event_counter *);
struct tcp_event_counter * parse_input(char* path, struct tcp_event_counter *);
int main(int argc, char** argv);
void decode_tcp_flags(char *buffer, int buflen, unsigned short urg,
unsigned short ack, unsigned short psh, unsigned short rst,
unsigned short syn, unsigned short fin);
void print_usage();
struct tcp_event_counter * increment_Event(struct tcphdr *, struct ip *,
struct tcp_event_counter *);
void free_event_nodes(struct tcp_event_counter *ig_event_counter);
void print_type_of_attack(struct tcp_event_counter *ig_event_counter);
/* function implementations */
/* check_events()
*
* This function takes in a tcphdr struct and decides if an searched for event
* occured. It just looks for RST but can be easily modified for others
*/
struct tcp_event_counter *check_events(struct tcphdr *tcpheader,
struct ip *ipheader, struct tcp_event_counter *ig_event_counter)
{
char buffer[25];
char *rst_search, *syn_search, *syn_ack_search, *buf, *result;
char synstr[] = "SYN", rststr[] = "RST", synackstr[] = "SYN ACK";
decode_tcp_flags(buffer, 25, tcpheader->urg, tcpheader->ack,
tcpheader->psh, tcpheader->rst, tcpheader->syn, tcpheader->fin);
rst_search = rststr;
syn_search = synstr;
syn_ack_search = synackstr;
buf = buffer;
/* search for different types of events
* Type 1: Large num of RST + Small set of IPs + Large set of ports
* Type 2: Large num of RST + Large num of Ips + Small num of ports
* Type 3: Large number of SYN ACK with no corresponding ACK
* This type will not be checked for here because we will determin
* the info from RST
*/
result = strstr(buf,rst_search);
/* If RST increment event counter */
if(result != NULL){
ig_event_counter = increment_Event(tcpheader,ipheader, ig_event_counter);
}
return ig_event_counter;
}
/* decode_tcp_flags()
*
* This function takes in all the flags from a tcp struct and prints
* which flags are set as a string. It outputs the string to the
* buffer parameter. This buffer needs to be at least 25 chars in length.
*/
void decode_tcp_flags(char *buffer,
int buflen,
unsigned short urg,
unsigned short ack,
unsigned short psh,
unsigned short rst,
unsigned short syn,
unsigned short fin)
{
int index = 0;
bzero(buffer, buflen); // clear buffer
if (syn) {
sprintf(index + buffer, "SYN ");
index += 4;
}
if (urg) {
sprintf(buffer, "URG ");
index += 4;
}
if (ack) {
sprintf(index + buffer, "ACK ");
index += 4;
}
if (psh) {
sprintf(index + buffer, "PSH ");
index += 4;
}
if (rst) {
sprintf(index + buffer, "RST ");
index += 4;
}
if (fin) {
sprintf(index + buffer, "FIN ");
index += 4;
}
}
/* parse_input()
*
* Given the trace file name and path, opens the file and reads in the
* data line by line. Each line corresponds to one packet, so the hex
* characters in each line are converted to an array of binary values.
* The ip and tcphdr structs are applied to the data array,
*/
struct tcp_event_counter *parse_input(char* path,
struct tcp_event_counter *ig_event_counter)
{
FILE *tracefile = NULL;
char *line, *curr;
size_t nbytes = 400;
ssize_t bytes_read;
int packetnum, input2;
unsigned short shortarr[66000]; // max packet length is 2^16
int count;
struct ip *ipheader;
struct tcphdr *tcpheader;
/* open file for reading */
tracefile = fopen(path, "r");
if (tracefile == NULL)
{
printf("Error: input file %s not found\n", path);
exit(1);
}
/* allocate buffer space */
line = (char *)malloc(nbytes + 1);
/* input loop */
while (1)
{
bytes_read = getline(&line, &nbytes, tracefile);
if (bytes_read > 0)
{
count = 0;
/* only parse valid data */
char ok_chars[] = "0123456789 abcdef ABCDEF";
/* minus 2 for end of file and end of line */
if ((strspn(line, ok_chars)) < strlen(line)-2){
printf("Error: Invalid file. ");
printf("The file should only contain hex and white spaces\n");
exit(1);
}
/* get rid of line number */
curr = strtok(line, " ");
sscanf(curr, "%d", &packetnum);
while ((curr = strtok(NULL, " ")) != NULL) {
/* translate line into array of 16-bit values */
sscanf(curr, "%x", &input2);
/* put input into network byte order. we do this so we can
* use the helper functions defined in the netinet
* package. */
shortarr[count] = htons((unsigned short)input2);
count++;
}
/* process short array */
ipheader = (struct ip *)shortarr;
tcpheader = (struct tcphdr *)(shortarr + (ipheader->ip_hl * 2));
/* We only want TCP packets */
if (ipheader->ip_p == IPPROTO_TCP) {
/* check for RST events */
ig_event_counter = check_events(tcpheader, ipheader,
ig_event_counter);
/* after first RST count all packets */
if(ig_event_counter != NULL){
ig_event_counter->num_of_packets_less_events++;
}
if(ig_event_counter != NULL){
/* if RST went over limit */
if((ig_event_counter->num_of_events) > ALLOWED_RST){
/* print type of attack (if one) */
print_type_of_attack(ig_event_counter);
/* clean up */
fclose(tracefile);
free(line);
return ig_event_counter;
/* if enough good events then clear RST counter */
}else if(ig_event_counter->num_of_packets_less_events > RESET_COUNT){
free_event_nodes(ig_event_counter);
ig_event_counter = NULL;
}
}
}
} else {
break;
}
}
/* clean up */
fclose(tracefile);
free(line);
/* if no detected attack free counter */
free_event_nodes(ig_event_counter);
return ig_event_counter;
}
/* print_usage()
*
* Prints usage information for the program to stdout
*/
void print_usage()
{
printf("Usage: attackalarm FILE\n\n");
}
/* main()
*
* Checks arguments, sets options, and does the work.
*/
int main(int argc, char** argv)
{
char *infilename = NULL;
int i;
struct tcp_event_counter *ig_event_counter;
ig_event_counter = NULL;
/* check for at least 1 command-line option */
if (argc < 2)
{
printf("Error: required parameter missing\n");
print_usage();
exit(1);
}
// assume this is the input filename
if (!infilename) {
infilename = argv[1];
} else {
printf("Error: unexpected argument: %s\n", argv[i]);
print_usage();
exit(1);
}
if (!infilename)
{
printf("Error: required parameter missing\n");
print_usage();
exit(1);
}
/* parse input and count events */
ig_event_counter = parse_input(infilename, ig_event_counter);
if(ig_event_counter == NULL){
printf("No scan detected\n");
}else{
/* free the event counter */
free_event_nodes(ig_event_counter);
}
return 0;
}
/* If we get a EVENT this fun gets called and increments the relavant info
*
*
*/
struct tcp_event_counter *increment_Event(struct tcphdr *tcpheader,
struct ip *ipheader,
struct tcp_event_counter *event_counter){
struct host *stored_host;
struct port_list *port;
int type; /* 0 = source, 1 = dest */
/* initialize rstcounter */
if(event_counter == NULL){
event_counter = (struct tcp_event_counter *)malloc
(sizeof(struct tcp_event_counter));
if(event_counter == NULL){
printf("Error: malloc\n");
exit(1);
}
event_counter->num_of_source = 1;
event_counter->num_of_dest = 1;
event_counter->num_of_events = 1;
event_counter->source = (struct host *)malloc(sizeof(struct host));
if(event_counter->source == NULL){
printf("Error: malloc\n");
exit(1);
}
event_counter->source->num_of_ports = 1;
event_counter->source->count = 1;
event_counter->source->port = (struct port_list *)malloc
(sizeof(struct port_list));
if(event_counter->source->port == NULL){
printf("Error: malloc\n");
exit(1);
}
strncpy(event_counter->source->ip_addr, inet_ntoa(ipheader->ip_src),
strlen(inet_ntoa(ipheader->ip_src)));
event_counter->source->port->number = (int)ntohs(tcpheader->source);
event_counter->source->port->count = 1;
event_counter->source->port->next = NULL;
event_counter->num_source_ports = 1;
event_counter->dest = (struct host *)malloc(sizeof(struct host));
if(event_counter->dest == NULL){
printf("Error: malloc\n");
exit(1);
}
event_counter->dest->num_of_ports = 1;
event_counter->dest->count = 1;
event_counter->dest->port = (struct port_list *)malloc(sizeof
(struct port_list));
if(event_counter->dest->port == NULL){
printf("Error: malloc\n");
exit(1);
}
strncpy(event_counter->dest->ip_addr, inet_ntoa(ipheader->ip_dst),
strlen(inet_ntoa(ipheader->ip_dst)));
event_counter->dest->port->number = (int)ntohs(tcpheader->dest);
event_counter->dest->port->next = NULL;
event_counter->dest->port->count = 1;
event_counter->num_dest_ports = 1;
event_counter->num_of_packets_less_events = 0;
}else{
event_counter->num_of_events++;
char *current_ip;
int current_port;
for(type = 0; type < 2; type++){
if(type == 0){
stored_host = event_counter->source;
current_ip = inet_ntoa(ipheader->ip_src);
current_port = (int)ntohs(tcpheader->source);
}else{
stored_host = event_counter->dest;
current_ip = inet_ntoa(ipheader->ip_dst);
current_port = (int)ntohs(tcpheader->dest);
}
/* check if we have source and increment it if we do */
//printf("Is %s == \n", current_ip);
while(stored_host != NULL){
/* check against current */
if(strncmp(current_ip, stored_host->ip_addr, strlen(
stored_host->ip_addr)) == 0){
stored_host->count++;
port = stored_host->port;
/* if we have source check if we have port */
while(port != NULL){
if(port->number == current_port){
port->count++;
break;
}else if(port->next == NULL){
/* keep count of ports */
if(type == 0){
event_counter->num_source_ports++;
}else{
event_counter->num_dest_ports++;
}
port->next = (struct port_list *)malloc
(sizeof(struct port_list));
port = port->next;
port->next = NULL; /* don't know why I need this */
if(port == NULL){
printf("Error: malloc\n");
exit(1);
}
port->number = current_port;
port->count = 1;
stored_host->num_of_ports++;
break;
}else{
port = port->next;
}
}
stored_host = stored_host->next;
break;
/* if new source then store it */
}else if(stored_host->next == NULL) {
if(type == 0){
event_counter->num_of_source++;
}else{
event_counter->num_of_dest++;
}
stored_host->next = (struct host *)malloc(sizeof(
struct host));
stored_host = stored_host->next;
if(stored_host == NULL){
printf("Error: malloc\n");
exit(1);
}
stored_host->num_of_ports = 1;
stored_host->count = 1;
stored_host->port = (struct port_list *)malloc(sizeof(
struct port_list));
if(stored_host->port == NULL){
printf("Error: malloc\n");
exit(1);
}
strncpy(stored_host->ip_addr, current_ip, strlen(
current_ip));
stored_host->port->number = current_port;
stored_host->port->count = 1;
stored_host->port->next = NULL;
stored_host->next = NULL; /* Don't know why */
break;
}else{
stored_host = stored_host->next;
}
}
} /* for loop */
}
return event_counter;
}
/* This needs to recurse through the linked lists and free the memory */
void free_event_nodes(struct tcp_event_counter *ig_event_counter){
struct host *source, *source_free, *dest, *dest_free;
struct port_list *port, *port_free;
if(ig_event_counter != NULL){
dest = ig_event_counter->dest;
while(dest != NULL){
port = dest->port;
while(port != NULL){
port_free = port;
port = port->next;
free(port_free);
}
dest_free = dest;
dest = dest->next;
free(dest_free);
}
source = ig_event_counter->source;
while(source != NULL){
port = source->port;
while(port != NULL){
port_free = port;
port = port->next;
free(port_free);
}
source_free = source;
source = source->next;
free(source_free);
}
free(ig_event_counter);
}
}
void print_type_of_attack(struct tcp_event_counter *ig_event_counter){
int count = 0;
struct host *source, *source_itr, *dest, *dest_itr;
source_itr = ig_event_counter->source;
source = source_itr;
while(source_itr != NULL){
/* find largest source creating event */
if(source->count < source_itr->count){
source = source_itr;
}
source_itr = source_itr->next;
count++;
}
dest_itr = ig_event_counter->dest;
dest = dest_itr;
while(dest_itr != NULL){
/* find largest source creating event */
if(dest->count < dest_itr->count){
dest = dest_itr;
}
dest_itr = dest_itr->next;
count++;
}
/* check for port scan */
if(source->num_of_ports > MAX_ALLOWED_PORTS && ig_event_counter->num_of_dest < MAX_ALLOWED_IP){
printf("Port scan detected!\n");
printf("Source host: %s\n", dest->ip_addr);
printf("Destination host: %s\n", source->ip_addr);
/* check for SYN flood to closed port */
}else if(ig_event_counter->num_of_dest > MAX_ALLOWED_IP){
printf("SYN flood detected!\n");
printf("Source host: spoofed\n");
printf("Destination host: %s\n", source->ip_addr);
/* check for SYN Flood to open port */
}else if(ig_event_counter->num_of_source > MAX_ALLOWED_IP){
printf("SYN flood detected!\n");
printf("Source host: spoofed\n");
printf("Destination host: %s\n", dest->ip_addr);
}
}