These posts will eventually make it into the guide but I don’t have the time currently so I will do a series of short posts as and when I can.
Today we are going to look at packet checksums and how to ensure that when you are using packets within Scapy that you don’t send packets with “bad checksums”.
Wikipedia has this to say about checksums:
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. If a segment contains an odd number of header and text octets to be checksummed, the last octet is padded on the right with zeros to form a 16-bit word for checksum purposes. The pad is not transmitted as part of the segment. While computing the checksum, the checksum field itself is replaced with zeros.(sourced from http://en.wikipedia.org/wiki/Transmission_Control_Protocol#Checksum_computation)
So how does this relate to packets we create or re-use in Scapy?? Well lets start at the beginning. First off we are going to create a simple packet for testing:
>>> pkt=(IP(dst="10.1.99.2")/ICMP()/"HelloWorld")
We’ve given this packet a name of pkt so we can reference it easier later on. So lets look at it’s default values.
>>> pkt.show()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= icmp
chksum= None
src= 10.1.99.25
dst= 10.1.99.2
\options\
###[ ICMP ]###
type= echo-request
code= 0
chksum= None
id= 0x0
seq= 0x0
###[ Raw ]###
load= 'HelloWorld'
Notice how the chksum value in both the IP layer and the ICMP layer show None? That’s because the show() function doesn’t show the checksum values of a packet before it’s sent (the checksums are generated by Scapy when the packet is sent)
If you want to see the actual checksum value for that packet when it gets sent, you need to use a different Scapy function. Today we are going to use show2().
Lets have a look and see what the difference is:
>>> pkt.show2()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 38
id= 1
flags=
frag= 0L
ttl= 64
proto= icmp
chksum= 0xa0b9
src= 10.1.99.25
dst= 10.1.99.2
\options\
###[ ICMP ]###
type= echo-request
code= 0
chksum= 0xf7ff
id= 0x0
seq= 0x0
###[ Raw ]###
load= 'HelloWorld'
Now we can actually see the chksum value for each layer. Lets send the packet (pkt) to Wireshark.
>>> wireshark(pkt)
This should load Wireshark in a seperate window and display the ICMP packet, if we expand the IP and ICMP layers within Wireshark we can verify the checksums are correct against what we saw above in the show2() output.
Header checksum: 0xa0b9 [correct] (IP Layer)
Checksum: 0xf7ff [correct] (ICMP Layer)
OK so lets get to the bad checksum part of this post, we are going to save this packet to a pcap file and then reload it back into Scapy. We are doing it this way so I can show you a more realistic reason why you need to “sort” bad checksums.
>>> wrpcap("/tmp/ping.pcap",pkt)
>> t=rdpcap("/tmp/ping.pcap")
>> t.summary()
IP / ICMP 10.1.99.25 > 10.1.99.2 echo-request 0 / Raw
The first command writes the pkt to a pcap file in my /tmp directory, we then re-read it as t and display a summary to show it’s the same packet. Once we are happy with that we store the single packet as bad and show it’s values
>>> bad=t[0]
>> bad.show()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 38
id= 8
flags=
frag= 0L
ttl= 128
proto= icmp
chksum= 0x60b2
src= 10.1.99.25
dst= 10.1.99.2
\options\
###[ ICMP ]###
type= echo-request
code= 0
chksum= 0xf7ff
id= 0x0
seq= 0x0
###[ Raw ]###
load= 'HelloWorld'
Oh what’s that you say, you used show() and saw the chksum value?? Yes well spotted, bad checksum’s only seem to be an issue when you are reading packets from another source (i.e. a pcap file). If you generate new packets within Scapy the checksum is only generated when you send it, which means changing the values on our original packet (pkt) doesn’t generate a bad checksum message (before you send it).
Lets change the TTL again and then send the new packet (bad).
>>> bad.ttl=213
>> bad
>> wireshark(bad)
This is what I saw in Wireshark (and you as well I hope).
Header checksum: 0x60b2 [incorrect, should be 0x0bb2 (maybe caused by "IP checksum offload"?)
Yep seems that my checksum is all messed up, this means that the packet (bad) is not really any good for anything and you can’t send it due to the bad checksum. So how do we deal with this??
First off, lets wipe out the old bad packet and recreate it again.
>>> bad=t[0]
Then we can quickly check the chksum value currently stored:
>>> bad.show()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 38
id= 8
flags=
frag= 0L
ttl= 213
proto= icmp
chksum= 0x60b2
src= 10.1.99.25
dst= 10.1.99.2
\options\
###[ ICMP ]###
type= echo-request
code= 0
chksum= 0xf7ff
id= 0x0
seq= 0x0
###[ Raw ]###
load= 'HelloWorld'
Now before we change the TTL again, we need to delete the old chksum value so that it gets recreated again after we modify the TTL.
>>>del bad[IP].chksum
Now we change the TTL:
>>> bad[IP].ttl=222
Do a quick check to make sure that’s worked:
>>>bad.summary
Now if we look at the stored chksum value in the packet (bad) what do you notice?
>>bad.show()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 38
id= 8
flags=
frag= 0L
ttl= 222
proto= icmp
chksum= None
src= 10.1.99.25
dst= 10.1.99.2
\options\
###[ ICMP ]###
type= echo-request
code= 0
chksum= 0xf7ff
id= 0x0
seq= 0x0
###[ Raw ]###
load= 'HelloWorld'
The old stored chksum value is missing. Now if we send this to Wireshark what happens?
Header checksum: 0x02b2 [correct]
Once again the checksum is good because it gets recreated when we send the packet. We can verify the checksums match by using the show2() function again.
>>> bad.show2()
###[ IP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 38
id= 8
flags=
frag= 0L
ttl= 222
proto= icmp
chksum= 0x2b2
src= 10.1.99.25
dst= 10.1.99.2
\options\
###[ ICMP ]###
type= echo-request
code= 0
chksum= 0xf7ff
id= 0x0
seq= 0x0
###[ Raw ]###
load= 'HelloWorld'
Well I hope that all makes sense to you? If you are modifying existing packets that display a chksum value when you use the show() function, you need to delete them before sending the packets.
A general guideline would be if you modify a value in a layer that has another layer above it, delete the chksum value from both layers. Some layers checksum’s are generated from the information in the layers below them (if that makes sense). If in doubt just delete all the chksum values..
Adam