Skip to content

Commit 8ce727b

Browse files
committed
dot15d4: fix aux_sec_header incorrect parsing (#4928)
Fix two bugs preventing correct parsing of 802.15.4 frames with the security bit set: 1. Replace `is True` identity check with truthiness check in ConditionalField lambdas for Dot15d4Data, Dot15d4Beacon, and Dot15d4Cmd. In Python 3, `1 is True` is False because `is` checks object identity, not equality, so aux_sec_header was never parsed. 2. Add extract_padding() to Dot15d4AuxSecurityHeader so that remaining bytes after the header fields are returned to the parent packet instead of being consumed as payload.
1 parent 66ef96a commit 8ce727b

2 files changed

Lines changed: 67 additions & 3 deletions

File tree

scapy/layers/dot15d4.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ class Dot15d4AuxSecurityHeader(Packet):
221221
lambda pkt: pkt.getfieldval("sec_sc_keyidmode") != 0),
222222
]
223223

224+
def extract_padding(self, s):
225+
return b"", s
226+
224227

225228
class Dot15d4Data(Packet):
226229
name = "802.15.4 Data"
@@ -233,7 +236,7 @@ class Dot15d4Data(Packet):
233236
lambda pkt:pkt.underlayer.getfieldval("fcf_srcaddrmode") != 0), # noqa: E501
234237
# Security field present if fcf_security == True
235238
ConditionalField(PacketField("aux_sec_header", Dot15d4AuxSecurityHeader(), Dot15d4AuxSecurityHeader), # noqa: E501
236-
lambda pkt:pkt.underlayer.getfieldval("fcf_security") is True), # noqa: E501
239+
lambda pkt:pkt.underlayer.getfieldval("fcf_security")), # noqa: E501
237240
]
238241

239242
def guess_payload_class(self, payload):
@@ -268,7 +271,7 @@ class Dot15d4Beacon(Packet):
268271
dot15d4AddressField("src_addr", None, length_of="fcf_srcaddrmode"),
269272
# Security field present if fcf_security == True
270273
ConditionalField(PacketField("aux_sec_header", Dot15d4AuxSecurityHeader(), Dot15d4AuxSecurityHeader), # noqa: E501
271-
lambda pkt:pkt.underlayer.getfieldval("fcf_security") is True), # noqa: E501
274+
lambda pkt:pkt.underlayer.getfieldval("fcf_security")), # noqa: E501
272275

