How to build TCP packets from scratch in Python 3
• updated 26 Feb 2023
One of the assignments I got at my university’s IT Security classes was to write a small Python script that would create and send an empty TCP packet with specified flags to the desired host and port combo. This could be easily achieved with scapy, but here’s the catch — we weren’t allowed to use external dependencies for this assignment.
Building the packet itself didn’t cause problems, but checksum did. I searched the entire WWW to find answers, but the only thing I found was some spaghetti code that didn’t work (at least in Python 3). So I decided to tear down scapy and create a lightweight solution to this exact issue.
Let’s get down to business!
Building the packet🔗
Let’s create a class
TCPPacket, which will hold all the needed packet fields. I will omit the options and data fields.
= = = = =
Let’s define the
build function that will take those fields and encode them into a long bytes sequence.
Here, I use the built-in module
struct. Notice how the Data Offset field is offset (no pun intended) by four bits — this is done because, according to the TCP spec, it only takes the first four bits of the byte, while the rest is reserved.
The Checksum field should be left at 0 for now, since it will be calculated later. The other constant parameters can be changed to your liking.
Calculating the checksum🔗
We start by composing a function that will calculate our checksum. The spec tells us the following:
The checksum field is the 16 bit one’s complement of the one’s complement sum of all 16 bit words in the header and text.
I don’t know about you, but I didn’t understand it even after I’ve read it for the twentieth time. So instead I referred to scapy’s source code and this is what I’ve composed:
+= b = = + += >> 16 return & 0xffff
This method makes use of Python’s built-in
array module, that creates an array with fixed element types. This lets us calculate the sum of 16-bit words more easily than using a loop. Then the function simply applies some bit arithmetic magic to the sum and returns it.
Before we can apply this method to our packet, we need to prepend it with a pseudo-header, that contains extra information, such as IP Addresses and TCP Length. Let’s head back to the
build() method and compose the pseudo-header:
Please note that TCP Length should also include the length of data sent with the packet. In our case, the data is empty, so we just use the header length.
After composing the pseudo-header, we only need to calculate the checksum and insert it back into the packet:
= = + +
Make sure that the checksum is inserted using the native byte order and not big-endian; this is why there is no exclamation point in the first argument of
In my example, I simply cut the packet in between and insert the checksum. You can also build the packet from scratch using three consecutive
The packet is finished, don’t forget to return it:
Sending the packet🔗
Now let’s make use of the class we just made and send a TCP Packet. For example, this is how we would create a Christmas Tree Packet (a packet with
To send this packet, we need to create a socket connection using the TCP protocol:
This will add the required IP header for us, so we don’t need to bother building it ourselves.
And finally, we send the built packet using the
The full code (licensed under the GNU GPL 3.0) is available as a GitHub Gist. In conclusion, I’d like to thank the developers of scapy for being my, umm, ‘inspiration’ ;)