When a product uses a new or proprietary protocol, there may be no tool ready to help you assess its security. You may have to write a new tool from scratch in order to accomplish this task, but this usually takes a lot of time and effort. It is much more efficient if you have to focus only on re-implementing the protocol and use an existing framework that already provides everything to manipulate, visualize, send, receive, probe, fuzz, etc. Thankfully, Scapy’s three-part architecture (the core, the protocols, and the techniques) provides just such a framework. This means that each time that something is added to the core, all the protocols will be able to take advan- tage of it. Each time a new protocol is added, all the core functionalities of Scapy are already there to enhance it. Thus, Scapy offers an efficient framework to describe new protocol layers.
A layer is compounded of a list of fields. Each field is described with an instance of a
Field class or subclass. Each field has a name, a default value, and some other parameters depending upon its type.
As shown in Example 6-8, there are basic rules to make a new layer. Your new layer must be a subclass ofPacket. The attributenameis used to hold the long name of the layer, the short one being the name of the class. Then, the list of fields is put into the Example 6-6. A firewalker
res,unans = sr(IP(dst=TARGET, ttl=GW_TTL)/TCP(dport=(0,1024)), timeout=4) res.make_table(lambda (s,r):(s.dst, s.dport, r.sprintf("{TCP:%TCP.flags%} {ICMP:%IP.src%#%r,ICMP.type%}")))
Example 6-7. A DNS traceroute
res,unans = sr(IP(dst=TARGET, ttl=(1,30)/UDP(sport=RandShort( )) /DNS(qd=DNSQR(qname="www.test.com"))),timeout=4)
res.make_table(lambda (s,r):(s.dst, s.ttl, r.sprintf("{ICMP:%IP.src%#%ICMP.type%} {UDP:%IP.src% %DNSRR.rname%}"))
fields_desc attribute. Here we have a 1-byte field named mickey, a short field (2 bytes) namedminnieand an int field (4 bytes) nameddonald. They both are unsigned and big endian—the network endianness. The ShortFieldis the vanilla short field type.XShortFieldis a different flavor and expresses the fact that the preferred field’s value representation is in hexadecimal notation. The IntEnumField is a flavor of
IntFieldthat can translate some values into identifiers and vice versa, according to a provided dictionary.
If you want to begin to play with new protocols right now, read “Writ- ing Add-Ons” in Section 6.3 before directly modifying the Scapy source.
The simple description in Example 6-8 is sufficient to assemble, disassemble, and manipulate the layer like any other layer in Scapy. For example:
>>> a=MyField(mickey=2) >>> a <MyField mickey=2 |> >>> a.donald 2 >>> a.show( ) ###[ My Field ]### mickey= 2 minnie= 0x43 donald= bashful >>> a.donald="happy" >>> a.donald 1 >>> str(a) '\x02\x00C\x00\x00\x00\x01' >>> MyField(_)
<MyField mickey=2 minnie=0x43 donald=happy |> >>> send(IP(proto=42)/MyField(minnie=1)) .
Sent 1 packets.
This is possible because all the brainpower is concentrated into the fields classes. Several kinds of fields are provided.
Example 6-8. Creating a new field in Scapy class MyField(Packet):
name = "My Field"
fields_desc = [ ByteField("mickey", 4), XShortField("minnie", 67), IntEnumField("donald", 2,
{1:"happy", 2:"bashful", 3:"sneezy"}) ]
As an example of concentrating the power into the fields classes, Figure 6-3 has a layer with a byte value that encodes a type, a field that encodes the length of a string, a flags field, four reserved unused bits, and the string itself.
We have a dependency between thestringfield and thelengthfield. When the layer is assembled, thelengthfield must take its value from thestringfield. When the layer is dissected, thestringfield must know thelengthfield value to know where to stop. For thelengthfield,FieldLenFieldclass will be used. It is able to takes its value from the length of another field for assembly. The string field will use the StrLenField
class, which is able to use another field’s value that is already dissected to know how much bytes to take for the packet at disassembly time.
The type field behavior can be modeled by a ByteField instance. But we can add labels to sometype value by using aByteEnumField instance.
The reservedfield is only four bits long. It is modeled by aBitField instance. The number of bits must be passed to its constructor, along with the field’s name and default value. BitField instances must be followed by other BitField instances if they do not end on a byte boundary.
Theflagsfield will be modeled by a FlagsField. AFlagsFieldhas almost the same behavior as aBitFieldexcept that each bit can be addressed independently. For this, a list of labels is provided, either in the form of a string whose characters are associ- ated with bits or in the form of a list of labels. In this case, we will use the labelAfor bit 0,B for bit 1, and so on until bit 12.
The length field will be modeled by a FieldLenField instance. The FieldLenField
class does not impose any encoding length. By default, it is on two bytes, so we must enforce it to be on one byte with the"B"directive (these directives come from the Python struct module). The field whose length must be measured when the value must be automatically computed is also provided.
Thestring field will be modeled by aStrLenFieldinstance. It differs from a simple
StrField instance by the fact its length is imposed by another field, whose name must be provided.
Example 6-9 shows an initial implementation of the demonstration layer class. Figure 6-3. Demonstration layer
Type Length Reserved Flags
Once the class is defined, we can play with it right away: >>> a=Demo( ) >>> a.show( ) ###[ Demo ]### type= type2 length= 0 reserved= 0 flags= BDF string= 'default' >>> a.flags 42 >>> a.flags=0xF0 >>> a.sprintf("%flags%") 'EFGH' >>> hexdump(a) 0000 02 07 00 F0 64 65 66 61 75 6C 74 ....default >>> a.string="X"*33 >>> hexdump(a) 0000 02 21 00 F0 58 58 58 58 58 58 58 58 58 58 58 58 .!..XXXXXXXXXXXX 0010 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 XXXXXXXXXXXXXXXX 0020 58 58 58 58 XXXX
Now you have to inform Scapy of when to use this layer. Let’s say it is a protocol that works on UDP packets with source and destination ports of 31337. We will bind it with:
>>> bind_layers(UDP, Demo, sport=31337, dport=31337)
Not only will it make UDP recognize Demo and set ports accordingly: >>> IP()/UDP()/Demo( )
<IP frag=0 proto=udp |<UDP sport=31337 dport=31337 |<Demo |>>>
but Scapy will also know it must call the Demo layer when it meets such UDP ports during a dissection:
>>> a=IP()/UDP()/Demo( ) >>> IP(str(a))