3
votes

I'm trying to create the ZMTP protocol for Scapy but can't find how to effectively pad a field and the packet up to a certain byte size.

I've tried creating the Greeting message described here, https://www.codeproject.com/Articles/863889/ZeroMQ-Diving-into-the-Wire, as a new protocol in Scapy:

class ZmtpGreeting(Packet):
    name = "ZMTP Greeting"
    fields_desc = [
        XByteField("signature", "ff00000000000000117f"),
        XByteField("version_major", "03"),
        XByteField("version_minor", "00"),
        PadField(XByteField("security_mechanism", "4e554c4c"), 20, padwith="\x00"),
        XByteField("as_server", "00"),
    ]

Problem 1: The above implementation doesn't seem to include the padding up to 20 bytes for the security_mechanism field:

###[ IP ]###
  version= 4
  ihl= None
  tos= 0x0
  len= None
  id= 1
  flags=
  frag= 0
  ttl= 64
  proto= tcp
  chksum= None
  src= 1.1.1.2
  dst= 1.1.1.1
  \options\
###[ TCP ]###
     sport= 1111
     dport= 2222
     seq= 1
     ack= 1
     dataofs= None
     reserved= 0
     flags= PA
     window= 8192
     chksum= None
     urgptr= 0
     options= []
###[ ZMTP Greeting ]###
        signature= ff00000000000000117f
        version_major= 03
        version_minor= 00
        security_mechanism= 4e554c4c
        as_server= 00

This entire packet should also be padded to 64 octets and I'm not sure how to include that in the protocol (or as padding outside of it).

Problem 2: I am unable to use raw(..) or len(..) because they fail with the following error:

fields.py in addfield(self, pkt, s, val)
    138         `pkt`) to the raw string packet `s`, and return the new string packet.
    139         """
--> 140         return s + struct.pack(self.fmt, self.i2m(pkt, val))
    141
    142     def getfield(self, pkt, s):

error: required argument is not an integer

Problem 3: This is in its own python file under layers, but only works when I directly paste it into the Scapy console.

I've been looking through the documentation for Scapy but there haven't found out how to achieve this. Thanks in advance!

2
I have all the same problems with the simple Disney example here: scapy.readthedocs.io/en/latest/build_dissect.htmlPazow

2 Answers

1
votes
  • First of all, in Scapy, a X[...] field is only for user-friendliness. This means that you can't use a string as a default value: Scapy only works with ints (except for special fields, packet fields, string fields...). Updating those values to the numeral representation will fix Q2
  • I doubt the ByteField can take such a high value :/
  • to add the final padding, have a look at post_dissection
1
votes

Q : how to effectively pad a field and the packet up to a certain byte size.

On python side, you can always import struct and operate using all of its bit-fields' manipulation, using the <mask>-meta-language controlled smart-mappings of values onto the resulting bit-field (in any of both ways using {.pack()|.unpack()}-methods) as in struct.unpack( aMSG_STRUCT_MASK, aMSG )

A "hierarchical" (step-wise) assembly might be needed, if partial checksums are to be used in the game of making the final ZMTP:RFC-compatible packet-payloads.

This includes several code-paths for potentially version-specific payload-assembly handling rules, set from different ZMTP:RFC versions/releases/modes-of-operations.

Perhaps start with pencil and paper, to sketch all the bit-fields details, as required by the ZMTP:RFC and check all theirs implementations ( This is a brutally manual method, yet this inventory + checklist can literally save you as the last-resort because it works and is damn methodically orthodox - unchecked mandatory requirements mean "forgotten" part of the implementation + incompatible/wrong implementation can be tested feature by feature, so no one is ever left unchecked or ill-validated ) - this might sound boring, but is a life-saving habit.

Q : Problem 2:

is undecideable as not being reproducible, yet further debugging efforts will show the root-cause ( trying to add a <pyObject>s, using a +-operator, to a <pyObject>-yielded from a call to the above recommended tool, the struct-package .pack()-method:
struct.pack( self.<anInstanceAttributeMASK>, self.<aMethod>( <pyObject>, <pyObject>) )

Q : Problem 1: 64-octet padded / word-aligned / null-terminated

All these requirements, controlled by the detailed ZMTP/RFC-specifications are to be met, and struct.pack() will meet these, given just properly set <aPackMASK>-s will participate on a ZMTP-payload-content packet-data assembly.

Best review, whether all of the ZMTP:RFC specified details ( lengths, alignments, paddings, terminations, (partial)checksum(s) ) are indeed met (even for corner cases) and correct/modify all features found to have been missed.


In case large-integer elements fail to get processed by formatting, one may always split the large block into a series of a few, smaller ones, to circumvent any potentially failing int/string operations on formatting/packet assembly in any part of the ScaPy hard-coded functions, as show here:

|>>> sig_num= 0xff00000000000000117f
|>>> sig_num
1204203453131759529496959L
|>>> "{0:x}".format( sig_num )
'ff00000000000000117f'
|>>> "{0:b}".format( sig_num )
'11111111000000000000000000000000000000000000000000000000000000000001000101111111'
|>>> len( "{0:b}".format( sig_num ) )
80
|>>> "{0:0>20b}{1:0>20b}{2:0>20b}{3:0>20b}".format( sig_num / 2**60,
                                                    sig_num % 2**60 / 2**40,
                                                    sig_num %         2**40 / 2**20,
                                                    sig_num %                 2**20
                                                    )
'11111111000000000000000000000000000000000000000000000000000000000001000101111111'