Sorting Packet Captures with Scapy

Today I spent a little time looking into a packet capture supplied by Vivek Ramachandran at SecurityTube. This packet capture is part of a series of WiFi hacking challenges he is putting together, and immediately after opening it I got freaked out.

Normally, packet captures are sorted by their capture timestamp, though there is no requirement for that to be the case. In Vivek's challenge, the packet capture appears to be intentionally out-of-order to make analysis more difficult. You can open it up in Wireshark and sort by the timestamp column, but it makes it impossible to apply packet filters such as "frame.number < 10000" since the frame number isn't related to incrementing timestamps.

Scapy to the rescue! A little Scapy and some Python love lets us re-write the packet capture, sorting the capture by the timestamp field. After starting Scapy we read the contents of the packet capture into a list element "p" as shown:
>>> p=rdpcap("Challenge2-Extract1.pcap")
>>> p[0]
<Dot11  subtype=8L type=Data proto=0L FCfield=from-DS+wep ID=11264 addr1=60:fb:42:d5:e4:01 addr2=00:21:91:d2:8e:25 addr3=00:21:91:d2:8e:25 SC=35792 addr4=None |<Dot11QoS  TID=0L EOSP=0L Ack Policy=0L Reserved=0L TXOP=0 |<Dot11WEP  iv='h-`' keyid=0 wepdata='\xb2\xaa\xdf\x8dV\xbb\xa3\x15\xc9$\xcb\x1d\x00\xffZ\xdd\xa2\xbe\xb4\xc0\xae\xa3u@\xeb\x99u\xeaO\x0f+\xec\x00\x19\xa2\xaa' icv=2325285167L |>>>
Great, p[0] is the first packet in the capture file. I wasn't sure how Scapy represented the timestamp value for each packet, but a little Python introspection made it easy to figure out:

>>> dir(p[0])
['__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__div__', '__doc__', '__eq__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__len__', '__lt__', '__metaclass__', '__module__', '__mul__', '__ne__', '__new__', '__nonzero__', '__rdiv__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__setitem__', '__str__', '__weakref__', '_do_summary', 'add_payload', 'add_underlayer', 'aliastypes', 'answers', 'build', 'build_done', 'build_payload', 'build_ps', 'canvas_dump', 'clone_with', 'command', 'copy', 'decode_payload_as', 'default_fields', 'default_payload_class', 'delfieldval', 'display', 'dissect', 'dissection_done', 'do_build', 'do_build_ps', 'do_dissect', 'do_dissect_payload', 'do_init_fields', 'explicit', 'extract_padding', 'fields', 'fields_desc', 'fieldtype', 'firstlayer', 'fragment', 'from_hexcap', 'get_field', 'getfield_and_val', 'getfieldval', 'getlayer', 'guess_payload_class', 'hashret', 'haslayer', 'hide_defaults', 'init_fields', 'initialized', 'lastlayer', 'libnet', 'lower_bonds', 'mysummary', 'name', 'overload_fields', 'overloaded_fields', 'packetfields', 'payload', 'payload_guess', 'pdfdump', 'post_build', 'post_dissect', 'post_dissection', 'post_transforms', 'pre_dissect', 'psdump', 'remove_payload', 'remove_underlayer', 'route', 'sent_time', 'setfieldval', 'show', 'show2', 'show_indent', 'sprintf', 'summary', 'time', 'underlayer', 'unwep', 'upper_bonds']
>>> p[0].time
>>> p[1].time

Great, each element in the list has an epoch timestamp associated with the packet as ".time". Next, we can use the Python sorted() method to sort the contents of the list using the packet timestamp as our sort criteria:

>>> o=sorted(p, key=lambda ts: ts.time)

The output "o" is our timestamp sorted packet capture. Now it's just a matter of saving the packet capture list to a new capture file:

>>> wrpcap("Challenge2-sorted-time.cap",o)

I've never had a reason to need to sort a packet capture by timestamp before, but it's nice to know that it only took a few minutes with Scapy and Python to make it happen.