Tuesday, July 3, 2018

Scapy part 3: Analyzing custom packets


In the last two sections I covered how Scapy can be used as a general tool for packet analysis, and also how it can be used to craft custom protocols to meet your own needs. In this third and final installment I will discuss how to use scapy to analyze packets again, this time with an eye towards processing custom protocol layers.
The most important thing to understand about parsing different packets in scapy is: Layers. Everything comes down to a collection of layers. Which in themselves are only slightly more than a collection of field definitions and sane default values.
If you recall from part 2, the Packet I defined had the structure Ether()/IP()/DEPKP(). Each one of these functions (Ether(), IP(), and DEPKP()) each refer to a layer. When dissecting a packet, you can refer to each layer directly by name (p[DEPKP] for example) or you can use p.getlayer(DEPKP) equivalently.

Layer Dissection

According to the documentation:
"A layer is a subclass of the Packet class. All the logic behind layer manipulation is held by the Packet class and will be inherited. A simple layer is compounded by a list of fields that will be either concatenated when assembling the layer or dissected one by one when disassembling a string. The list of fields is held in an attribute named fields_desc. Each field is an instance of a field class"
The meaning of this is that, to Scapy, an incoming packet is little more than an amorphous blob, until some parsing takes place which parses the bytes into an assumed specifications. When we defined a new protocol we defined the field type, length, and order of each of it's parameters. Also, we defined that this protocol sat above the IP layer of the packet (by appending /DEPKP() after IP())
To understand how a Layer is processed by Scapy then, one must understand how Scapy is breaking up the data in the background.

Behind-the-Scenes

First, it is important to realize that Scapy looks at each field differently based on context. The first context is whether or not the information is being processed by a human (h) , another machine (m), or internally (i). Each of these representations of the field's data are in use at different times throughout the life-cycle of the packet being analyzed. Although you may never need to manually handle these translations, Scapy provides special methods for converting such as i2h(), i2m() and m2h() which handle the translations. There are many more, so if you find yourself in this position consult the documentation.

A much higher-level interface getfield(pkt, s) extracts from the raw packet s the field value belonging to layer pkt. The 1st element of the list it returns is the raw packet string after having removed the extracted field, a sort of "Pop" for raw field data. The second item is the extracted field itself in internal representation. When defining your own layer protocols, you usually just need to define some *2*() methods, and also the addfield() and getfield() If you use extended field types.

The workhorse of a custom dissection will usually come down to the Packet.dissect()function. This function defines pre_, do_ and post_ processing methods on a raw packet string. It then extracts the payload and padding. Finally, it dissects the payload and stores it off if required. There is another function, do_dissect(), which is related. It is detailed in the documents and you should consult it when you build your protocol specification.

When a Door Man packet comes in, there are 3 calls to Packet.dissect(). The first to parse the fields in the Ether() layer, then to parse the fields in the IP() layer, and finally to parse the fields in the DEPKP() layer. If you do not like Scapy’s behavior for a given layer, you can either change or disable it through the call to split_layer(). For example (from the documentation):
If you do not want UDP/53 to be bound with DNS, just add in your code: `` split_layers(UDP, DNS, sport=53) `` Now every packet with source port 53 will not be handled as DNS, but whatever you specify instead.
Each layer has a field payload_guess. When we use the bind_layers() function This is sort of the opposite end of the split_layer() function., it adds the defined next layers to that list. You can use this, for example to bind all traffic on port 13210 to the DEPKP() layer like so:
bind_layers( IP, DEPKP, sport=13210 )
bind_layers( IP, DEPKP, dport=13210 )
Now every packet related to port 1320 will be associated to the layer DEPKP(), whether it is read from a .pcap file or received from the network. We can analyze the fields in all three layers of the packet just as we would any other packet (so long as we add the previously mentioned conversion methods to the DEPKP() class)

Conclusion


At this point we have reached the end of our Door Man journey. You should be able to write a program which can sniff traffic off the network at this point. I hope you feel you understand the concepts of creating packets in Scapy, and defining a custom protocol layer. Now, you should understand how to create the code which will take these packets and allow you to work with the contained fields. Once you have parsed each of the fields, the matter of decrypting the payload and granting access to the contained mac address is a simple matter covered by other encryption/decryption posts.

While this is the end of one Scapy project, it is not the end of them all for me. In a future installment, I will tie my Scapy adventures in with my recent post on Network Infection Simulation to lay the groundwork for an intelligent Risk-Based Packet Filter which can be used to proxy applications.

No comments:

Post a Comment