Auditing TCP stack with Scapy

Recently I have been playing with the library Scapy for Python. It allows to create any type of network packet with a few simple commands, even for non existing protocols making use of RAW packets.

Suppose we want to evaluate the behavior of the TCP stack when any combination of TCP flags is received. In order to do it, we need to send TCP packets to a given port using any combination of them.

Keep in mind that sending a packet with the SYN and ACK flags is the same as sending it with the flags ACK and SYN. Therefore it is necessary to generate any combination considering that the order does not affect the result and avoiding to send more packets than those strictly necessary.

With this idea I took a piece of paper and wrote an algorithm to solve the problem. This gave me “something” rather slow and very complex in terms of logic, taking into account that I didn’t consider the flags of congestion and traffic prioritization. The result was as follows:

#!/usr/bin/python
# -*- encoding: utf-8 -*-

from scapy.all import *
import sys
ip = str(sys.argv[1])
port = int(sys.argv[2])
flags = ['S','A','F','R','P']
send(IP(dst=ip)/TCP(dport=port, flags=""))
for i in range(len(flags)):
	send(IP(dst=ip)/TCP(dport=port, flags=str(flags[i])))
vueltag = 2
# Bloque mayor
while(len(flags) >= vueltag):
	vueltap = vueltag
	inib = 0
	while (vueltap < = len(flags)):
		bloque = ""
		aux = inib
		cont = 0
		while (cont < vueltag - 1):
			bloque = bloque + flags[aux] 
			aux = aux + 1
			cont = cont + 1
		finb = inib + cont
		while(len(flags) > finb):
                        bandera = (bloque + flags[finb])
			send(IP(dst=ip)/TCP(dport=port, flags=bandera)) 
			finb = finb + 1		
		inib = inib + 1
		vueltap = vueltap + 1
	vueltag = vueltag + 1

If you wish to run the program, you must specify server IP and post as arguments. You must also have Python and Scapy, both included in Backtrack distributions. For instance, to audit port 80 at IP 127.0.0.1 the command is as follows:

# ./programa.py 127.0.0.1 80

When the programme ran against port 80 in my local machine (127.0.0.1) and traffic was registred by tdpdump, I could see that the result was correct, as seen in the following image:

But if you take into account how TCP/IP works and that Scapy allows to introduce raw data with hex or binary values, we could abbreviate the last code with something as simple as the following:

#!/usr/bin/python
# -*- encoding: utf-8 -*- 

from scapy.all import *
import sys

ip = str(sys.argv[1])
port = int(sys.argv[2])

num = 0b000000
while(num < 0b1000000):
	send(IP(dst=ip)/TCP(dport=port, flags=num))
	num = num + 0b1

If I would like to broad the algorithm to check traffic congestion flags we should only modify the while condition:

num = 0b00000000
while(num < 0b100000000):
	send(IP(dst=ip)/TCP(dport=port, flags=num))
	num = num + 0b1

As you can see, it’d be easy to program a Python script to audit TCP/IP stacks of several devices, adding options such as:

  • To send to port “0”, port values within certain range or same source and destination port.
  • TCP version different than 4 and 6.
  • Session and acknowledgment number with identical values​​, maximum values ​​or “0”.
  • Send multiple packets with the same session value but different source IP.
  • Same IP address as source and destination or reserved IP address as source.
  • simple and overlayed fragmentation.
  • Size, field size, checksum invalid or not multiple of 32 bits.
  • Extra TCP options invalid.

As last recommendation, if you wish to program the previous options, take special care with speed and what is sent, due telephone switchboards, SCADA environments or printers often behave differently than was expected