From 74070810e31288eae030227ec3949fa1e612579b Mon Sep 17 00:00:00 2001 From: NK Date: Thu, 3 Sep 2026 02:06:53 +0530 Subject: [PATCH 1/3] hsrp: do not crash when dissecting HSRP without an underlayer HSRP.guess_payload_class read self.underlayer.len unconditionally, so dissecting an HSRP packet that carries a trailing payload without an underlayer (e.g. HSRP(b'\x00\x01' + ...) or via layers that do not expose .len) raised AttributeError instead of dissecting the trailing bytes as the default payload class. Guard the underlayer access, keeping the HSRPmd5 selection unchanged when the underlayer is present and long enough. AI-Assisted: yes (Claude) --- scapy/layers/hsrp.py | 9 ++++++--- test/scapy/layers/hsrp.uts | 5 +++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/scapy/layers/hsrp.py b/scapy/layers/hsrp.py index 341ea206fbd..a3fa6ba029a 100644 --- a/scapy/layers/hsrp.py +++ b/scapy/layers/hsrp.py @@ -88,10 +88,13 @@ def dispatch_hook(cls, _pkt=None, *args, **kargs): return HSRP def guess_payload_class(self, payload): - if self.underlayer.len > 28: + if ( + self.underlayer is not None + and getattr(self.underlayer, "len", None) is not None + and self.underlayer.len > 28 + ): return HSRPmd5 - else: - return Packet.guess_payload_class(self, payload) + return Packet.guess_payload_class(self, payload) class HSRPAdvertise(Packet): diff --git a/test/scapy/layers/hsrp.uts b/test/scapy/layers/hsrp.uts index e0bd1676448..95063190730 100644 --- a/test/scapy/layers/hsrp.uts +++ b/test/scapy/layers/hsrp.uts @@ -51,3 +51,8 @@ assert pkt[HSRPv2].ipVer == 6 and pkt[HSRPv2].padding == None assert pkt[HSRPv2].version == 2 and pkt[HSRPv2].opcode == 0 and pkt[HSRPv2].state == 16 assert pkt[HSRPv2].hellotime == 3000 and pkt[HSRPv2].holdtime == 10000 assert pkt[HSRPv2TextAuth].type == 3 and pkt[HSRPv2TextAuth].auth == b"cisco\x00\x00\x00" + += HSRP without underlayer does not crash +pkt = HSRP(b"\x00\x01" + b"\x00" * 26 + b"\x41" * 20) +assert pkt.version == 0 and pkt.opcode == 1 +assert Raw in pkt and pkt[Raw].load.endswith(b"A" * 20) From 0d469dffe41e6ce8291d058b9b7dfeab087f04ce Mon Sep 17 00:00:00 2001 From: NK Date: Thu, 3 Sep 2026 02:06:53 +0530 Subject: [PATCH 2/3] bluetooth: fix HCI_Event_Inquiry_Result_With_Rssi field classes The six FieldListField entries were given the field classes (LEMACField, ByteField, ...) instead of instances, so any dissection of the event with num_response >= 1 raised 'Field.getfield() missing 1 required positional argument'. FieldListField expects an instance per its annotation (field: AnyField). Pass instances and cover a full inquiry result round trip in the test campaign. AI-Assisted: yes (Claude) --- scapy/layers/bluetooth.py | 12 ++++++------ test/scapy/layers/bluetooth.uts | 9 +++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/scapy/layers/bluetooth.py b/scapy/layers/bluetooth.py index f308fefe45d..c1486fd9621 100644 --- a/scapy/layers/bluetooth.py +++ b/scapy/layers/bluetooth.py @@ -2697,17 +2697,17 @@ class HCI_Event_Inquiry_Result_With_Rssi(Packet): name = "HCI_Inquiry_Result_with_RSSI" fields_desc = [ ByteField("num_response", 0x00), - FieldListField("bd_addr", None, LEMACField, + FieldListField("bd_addr", None, LEMACField("", None), count_from=lambda p: p.num_response), - FieldListField("page_scan_repetition_mode", None, ByteField, + FieldListField("page_scan_repetition_mode", None, ByteField("", 0), count_from=lambda p: p.num_response), - FieldListField("reserved", None, LEShortField, + FieldListField("reserved", None, LEShortField("", 0), count_from=lambda p: p.num_response), - FieldListField("device_class", None, XLE3BytesField, + FieldListField("device_class", None, XLE3BytesField("", 0), count_from=lambda p: p.num_response), - FieldListField("clock_offset", None, LEShortField, + FieldListField("clock_offset", None, LEShortField("", 0), count_from=lambda p: p.num_response), - FieldListField("rssi", None, SignedByteField, + FieldListField("rssi", None, SignedByteField("", 0), count_from=lambda p: p.num_response) ] diff --git a/test/scapy/layers/bluetooth.uts b/test/scapy/layers/bluetooth.uts index 40bd78b8a2c..5581fe25d4e 100644 --- a/test/scapy/layers/bluetooth.uts +++ b/test/scapy/layers/bluetooth.uts @@ -1284,3 +1284,12 @@ assert p.num_sets == 1 assert len(p.sets) == 1 assert p.sets[0].handle == 1 assert raw(p) == hex_bytes("010101640005") + += HCI_Event_Inquiry_Result_With_Rssi dissection + +p = HCI_Event_Inquiry_Result_With_Rssi(hex_bytes("011122334455660100000000000000c0")) +assert p.num_response == 1 +assert p.bd_addr == ["66:55:44:33:22:11"] +assert p.page_scan_repetition_mode == [1] +assert p.rssi == [-64] +assert raw(p) == hex_bytes("011122334455660100000000000000c0") From 39e9a09fca867709dedb72b45714995cc1cb922b Mon Sep 17 00:00:00 2001 From: NK Date: Thu, 3 Sep 2026 02:06:53 +0530 Subject: [PATCH 3/3] kerberos: do not crash on a KRB_InnerToken with an unknown TOK_ID _InitialContextTokens[_parent.TOK_ID] raised KeyError for any TOK_ID outside the implemented set, aborting the dissection of a token with an attacker-supplied or future token identifier. Fall back to conf.raw_layer, consistent with the mysummary() fallback, keeping the body as raw bytes. Known TOK_IDs are unaffected. AI-Assisted: yes (Claude) --- scapy/layers/kerberos.py | 4 +++- test/scapy/layers/kerberos.uts | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/scapy/layers/kerberos.py b/scapy/layers/kerberos.py index 990e0ebcf68..88e4e302ffb 100644 --- a/scapy/layers/kerberos.py +++ b/scapy/layers/kerberos.py @@ -2520,7 +2520,9 @@ class KRB_InnerToken(Packet): PacketField( "root", KRB_AP_REQ(), - lambda x, _parent: _InitialContextTokens[_parent.TOK_ID](x), + lambda x, _parent: _InitialContextTokens.get( + _parent.TOK_ID, conf.raw_layer + )(x), ), ] diff --git a/test/scapy/layers/kerberos.uts b/test/scapy/layers/kerberos.uts index 14c762612fd..a8161f1dcf1 100644 --- a/test/scapy/layers/kerberos.uts +++ b/test/scapy/layers/kerberos.uts @@ -2168,4 +2168,10 @@ with KrbRandomPatcher(): ) ) -assert negState == 0 \ No newline at end of file +assert negState == 0 += KRB_InnerToken with an unknown TOK_ID does not crash + +p = KRB_InnerToken(hex_bytes("ead5") + b"\x00" * 30) +assert p.TOK_ID == b"\xea\xd5" +assert isinstance(p.root, conf.raw_layer) +assert p.root.load == b"\x00" * 30