# SPDX-License-Identifier: GPL-2.0-only # This file is part of Scapy # See https://scapy.net/ for more information # Copyright (C) Philippe Biondi # Copyright (C) Gabriel Potter """ SMB 1.0 (Server Message Block), also known as CIFS. .. note:: You will find more complete documentation for this layer over at `SMB `_ Specs: - [MS-CIFS] (base) - [MS-SMB] (extension of CIFS - SMB v1) """ import struct from scapy.config import conf from scapy.packet import Packet, bind_layers, bind_top_down from scapy.fields import ( ByteEnumField, ByteField, ConditionalField, FieldLenField, FieldListField, FlagsField, IPField, LEFieldLenField, LEIntEnumField, LEIntField, LELongField, LEShortEnumField, LEShortField, MultipleTypeField, PacketField, PacketLenField, PacketListField, ReversePadField, ScalingField, ShortField, StrFixedLenField, StrNullField, StrNullFieldUtf16, UTCTimeField, UUIDField, XLEShortField, XStrLenField, ) from scapy.layers.dns import ( DNSStrField, DNSCompressedPacket, ) from scapy.layers.ntlm import ( _NTLMPayloadPacket, _NTLMPayloadField, _NTLM_ENUM, _NTLM_post_build, ) from scapy.layers.netbios import NBTSession, NBTDatagram from scapy.layers.gssapi import ( GSSAPI_BLOB, ) from scapy.layers.smb2 import ( STATUS_ERREF, SMB2_Header, ) SMB_COM = { 0x00: "SMB_COM_CREATE_DIRECTORY", 0x01: "SMB_COM_DELETE_DIRECTORY", 0x02: "SMB_COM_OPEN", 0x03: "SMB_COM_CREATE", 0x04: "SMB_COM_CLOSE", 0x05: "SMB_COM_FLUSH", 0x06: "SMB_COM_DELETE", 0x07: "SMB_COM_RENAME", 0x08: "SMB_COM_QUERY_INFORMATION", 0x09: "SMB_COM_SET_INFORMATION", 0x0A: "SMB_COM_READ", 0x0B: "SMB_COM_WRITE", 0x0C: "SMB_COM_LOCK_BYTE_RANGE", 0x0D: "SMB_COM_UNLOCK_BYTE_RANGE", 0x0E: "SMB_COM_CREATE_TEMPORARY", 0x0F: "SMB_COM_CREATE_NEW", 0x10: "SMB_COM_CHECK_DIRECTORY", 0x11: "SMB_COM_PROCESS_EXIT", 0x12: "SMB_COM_SEEK", 0x13: "SMB_COM_LOCK_AND_READ", 0x14: "SMB_COM_WRITE_AND_UNLOCK", 0x1A: "SMB_COM_READ_RAW", 0x1B: "SMB_COM_READ_MPX", 0x1C: "SMB_COM_READ_MPX_SECONDARY", 0x1D: "SMB_COM_WRITE_RAW", 0x1E: "SMB_COM_WRITE_MPX", 0x1F: "SMB_COM_WRITE_MPX_SECONDARY", 0x20: "SMB_COM_WRITE_COMPLETE", 0x21: "SMB_COM_QUERY_SERVER", 0x22: "SMB_COM_SET_INFORMATION2", 0x23: "SMB_COM_QUERY_INFORMATION2", 0x24: "SMB_COM_LOCKING_ANDX", 0x25: "SMB_COM_TRANSACTION", 0x26: "SMB_COM_TRANSACTION_SECONDARY", 0x27: "SMB_COM_IOCTL", 0x28: "SMB_COM_IOCTL_SECONDARY", 0x29: "SMB_COM_COPY", 0x2A: "SMB_COM_MOVE", 0x2B: "SMB_COM_ECHO", 0x2C: "SMB_COM_WRITE_AND_CLOSE", 0x2D: "SMB_COM_OPEN_ANDX", 0x2E: "SMB_COM_READ_ANDX", 0x2F: "SMB_COM_WRITE_ANDX", 0x30: "SMB_COM_NEW_FILE_SIZE", 0x31: "SMB_COM_CLOSE_AND_TREE_DISC", 0x32: "SMB_COM_TRANSACTION2", 0x33: "SMB_COM_TRANSACTION2_SECONDARY", 0x34: "SMB_COM_FIND_CLOSE2", 0x35: "SMB_COM_FIND_NOTIFY_CLOSE", 0x70: "SMB_COM_TREE_CONNECT", 0x71: "SMB_COM_TREE_DISCONNECT", 0x72: "SMB_COM_NEGOTIATE", 0x73: "SMB_COM_SESSION_SETUP_ANDX", 0x74: "SMB_COM_LOGOFF_ANDX", 0x75: "SMB_COM_TREE_CONNECT_ANDX", 0x7E: "SMB_COM_SECURITY_PACKAGE_ANDX", 0x80: "SMB_COM_QUERY_INFORMATION_DISK", 0x81: "SMB_COM_SEARCH", 0x82: "SMB_COM_FIND", 0x83: "SMB_COM_FIND_UNIQUE", 0x84: "SMB_COM_FIND_CLOSE", 0xA0: "SMB_COM_NT_TRANSACT", 0xA1: "SMB_COM_NT_TRANSACT_SECONDARY", 0xA2: "SMB_COM_NT_CREATE_ANDX", 0xA4: "SMB_COM_NT_CANCEL", 0xA5: "SMB_COM_NT_RENAME", 0xC0: "SMB_COM_OPEN_PRINT_FILE", 0xC1: "SMB_COM_WRITE_PRINT_FILE", 0xC2: "SMB_COM_CLOSE_PRINT_FILE", 0xC3: "SMB_COM_GET_PRINT_QUEUE", 0xD8: "SMB_COM_READ_BULK", 0xD9: "SMB_COM_WRITE_BULK", 0xDA: "SMB_COM_WRITE_BULK_DATA", 0xFE: "SMB_COM_INVALID", 0xFF: "SMB_COM_NO_ANDX_COMMAND", } class SMB_Header(Packet): name = "SMB 1 Protocol Request Header" fields_desc = [ StrFixedLenField("Start", b"\xffSMB", 4), ByteEnumField("Command", 0x72, SMB_COM), LEIntEnumField("Status", 0, STATUS_ERREF), FlagsField( "Flags", 0x18, 8, [ "LOCK_AND_READ_OK", "BUF_AVAIL", "res", "CASE_INSENSITIVE", "CANONICALIZED_PATHS", "OPLOCK", "OPBATCH", "REPLY", ], ), FlagsField( "Flags2", 0x0000, -16, [ "LONG_NAMES", "EAS", "SMB_SECURITY_SIGNATURE", "COMPRESSED", "SMB_SECURITY_SIGNATURE_REQUIRED", "res", "IS_LONG_NAME", "res", "res", "res", "REPARSE_PATH", "EXTENDED_SECURITY", "DFS", "PAGING_IO", "NT_STATUS", "UNICODE", ], ), LEShortField("PIDHigh", 0x0000), StrFixedLenField("SecuritySignature", b"", length=8), LEShortField("Reserved", 0x0), LEShortField("TID", 0), LEShortField("PIDLow", 0), LEShortField("UID", 0), LEShortField("MID", 0), ] def guess_payload_class(self, payload): # type: (bytes) -> Packet if not payload: return super(SMB_Header, self).guess_payload_class(payload) WordCount = ord(payload[:1]) if self.Command == 0x72: if self.Flags.REPLY: if self.Flags2.EXTENDED_SECURITY: return SMBNegotiate_Response_Extended_Security else: return SMBNegotiate_Response_Security else: return SMBNegotiate_Request elif self.Command == 0x73: if WordCount == 0: return SMBSession_Null if self.Flags.REPLY: if WordCount == 0x04: return SMBSession_Setup_AndX_Response_Extended_Security elif WordCount == 0x03: return SMBSession_Setup_AndX_Response if self.Flags2.EXTENDED_SECURITY: return SMBSession_Setup_AndX_Response_Extended_Security else: return SMBSession_Setup_AndX_Response else: if WordCount == 0x0C: return SMBSession_Setup_AndX_Request_Extended_Security elif WordCount == 0x0D: return SMBSession_Setup_AndX_Request if self.Flags2.EXTENDED_SECURITY: return SMBSession_Setup_AndX_Request_Extended_Security else: return SMBSession_Setup_AndX_Request elif self.Command == 0x25: if self.Flags.REPLY: if WordCount == 0x11: return SMBMailslot_Write else: return SMBTransaction_Response else: if WordCount == 0x11: return SMBMailslot_Write else: return SMBTransaction_Request return super(SMB_Header, self).guess_payload_class(payload) def answers(self, pkt): return SMB_Header in pkt # SMB Negotiate Request class SMB_Dialect(Packet): name = "SMB Dialect" fields_desc = [ ByteField("BufferFormat", 0x02), StrNullField("DialectString", "NT LM 0.12"), ] def default_payload_class(self, payload): return conf.padding_layer class SMBNegotiate_Request(Packet): name = "SMB Negotiate Request" fields_desc = [ ByteField("WordCount", 0), LEFieldLenField("ByteCount", None, length_of="Dialects"), PacketListField( "Dialects", [SMB_Dialect()], SMB_Dialect, length_from=lambda pkt: pkt.ByteCount, ), ] bind_layers(SMB_Header, SMBNegotiate_Request, Command=0x72) # SMBNegociate Protocol Response def _SMBStrNullField(name, default): """ Returns a StrNullField that is either normal or UTF-16 depending on the SMB headers. """ def _isUTF16(pkt): while not hasattr(pkt, "Flags2") and pkt.underlayer: pkt = pkt.underlayer return hasattr(pkt, "Flags2") and pkt.Flags2.UNICODE return MultipleTypeField( [(StrNullFieldUtf16(name, default), _isUTF16)], StrNullField(name, default), ) def _len(pkt, name): """ Returns the length of a field, works with Unicode strings. """ fld, v = pkt.getfield_and_val(name) return len(fld.addfield(pkt, v, b"")) class _SMBNegotiate_Response(Packet): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: # Yes this is inspired by # https://github.com/wireshark/wireshark/blob/925e01b23fd5aad2fa929fafd894128a88832e74/epan/dissectors/packet-smb.c#L2902 wc = struct.unpack(" bytes return ( _NTLM_post_build( self, pkt, 32 + 31 + len(self.Setup) * 2 + len(self.Name) + 1, { "Parameter": 19, "Data": 23, }, config=_SMB_CONFIG, ) + pay ) def mysummary(self): if getattr(self, "Data", None) is not None: return self.sprintf("Tran %Name% ") + self.Data.mysummary() return self.sprintf("Tran %Name%") bind_top_down(SMB_Header, SMBTransaction_Request, Command=0x25) class SMBMailslot_Write(SMBTransaction_Request): WordCount = 0x11 # [MS-CIFS] sect 2.2.4.33.2 class SMBTransaction_Response(_NTLMPayloadPacket): name = "SMB COM Transaction Response" _NTLM_PAYLOAD_FIELD_NAME = "Buffer" fields_desc = [ FieldLenField( "WordCount", None, length_of="SetupCount", adjust=lambda pkt, x: x + 0x0A, fmt="B", ), FieldLenField( "TotalParamCount", None, length_of="Buffer", fmt=" bytes return ( _NTLM_post_build( self, pkt, 32 + 22 + len(self.Setup) * 2, { "Parameter": 7, "Data": 13, }, config=_SMB_CONFIG, ) + pay ) bind_top_down(SMB_Header, SMBTransaction_Response, Command=0x25, Flags=0x80) # [MS-ADTS] sect 6.3.1.4 _NETLOGON_opcodes = { 0x7: "LOGON_PRIMARY_QUERY", 0x12: "LOGON_SAM_LOGON_REQUEST", 0x13: "LOGON_SAM_LOGON_RESPONSE", 0x15: "LOGON_SAM_USER_UNKNOWN", 0x17: "LOGON_SAM_LOGON_RESPONSE_EX", 0x19: "LOGON_SAM_USER_UNKNOWN_EX", } _NV_VERSION = { 0x00000001: "V1", 0x00000002: "V5", 0x00000004: "V5EX", 0x00000008: "V5EX_WITH_IP", 0x00000010: "V5EX_WITH_CLOSEST_SITE", 0x01000000: "AVOID_NT4EMUL", 0x10000000: "PDC", 0x20000000: "IP", 0x40000000: "LOCAL", 0x80000000: "GC", } class NETLOGON(Packet): @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: if _pkt[0] == 0x07: # LOGON_PRIMARY_QUERY return NETLOGON_LOGON_QUERY elif _pkt[0] == 0x12: # LOGON_SAM_LOGON_REQUEST return NETLOGON_SAM_LOGON_REQUEST elif _pkt[0] == 0x13: # LOGON_SAM_USER_RESPONSE try: i = _pkt.index(b"\xff\xff\xff\xff") NtVersion = ( NETLOGON_SAM_LOGON_RESPONSE_NT40.fields_desc[-3].getfield( None, _pkt[i - 4:i] )[1] ) if NtVersion.V1 and not NtVersion.V5: return NETLOGON_SAM_LOGON_RESPONSE_NT40 except Exception: pass return NETLOGON_SAM_LOGON_RESPONSE elif _pkt[0] == 0x15: # LOGON_SAM_USER_UNKNOWN return NETLOGON_SAM_LOGON_RESPONSE elif _pkt[0] == 0x17: # LOGON_SAM_LOGON_RESPONSE_EX return NETLOGON_SAM_LOGON_RESPONSE_EX elif _pkt[0] == 0x19: # LOGON_SAM_USER_UNKNOWN_EX return NETLOGON_SAM_LOGON_RESPONSE return cls class NETLOGON_LOGON_QUERY(NETLOGON): fields_desc = [ LEShortEnumField("OpCode", 0x7, _NETLOGON_opcodes), StrNullField("ComputerName", ""), StrNullField("MailslotName", ""), StrNullFieldUtf16("UnicodeComputerName", ""), FlagsField("NtVersion", 0xB, -32, _NV_VERSION), XLEShortField("LmNtToken", 0xFFFF), XLEShortField("Lm20Token", 0xFFFF), ] # [MS-ADTS] sect 6.3.1.6 class NETLOGON_SAM_LOGON_REQUEST(NETLOGON): fields_desc = [ LEShortEnumField("OpCode", 0x12, _NETLOGON_opcodes), LEShortField("RequestCount", 0), StrNullFieldUtf16("UnicodeComputerName", ""), StrNullFieldUtf16("UnicodeUserName", ""), StrNullField("MailslotName", "\\MAILSLOT\\NET\\GETDC701253F9"), LEIntField("AllowableAccountControlBits", 0), FieldLenField("DomainSidSize", None, fmt="= 4: if _pkt[:4] == b"\xffSMB": return SMB_Header if _pkt[:4] == b"\xfeSMB": return SMB2_Header return cls bind_layers(NBTSession, _SMBGeneric) bind_layers(NBTDatagram, _SMBGeneric)