Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scapy: Using a PacketListField to dissect multiple packets contained in a packet

I am trying to dissect packets, which encapsulate another packet-like structure, called "tags". The structure looks like this

+---------+
|Ether    |
+---------+
|IP       |                   a tag
+---------+
|UDP      |                +------------+
+---------+                |tagNumber   |
|BVLC     |                +------------+
+---------+                |tagClass    |
|NPDU     |                +------------+
+---------+           +-+  |LVT field   |
|APDU     |           |    +------------+
|  +------+--+        |    |            |
|  |Tag 1    | <------+    |  data      |
|  +---------+             |            |
|  |Tag 2    |             +------------+
|  +---------+
|  |Tag n    |
+------------+

For this I created a class derived from the existing PacketListField, which looks like this:

class TagListField(PacketListField):
    def __init__(self):
        PacketListField.__init__(
            self,
            "tags",
            [],
            guessBACNetTagClass,

The referenced guessBACNetTagClass is a function which returns the correct class needed to dissect the tag.

BACNetTagClasses = {
    0x2: "BACNetTag_U_int",
    0xC: "BACNetTag_Object_Identifier"
}

def guessBACNetTagClass(packet, **kargs):

    """ Returns the correct BACNetTag Class needed to dissect
        the current tag

        @type    packet:    binary string
        @param   packet:    the current packet

        @type    cls:       class
        @param   cls:       the correct class for dissection
    """

    tagByteBinary = "{0:b}".format(int(struct.unpack("!B", packet[0])[0]))
    tagNumber = int(tagByteBinary[0:4],2)
    clsName = BACNetTagClasses.get(tagNumber)
    cls = globals()[clsName]
    return cls(packet, **kargs)

Currently there are, as you can see from the BACNetTagClasses dictionary from above, two of these classes.

class BACNetTag_Object_Identifier(Packet):
    name = "BACNetTag_Object_Identifier"
    fields_desc =[
        # fields
    ]

class BACNetTag_U_int(Packet):
    name = "BACNetTag_U_int"
    fields_desc = [
        # fields
    ]

In the encapsulating layer, called APDU I added the TagListField just like another field.

class APDU(Packet):

    name = "APDU"
    fields_desc = [
        # Some other fields

        TagListField()
    ]

A packet, which I am currently trying to dissect, contains multiple tags. The first one (of the type BACNetTag_Object_Identifier) can be correctly dissected, but the remaining tags are just listed as raw payload.

[<Ether |<UDP |<BVLC |<NPDU |<APDU 
pduType=UNCONFIRMED_SERVICE_REQUEST reserved=None serviceChoice=I_AM
tags=[<BACNetTag_Object_Identifier  tagNumber=BACNET_OBJECT_IDENTIFIER
tagClass=APPLICATION lengthValueType=4L objectType=DEVICE
instanceNumber=640899L |<Raw  load='"\x01\xe0\x91\x00!\xde'
|>>] |>>>>>>]

Is there something wrong with my implementation of PacketListField? As I understand, the field should try to dissect the remaining tags until no more Bytes are left.


UPDATE:

Using .show() reveals a bit more about the packets structure

###[ APDU ]###
  pduType   = UNCONFIRMED_SERVICE_REQUEST
  reserved  = None
  serviceChoice= I_AM
  \tags      \
   |###[ BACNetTag_Object_Identifier ]###
   |  tagNumber = BACNET_OBJECT_IDENTIFIER
   |  tagClass  = APPLICATION
   |  lengthValueType= 4L
   |  objectType= DEVICE
   |  instanceNumber= 640899L
   |###[ Raw ]###
   |     load      = '"\x01\xe0\x91\x00!\xde'

Scapy just appends the remaining Bytes as a Raw layer to the existing tags field. That's interesting, but I still don't know why it is doing so.

like image 918
vicco Avatar asked Nov 17 '15 11:11

vicco


People also ask

How do you sniff packets with scapy?

Sniffing packets using scapy: To sniff the packets use the sniff() function. The sniff() function returns information about all the packets that has been sniffed. To see the summary of packet responses, use summary(). The sniff() function listens for an infinite period of time until the user interrupts.

What is sr1 in scapy?

Send and receive packets (sr) The function sr1() is a variant that only returns one packet that answered the packet (or the packet set) sent. The packets must be layer 3 packets (IP, ARP, etc.).

Does scapy use Libpcap?

Scapy runs natively on Linux, Windows, OSX and on most Unixes with libpcap (see scapy's installation page). The same code base now runs natively on both Python 2 and Python 3.

What is LS in scapy?

ls() : Displays all the protocols supported by scapy. lsc() : Displays the list of commands and functions supported by scapy. conf : Displays all configuration options. help() : Displays help on a specific command, for example, help(sniff)


1 Answers

Try to overwrite the extract_padding method of your BACNet_Tag* classes with:

def extract_padding(self, s):
return '', s

I had similar problems with PacketListField and found this stackoverflow post:

Scapy: Adding new protocol with complex field groupings

like image 195
smehner Avatar answered Sep 17 '22 05:09

smehner