I just returned from teaching my Scapy course at a Cyber Camp in Richmond, VA. The Cyber Camps are part of the US Cyber Challenge to educate and cultivate future cyber security talent, taught by SANS instructors. It was an amazing experience and the students were so bright, I think I learned more than I taught.
For years, I’ve praised the ease of assembling protocol layers to form packets in Scapy. At the same time, I’ve cursed the difficulties Scapy presented changing one or more values in an existing packet. Scapy recomputes any checksums (IP, UDP, TCP, ICMP) when you assemble a packet layer by layer. However, it does not recompute the checksum when you simply alter a value in an already assembled packet, resulting in a bad checksum for the particular layer(s). I’d scoured the Internet for documentation for an elegant remedy and found none, so I basically reassembled a new packet, layer by layer, keeping the values I wanted to preserve and altering the values I wanted to change. It’s a clumsy ham-handed way of doing things, but it worked.
As I lamented about this particular idiosyncrasy of Scapy while teaching, an exceptional student named Chris Cockerham asked what would happen if I just deleted the checksum attribute in the layer of the changed value. Magically, it worked and provided an elegant solution. Let me take you through the process of evolved code. I’ll start with the inelegant solution I used and progress to Chris’ solution.
All the code is entered in the Scapy interactive interface. First, the pcap file “/tmp/onepacket.pcap” is read into a list named “r”. The only packet in the list is stored in variable named “rec”. We display “rec” and see it has a current TTL of 50. Ultimately, we want to change the TTL value to 255. We attempt to do that and redisplay “rec” to find a new value of 255.
We read the packet into Wireshark and discover that the problem is that the IP checksum is now broken since Scapy did not recompute it. This means we cannot send this packet nor do anything useful with it.
Let’s look at the old solution I used. We reread the packet to get a clean version of it. Next, we extract the IP layer. Unfortunately, it’s not just the IP layer; it’s the IP layer and everything that follows.
Therefore in order to disassemble all layers, we have to extract each layer as best we can and clone it – preserving the values we want and updating those we want to change. Similarly, we extract the TCP layer, but find it has the following payload layer. We extract the final layer – the payload.
Now we begin to clone the new layers. The new IP layer is called “newIP” and we preserve the source and destination IP’s and alter the TTL value to be 255. If we wanted to preserve any other field in the IP header instead of settling for Scapy’s default value, we’d have to add its field and value to the new IP layer. Next, we rebuild the TCP layer. We save all the current fields and values in the TCP header. You can see how cumbersome this is. Finally, we build the new packet to be the new IP header followed by the new TCP header followed by the payload. We see that the packet has the desired TTL value of 255.
But what does Wireshark think about the IP checksum?
Wireshark examines the IP checksum and discovers it is correct. We accomplished what we wanted to do – change the IP TTL to 255 and cause Scapy to recompute the IP checksum. But, it was a tedious process.
The elegant solution that Chris suggested involves deleting the IP checksum attribute from the IP header with the line “del rec[IP].chksum”. We set the TTL in the IP layer of “rec” to be 255.
Let’s read the new packet into Wireshark.
Wirehshark displays our new TTL of 255 and a correct IP checksum, reflecting that Scapy recomputed the checksum after the attribute was deleted. This is so much easier than essentially cloning each of the layers, altering the desired value(s), and reassembling the packet.
To expand on this concept, you need to delete the checksum for the layer that you change. For instance, if we change the TCP window size in an assembled packet, we have to delete the TCP checksum. Be careful, though if you are changing IP addresses. Obviously, you’ll need to change the IP checksum. If the protocol that follows is UDP or TCP, both the UDP and TCP checksums have something known as a pseudo header that includes the IP addresses in the checksum. This means that you’ll need to delete the protocol checksum (UDP/TCP) attribute in addition to the IP checksum attribute.
Thanks a lot Chris. I never thought I’d learn so much teaching!