Skip to content

Commit 65e9d39

Browse files
committed
dot15d4: prevent dissection of encrypted payloads as upper layers
When sec_sc_seclevel >= 4 (ENC, ENC-MIC-*), the payload after aux_sec_header is encrypted and must not be passed to upper layer dissectors (SixLoWPAN, ZigBee, etc.). Add encrypted payload checks to guess_payload_class() in Dot15d4Data, Dot15d4Beacon, and Dot15d4Cmd.
1 parent a0d311a commit 65e9d39

2 files changed

Lines changed: 27 additions & 0 deletions

File tree

scapy/layers/dot15d4.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ class Dot15d4Data(Packet):
240240
]
241241

242242
def guess_payload_class(self, payload):
243+
# Encrypted payloads (sec_sc_seclevel >= 4) cannot be dissected further
244+
if self.aux_sec_header is not None and \
245+
self.aux_sec_header.sec_sc_seclevel >= 4:
246+
return conf.raw_layer
243247
# TODO: See how it's done in wireshark:
244248
# https://github.com/wireshark/wireshark/blob/93c60b3b7c801dddd11d8c7f2a0ea4b7d02d700a/epan/dissectors/packet-ieee802154.c#L2061 # noqa: E501
245249
# it's too magic to me
@@ -309,6 +313,13 @@ class Dot15d4Beacon(Packet):
309313
# TODO beacon payload
310314
]
311315

316+
def guess_payload_class(self, payload):
317+
# Encrypted payloads (sec_sc_seclevel >= 4) cannot be dissected further
318+
if self.aux_sec_header is not None and \
319+
self.aux_sec_header.sec_sc_seclevel >= 4:
320+
return conf.raw_layer
321+
return Packet.guess_payload_class(self, payload)
322+
312323
def mysummary(self):
313324
return self.sprintf("802.15.4 Beacon ( %Dot15d4Beacon.src_panid%:%Dot15d4Beacon.src_addr% ) assocPermit(%Dot15d4Beacon.sf_assocpermit%) panCoord(%Dot15d4Beacon.sf_pancoord%)") # noqa: E501
314325

@@ -348,6 +359,10 @@ def mysummary(self):
348359
# command frame payloads are complete: DataReq, PANIDConflictNotify, OrphanNotify, BeaconReq don't have any payload # noqa: E501
349360
# Although BeaconReq can have an optional ZigBee Beacon payload (implemented in ZigBeeBeacon) # noqa: E501
350361
def guess_payload_class(self, payload):
362+
# Encrypted payloads (sec_sc_seclevel >= 4) cannot be dissected further
363+
if self.aux_sec_header is not None and \
364+
self.aux_sec_header.sec_sc_seclevel >= 4:
365+
return conf.raw_layer
351366
if self.cmd_id == 1:
352367
return Dot15d4CmdAssocReq
353368
elif self.cmd_id == 2:

test/scapy/layers/dot15d4.uts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,18 @@ assert pkt2[Dot15d4Cmd].aux_sec_header is not None
377377
assert pkt2[Dot15d4Cmd].aux_sec_header.sec_sc_seclevel == 5
378378
assert pkt2[Dot15d4Cmd].aux_sec_header.sec_sc_keyidmode == 1
379379

380+
= Dot15d4 Data with encrypted payload (seclevel >= 4) stays Raw
381+
382+
# Given: a Data frame with seclevel=4 (ENC) and trailing encrypted bytes
383+
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=4, sec_sc_keyidmode=1, sec_keyid_keyindex=0x01)) / Raw(b'\xaa\xbb\xcc\xdd')
384+
# When: packet is serialized and re-dissected
385+
pkt2 = Dot15d4(raw(pkt))
386+
# Then: encrypted payload must not be dissected as SixLoWPAN/ZigBee
387+
assert pkt2[Dot15d4Data].aux_sec_header.sec_sc_seclevel == 4
388+
assert Raw in pkt2
389+
from scapy.layers.sixlowpan import SixLoWPAN
390+
assert SixLoWPAN not in pkt2
391+
380392
= Dot15d4 Beacon without aux_sec_header (fcf_security=0)
381393

382394
# Given: raw bytes for a Dot15d4 Beacon frame with fcf_security=0

0 commit comments

Comments
 (0)