273276
# Superframe spec field:
274277
BitField("sf_sforder", 15, 4), # not used by ZigBee
@@ -323,7 +326,7 @@ class Dot15d4Cmd(Packet):
323326
lambda pkt:pkt.underlayer.getfieldval("fcf_srcaddrmode") != 0), # noqa: E501
324327
# Security field present if fcf_security == True
325328
ConditionalField(PacketField("aux_sec_header", Dot15d4AuxSecurityHeader(), Dot15d4AuxSecurityHeader), # noqa: E501
326-
lambda pkt:pkt.underlayer.getfieldval("fcf_security") is True), # noqa: E501
329+
lambda pkt:pkt.underlayer.getfieldval("fcf_security")), # noqa: E501
327330
ByteEnumField("cmd_id", 0, {
328331
1: "AssocReq", # Association request
329332
2: "AssocResp", # Association response

test/scapy/layers/dot15d4.uts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,67 @@ p = Dot15d4AuxSecurityHeader(b"\x18\x05\x00\x00\x00\xff\xee\xdd\xcc\xbb\xaa\x00\
325325
assert p.sec_sc_keyidmode == 3
326326
assert p.sec_keyid_keysource == 11024999611375677183
327327

328+
= Dot15d4AuxSecurityHeader - extract_padding does not consume trailing bytes
329+
330+
p = Dot15d4AuxSecurityHeader(b"\x04\x05\x00\x00\x00\xAA\xBB")
331+
assert p.sec_sc_seclevel == 4
332+
assert p.sec_framecounter == 0x5
333+
assert Raw not in p
334+
assert Padding in p
335+
assert p[Padding].load == b"\xAA\xBB"
336+
337+
= Dot15d4 Beacon with aux_sec_header (issue #4928)
338+
339+
# Given: raw bytes for a Dot15d4 Beacon frame with fcf_security=1
340+
pkt = Dot15d4(b'\x08\xD0\x84\x21\x43\x01\x00\x00\x00\x00\x48\xDE\xAC\x02\x05\x00\x00\x00\x55\xCF\x00\x00\x51\x52\x53\x54\x22\x3B\xC1\xEC\x84\x1A\xB5\x53')
341+
# When: packet is dissected
342+
# Then: fcf_security is set and aux_sec_header is correctly parsed
343+
assert pkt.fcf_frametype == 0
344+
assert pkt.fcf_security == 1
345+
assert Dot15d4Beacon in pkt
346+
assert pkt[Dot15d4Beacon].aux_sec_header is not None
347+
assert pkt[Dot15d4Beacon].aux_sec_header.sec_sc_seclevel == 2
348+
assert pkt[Dot15d4Beacon].aux_sec_header.sec_sc_keyidmode == 0
349+
assert pkt[Dot15d4Beacon].aux_sec_header.sec_framecounter == 0x5
350+
assert Raw in pkt
351+
352+
= Dot15d4 Data with aux_sec_header - build & dissect round-trip
353+
354+
# Given: a Dot15d4 Data frame with fcf_security=1
355+
pkt = Dot15d4(fcf_frametype=1, fcf_security=1, fcf_destaddrmode=2, fcf_srcaddrmode=2) / Dot15d4Data(dest_panid=0x1234, dest_addr=0xFFFF, src_panid=0x1234, src_addr=0x0001, aux_sec_header=Dot15d4AuxSecurityHeader(sec_sc_seclevel=5, sec_sc_keyidmode=1, sec_keyid_keyindex=0x01))
356+
# When: packet is serialized and re-dissected
357+
pkt2 = Dot15d4(raw(pkt))
358+
# Then: aux_sec_header is correctly parsed
359+
assert pkt2.fcf_security == 1
360+
assert Dot15d4Data in pkt2
361+
assert pkt2[Dot15d4Data].aux_sec_header is not None
362+
assert pkt2[Dot15d4Data].aux_sec_header.sec_sc_seclevel == 5
363+
assert pkt2[Dot15d4Data].aux_sec_header.sec_sc_keyidmode == 1
364+
assert pkt2[Dot15d4Data].aux_sec_header.sec_keyid_keyindex == 0x01
365+
366+
= Dot15d4 Cmd with aux_sec_header - build & dissect round-trip
367+
368+
# Given: a Dot15d4 Command frame with fcf_security=1
369+
pkt = Dot15d4(fcf_frametype=3, fcf_security=1, fcf_destaddrmode=2, fcf_srcaddrmode=2) / Dot15d4Cmd(dest_panid=0x1234, dest_addr=0xFFFF, src_panid=0x1234, src_addr=0x0001, aux_sec_header=Dot15d4AuxSecurityHeader(sec_sc_seclevel=5, sec_sc_keyidmode=1, sec_keyid_keyindex=0x01), cmd_id=4)
370+
# When: packet is serialized and re-dissected
371+
pkt2 = Dot15d4(raw(pkt))
372+
# Then: aux_sec_header is correctly parsed
373+
assert pkt2.fcf_security == 1
374+
assert Dot15d4Cmd in pkt2
375+
assert pkt2[Dot15d4Cmd].aux_sec_header is not None
376+
assert pkt2[Dot15d4Cmd].aux_sec_header.sec_sc_seclevel == 5
377+
assert pkt2[Dot15d4Cmd].aux_sec_header.sec_sc_keyidmode == 1
378+
379+
= Dot15d4 Beacon without aux_sec_header (fcf_security=0)
380+
381+
# Given: raw bytes for a Dot15d4 Beacon frame with fcf_security=0
382+
pkt = Dot15d4FCS(b'\x00\x80\x89\xaa\x99\x00\x00\xff\xcf\x00\x00\x00"\x84\xfe\xca\xef\xbe\xed\xfe\xce\xfa\xff\xff\xff\x00X\xa4')
383+
# When: packet is dissected
384+
# Then: fcf_security is not set and aux_sec_header is None
385+
assert pkt.fcf_security == 0
386+
assert Dot15d4Beacon in pkt
387+
assert pkt[Dot15d4Beacon].aux_sec_header is None
388+
328389
# RPL: unimplemented
329390
#p = SixLoWPAN(b"\x7b\x3b\x3a\x1a\x9b\x02\xae\x30\x21\x00\x00\xef\x05\x12\x00\x80\x20\x02\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x00\x33\x44\x09\x04\x00\x00\x00\x00\x06\x04\x00\x01\xef\xff")
330391
#p.show2()

0 commit comments

Comments
 (0)