From d8d24da22540c353a0f25eb2ab0e602de8f78423 Mon Sep 17 00:00:00 2001 From: gpotter2 <10530980+gpotter2@users.noreply.github.com> Date: Mon, 26 Feb 2024 22:02:55 +0100 Subject: [PATCH] Basic [MS-BRWS] support (#4300) --- scapy/layers/ldap.py | 2 +- scapy/layers/netbios.py | 13 +++++- scapy/layers/smb.py | 87 ++++++++++++++++++++++++++++++++++++++- test/scapy/layers/smb.uts | 55 +++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 3 deletions(-) diff --git a/scapy/layers/ldap.py b/scapy/layers/ldap.py index e9cfd4505a0..eaea379ec34 100644 --- a/scapy/layers/ldap.py +++ b/scapy/layers/ldap.py @@ -573,7 +573,7 @@ def is_request(self, req): req = req.protocolOp return ( req.attributes - and req.attributes[0].type.val == b"Netlogon" + and req.attributes[0].type.val.lower() == b"netlogon" and req.filter and isinstance(req.filter.filter, LDAP_FilterAnd) and any( diff --git a/scapy/layers/netbios.py b/scapy/layers/netbios.py index 2728df819ee..fa02dec8f40 100644 --- a/scapy/layers/netbios.py +++ b/scapy/layers/netbios.py @@ -279,7 +279,11 @@ class NBNSRegistrationRequest(Packet): IPField("NB_ADDRESS", "127.0.0.1") ] + def mysummary(self): + return self.sprintf("Register %G% %QUESTION_NAME% at %NB_ADDRESS%") + +bind_bottom_up(NBNSHeader, NBNSRegistrationRequest, OPCODE=0x5) bind_layers(NBNSHeader, NBNSRegistrationRequest, OPCODE=0x5, NM_FLAGS=0x11, QDCOUNT=1, ARCOUNT=1) @@ -312,7 +316,7 @@ class NBTDatagram(Packet): ShortField("ID", 0), IPField("SourceIP", "127.0.0.1"), ShortField("SourcePort", 138), - ShortField("Length", 272), + ShortField("Length", None), ShortField("Offset", 0), NetBIOSNameField("SourceName", "windows"), ShortEnumField("SUFFIX1", 0x4141, _NETBIOS_SUFFIXES), @@ -321,6 +325,13 @@ class NBTDatagram(Packet): ShortEnumField("SUFFIX2", 0x4141, _NETBIOS_SUFFIXES), ByteField("NULL2", 0)] + def post_build(self, pkt, pay): + if self.Length is None: + length = len(pay) + 68 + pkt = pkt[:10] + struct.pack("!H", length) + pkt[12:] + return pkt + pay + + # SESSION SERVICE PACKETS diff --git a/scapy/layers/smb.py b/scapy/layers/smb.py index f50ab0e6fd6..bafb790bd31 100644 --- a/scapy/layers/smb.py +++ b/scapy/layers/smb.py @@ -684,8 +684,10 @@ class SMBSession_Null(Packet): class _SMB_TransactionRequest_Data(PacketLenField): def m2i(self, pkt, m): - if pkt.WordCount == 0x11: + if pkt.Name == b"\\MAILSLOT\\NET\\NETLOGON": return NETLOGON(m) + elif pkt.Name == b"\\MAILSLOT\\BROWSE" or pkt.name == b"\\MAILSLOT\\LANMAN": + return BRWS(m) return conf.raw_layer(m) @@ -780,6 +782,11 @@ def post_build(self, pkt, pay): + pay ) + def mysummary(self): + if self.DataLen: + return self.sprintf("Tran %Name% ") + self.Data.mysummary() + return self.sprintf("Tran %Name%") + bind_top_down(SMB_Header, SMBTransaction_Request, Command=0x25) @@ -1084,6 +1091,84 @@ def get_full(self): return self.original +# [MS-BRWS] sect 2.2 + +class BRWS(Packet): + fields_desc = [ + ByteEnumField("OpCode", 0x00, { + 0x01: "HostAnnouncement", + 0x02: "AnnouncementRequest", + 0x08: "RequestElection", + 0x09: "GetBackupListRequest", + 0x0A: "GetBackupListResponse", + 0x0B: "BecomeBackup", + 0x0C: "DomainAnnouncement", + 0x0D: "MasterAnnouncement", + 0x0E: "ResetStateRequest", + 0x0F: "LocalMasterAnnouncement", + }), + ] + + def mysummary(self): + return self.sprintf("%OpCode%") + + registered_opcodes = {} + + @classmethod + def register_variant(cls): + cls.registered_opcodes[cls.OpCode.default] = cls + + @classmethod + def dispatch_hook(cls, _pkt=None, *args, **kargs): + if _pkt: + return cls.registered_opcodes.get(_pkt[0], cls) + return cls + + def default_payload_class(self, payload): + return conf.padding_layer + + +# [MS-BRWS] sect 2.2.1 + +class BRWS_HostAnnouncement(BRWS): + OpCode = 0x01 + fields_desc = [ + BRWS, + ByteField("UpdateCount", 0), + LEIntField("Periodicity", 128000), + StrFixedLenField("ServerName", b"", length=16), + ByteField("OSVersionMajor", 6), + ByteField("OSVersionMinor", 1), + LEIntField("ServerType", 4611), + ByteField("BrowserConfigVersionMajor", 21), + ByteField("BrowserConfigVersionMinor", 1), + XLEShortField("Signature", 0xAA55), + StrNullField("Comment", ""), + ] + + def mysummary(self): + return self.sprintf("%OpCode% for %ServerName%") + + +# [MS-BRWS] sect 2.2.6 + +class BRWS_BecomeBackup(BRWS): + OpCode = 0x0B + fields_desc = [ + BRWS, + StrNullField("BrowserToPromote", b""), + ] + + def mysummary(self): + return self.sprintf("%OpCode% from %BrowserToPromote%") + + +# [MS-BRWS] sect 2.2.10 + +class BRWS_LocalMasterAnnouncement(BRWS_HostAnnouncement): + OpCode = 0x0F + + # SMB dispatcher diff --git a/test/scapy/layers/smb.uts b/test/scapy/layers/smb.uts index 879551032c2..81d0426883a 100644 --- a/test/scapy/layers/smb.uts +++ b/test/scapy/layers/smb.uts @@ -148,3 +148,58 @@ assert smb_sax_resp_2.SecurityBlob.token.negResult == 0 assert smb_sax_resp_2.SecurityBlob.token.mechListMIC.value.val == b'\x01\x00\x00\x00\xee\t\x91S\xab\x7f]\xe6\x00\x00\x00\x00' assert smb_sax_resp_2.NativeOS == 'Windows 8.1 9600' assert smb_sax_resp_2.NativeLanMan == 'Windows 8.1 6.3' + + ++ Test BRWS + += BRWS BecomeBackup - build + +pkt = \ + IP(id=3109, ttl=128, src='192.168.1.2', dst='192.168.1.255') / \ + UDP(sport=138, dport=138) / \ + NBTDatagram(Type=17, Flags=2, ID=37087, SourceIP='192.168.1.2', + SourcePort=138, SourceName=b'VIKRANT-LAPTOP ', + SUFFIX1=16705, DestinationName=b'WORKGROUP', + SUFFIX2=16975) / \ + SMB_Header(Flags=0) / \ + SMBMailslot_Write(Data=BRWS_BecomeBackup(OpCode=11, BrowserToPromote='LENOVO-NETBOOK'), + Timeout=1000, Name='\\MAILSLOT\\BROWSE') + + +assert bytes(pkt) == b'E\x00\x00\xd4\x0c%\x00\x00\x80\x11\xa9\xa2\xc0\xa8\x01\x02\xc0\xa8\x01\xff\x00\x8a\x00\x8a\x00\xc0\xca)\x11\x02\x90\xdf\xc0\xa8\x01\x02\x00\x8a\x00\xaa\x00\x00 FGEJELFCEBEOFECNEMEBFAFEEPFACAAA\x00 FHEPFCELEHFCEPFFFACACACACACACABO\x00\xffSMB%\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00V\x00\x03\x00\x01\x00\x01\x00\x02\x00!\x00\\MAILSLOT\\BROWSE\x00\x0bLENOVO-NETBOOK\x00' + += BRWS BecomeBackup - dissection + +pkt = IP(b'E\x00\x00\xd4\x0c%\x00\x00\x80\x11\xa9\xa2\xc0\xa8\x01\x02\xc0\xa8\x01\xff\x00\x8a\x00\x8a\x00\xc0\xca)\x11\x02\x90\xdf\xc0\xa8\x01\x02\x00\x8a\x00\xaa\x00\x00 FGEJELFCEBEOFECNEMEBFAFEEPFACAAA\x00 FHEPFCELEHFCEPFFFACACACACACACABO\x00\xffSMB%\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00V\x00\x03\x00\x01\x00\x01\x00\x02\x00!\x00\\MAILSLOT\\BROWSE\x00\x0bLENOVO-NETBOOK\x00') + +assert SMBMailslot_Write in pkt +assert pkt[SMBMailslot_Write].Timeout == 1000 +assert pkt[SMBMailslot_Write].Name == b"\\MAILSLOT\\BROWSE" +assert pkt[SMBMailslot_Write].Data.BrowserToPromote == b'LENOVO-NETBOOK' + += BRWS HostAnnouncement - build + +pkt = \ + IP(id=51657, tos=0x20, src='192.168.1.8', dst='192.168.1.255') / \ + UDP(sport=138, dport=138) / \ + NBTDatagram(Type=17, Flags=2, ID=18755, SourceIP='192.168.1.8', + SourcePort=0, SourceName='MACBOOKPRO-199C', + SUFFIX1=16705, DestinationName='WORKGROUP', + SUFFIX2=16974) / \ + SMB_Header(Flags=0, PIDLow=176, MID=18754) / \ + SMBMailslot_Write(Data=BRWS_HostAnnouncement(ServerName="MACBOOKPRO-122A", Comment="Super's MacBook Pro"), + Timeout=0, Flags=2, Name='\\MAILSLOT\\BROWSE') + + +assert bytes(pkt) == b"E \x00\xf8\xc9\xc9\x00\x00@\x11+\xb4\xc0\xa8\x01\x08\xc0\xa8\x01\xff\x00\x8a\x00\x8a\x00\xe4\xb3\xb0\x11\x02IC\xc0\xa8\x01\x08\x00\x00\x00\xce\x00\x00 ENEBEDECEPEPELFAFCEPCNDBDJDJEDAA\x00 FHEPFCELEHFCEPFFFACACACACACACABN\x00\xffSMB%\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\x00\x00\x00BI\x11\x00\x004\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\x00V\x00\x03\x00\x01\x00\x01\x00\x02\x00E\x00\\MAILSLOT\\BROWSE\x00\x01\x00\x00\xf4\x01\x00MACBOOKPRO-122A\x00\x06\x01\x03\x12\x00\x00\x15\x01U\xaaSuper's MacBook Pro\x00" + += BRWS HostAnnouncement - dissection + +pkt = IP(b"E \x00\xf8\xc9\xc9\x00\x00@\x11+\xb4\xc0\xa8\x01\x08\xc0\xa8\x01\xff\x00\x8a\x00\x8a\x00\xe4\xb3\xb0\x11\x02IC\xc0\xa8\x01\x08\x00\x00\x00\xce\x00\x00 ENEBEDECEPEPELFAFCEPCNDBDJDJEDAA\x00 FHEPFCELEHFCEPFFFACACACACACACABN\x00\xffSMB%\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\x00\x00\x00BI\x11\x00\x004\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\x00V\x00\x03\x00\x01\x00\x01\x00\x02\x00E\x00\\MAILSLOT\\BROWSE\x00\x01\x00\x00\xf4\x01\x00MACBOOKPRO-122A\x00\x06\x01\x03\x12\x00\x00\x15\x01U\xaaSuper's MacBook Pro\x00") + +assert SMBMailslot_Write in pkt +assert pkt[SMBMailslot_Write].Name == b"\\MAILSLOT\\BROWSE" +assert pkt[SMBMailslot_Write].Data.OpCode == 1 +assert pkt[SMBMailslot_Write].Data.ServerName == b"MACBOOKPRO-122A\x00" +assert pkt[SMBMailslot_Write].Data.Comment == b"Super's MacBook Pro" +assert pkt[SMBMailslot_Write].Data.Signature == 0xAA55