1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Philippe Biondi <phil@secdev.org>
5# Copyright (C) Gabriel Potter
6
7"""
8SMB 1.0 (Server Message Block), also known as CIFS.
9
10.. note::
11 You will find more complete documentation for this layer over at
12 `SMB <https://scapy.readthedocs.io/en/latest/layers/smb.html>`_
13
14Specs:
15
16- [MS-CIFS] (base)
17- [MS-SMB] (extension of CIFS - SMB v1)
18"""
19
20import struct
21
22from scapy.config import conf
23from scapy.packet import Packet, bind_layers, bind_top_down
24from scapy.fields import (
25 ByteEnumField,
26 ByteField,
27 ConditionalField,
28 FieldLenField,
29 FieldListField,
30 FlagsField,
31 IPField,
32 LEFieldLenField,
33 LEIntEnumField,
34 LEIntField,
35 LELongField,
36 LEShortEnumField,
37 LEShortField,
38 MultipleTypeField,
39 PacketField,
40 PacketLenField,
41 PacketListField,
42 ReversePadField,
43 ScalingField,
44 ShortField,
45 StrFixedLenField,
46 StrNullField,
47 StrNullFieldUtf16,
48 UTCTimeField,
49 UUIDField,
50 XLEShortField,
51 XStrLenField,
52)
53
54from scapy.layers.dns import (
55 DNSStrField,
56 DNSCompressedPacket,
57)
58from scapy.layers.ntlm import (
59 _NTLMPayloadPacket,
60 _NTLMPayloadField,
61 _NTLM_ENUM,
62 _NTLM_post_build,
63)
64from scapy.layers.netbios import NBTSession, NBTDatagram
65from scapy.layers.gssapi import (
66 GSSAPI_BLOB,
67)
68from scapy.layers.smb2 import (
69 STATUS_ERREF,
70 SMB2_Compression_Transform_Header,
71 SMB2_Header,
72 SMB2_Transform_Header,
73)
74
75
76SMB_COM = {
77 0x00: "SMB_COM_CREATE_DIRECTORY",
78 0x01: "SMB_COM_DELETE_DIRECTORY",
79 0x02: "SMB_COM_OPEN",
80 0x03: "SMB_COM_CREATE",
81 0x04: "SMB_COM_CLOSE",
82 0x05: "SMB_COM_FLUSH",
83 0x06: "SMB_COM_DELETE",
84 0x07: "SMB_COM_RENAME",
85 0x08: "SMB_COM_QUERY_INFORMATION",
86 0x09: "SMB_COM_SET_INFORMATION",
87 0x0A: "SMB_COM_READ",
88 0x0B: "SMB_COM_WRITE",
89 0x0C: "SMB_COM_LOCK_BYTE_RANGE",
90 0x0D: "SMB_COM_UNLOCK_BYTE_RANGE",
91 0x0E: "SMB_COM_CREATE_TEMPORARY",
92 0x0F: "SMB_COM_CREATE_NEW",
93 0x10: "SMB_COM_CHECK_DIRECTORY",
94 0x11: "SMB_COM_PROCESS_EXIT",
95 0x12: "SMB_COM_SEEK",
96 0x13: "SMB_COM_LOCK_AND_READ",
97 0x14: "SMB_COM_WRITE_AND_UNLOCK",
98 0x1A: "SMB_COM_READ_RAW",
99 0x1B: "SMB_COM_READ_MPX",
100 0x1C: "SMB_COM_READ_MPX_SECONDARY",
101 0x1D: "SMB_COM_WRITE_RAW",
102 0x1E: "SMB_COM_WRITE_MPX",
103 0x1F: "SMB_COM_WRITE_MPX_SECONDARY",
104 0x20: "SMB_COM_WRITE_COMPLETE",
105 0x21: "SMB_COM_QUERY_SERVER",
106 0x22: "SMB_COM_SET_INFORMATION2",
107 0x23: "SMB_COM_QUERY_INFORMATION2",
108 0x24: "SMB_COM_LOCKING_ANDX",
109 0x25: "SMB_COM_TRANSACTION",
110 0x26: "SMB_COM_TRANSACTION_SECONDARY",
111 0x27: "SMB_COM_IOCTL",
112 0x28: "SMB_COM_IOCTL_SECONDARY",
113 0x29: "SMB_COM_COPY",
114 0x2A: "SMB_COM_MOVE",
115 0x2B: "SMB_COM_ECHO",
116 0x2C: "SMB_COM_WRITE_AND_CLOSE",
117 0x2D: "SMB_COM_OPEN_ANDX",
118 0x2E: "SMB_COM_READ_ANDX",
119 0x2F: "SMB_COM_WRITE_ANDX",
120 0x30: "SMB_COM_NEW_FILE_SIZE",
121 0x31: "SMB_COM_CLOSE_AND_TREE_DISC",
122 0x32: "SMB_COM_TRANSACTION2",
123 0x33: "SMB_COM_TRANSACTION2_SECONDARY",
124 0x34: "SMB_COM_FIND_CLOSE2",
125 0x35: "SMB_COM_FIND_NOTIFY_CLOSE",
126 0x70: "SMB_COM_TREE_CONNECT",
127 0x71: "SMB_COM_TREE_DISCONNECT",
128 0x72: "SMB_COM_NEGOTIATE",
129 0x73: "SMB_COM_SESSION_SETUP_ANDX",
130 0x74: "SMB_COM_LOGOFF_ANDX",
131 0x75: "SMB_COM_TREE_CONNECT_ANDX",
132 0x7E: "SMB_COM_SECURITY_PACKAGE_ANDX",
133 0x80: "SMB_COM_QUERY_INFORMATION_DISK",
134 0x81: "SMB_COM_SEARCH",
135 0x82: "SMB_COM_FIND",
136 0x83: "SMB_COM_FIND_UNIQUE",
137 0x84: "SMB_COM_FIND_CLOSE",
138 0xA0: "SMB_COM_NT_TRANSACT",
139 0xA1: "SMB_COM_NT_TRANSACT_SECONDARY",
140 0xA2: "SMB_COM_NT_CREATE_ANDX",
141 0xA4: "SMB_COM_NT_CANCEL",
142 0xA5: "SMB_COM_NT_RENAME",
143 0xC0: "SMB_COM_OPEN_PRINT_FILE",
144 0xC1: "SMB_COM_WRITE_PRINT_FILE",
145 0xC2: "SMB_COM_CLOSE_PRINT_FILE",
146 0xC3: "SMB_COM_GET_PRINT_QUEUE",
147 0xD8: "SMB_COM_READ_BULK",
148 0xD9: "SMB_COM_WRITE_BULK",
149 0xDA: "SMB_COM_WRITE_BULK_DATA",
150 0xFE: "SMB_COM_INVALID",
151 0xFF: "SMB_COM_NO_ANDX_COMMAND",
152}
153
154
155class SMB_Header(Packet):
156 name = "SMB 1 Protocol Request Header"
157 fields_desc = [
158 StrFixedLenField("Start", b"\xffSMB", 4),
159 ByteEnumField("Command", 0x72, SMB_COM),
160 LEIntEnumField("Status", 0, STATUS_ERREF),
161 FlagsField(
162 "Flags",
163 0x18,
164 8,
165 [
166 "LOCK_AND_READ_OK",
167 "BUF_AVAIL",
168 "res",
169 "CASE_INSENSITIVE",
170 "CANONICALIZED_PATHS",
171 "OPLOCK",
172 "OPBATCH",
173 "REPLY",
174 ],
175 ),
176 FlagsField(
177 "Flags2",
178 0x0000,
179 -16,
180 [
181 "LONG_NAMES",
182 "EAS",
183 "SMB_SECURITY_SIGNATURE",
184 "COMPRESSED",
185 "SMB_SECURITY_SIGNATURE_REQUIRED",
186 "res",
187 "IS_LONG_NAME",
188 "res",
189 "res",
190 "res",
191 "REPARSE_PATH",
192 "EXTENDED_SECURITY",
193 "DFS",
194 "PAGING_IO",
195 "NT_STATUS",
196 "UNICODE",
197 ],
198 ),
199 LEShortField("PIDHigh", 0x0000),
200 StrFixedLenField("SecuritySignature", b"", length=8),
201 LEShortField("Reserved", 0x0),
202 LEShortField("TID", 0),
203 LEShortField("PIDLow", 0),
204 LEShortField("UID", 0),
205 LEShortField("MID", 0),
206 ]
207
208 def guess_payload_class(self, payload):
209 # type: (bytes) -> Packet
210 if not payload:
211 return super(SMB_Header, self).guess_payload_class(payload)
212 WordCount = ord(payload[:1])
213 if self.Command == 0x72:
214 if self.Flags.REPLY:
215 if self.Flags2.EXTENDED_SECURITY:
216 return SMBNegotiate_Response_Extended_Security
217 else:
218 return SMBNegotiate_Response_Security
219 else:
220 return SMBNegotiate_Request
221 elif self.Command == 0x73:
222 if WordCount == 0:
223 return SMBSession_Null
224 if self.Flags.REPLY:
225 if WordCount == 0x04:
226 return SMBSession_Setup_AndX_Response_Extended_Security
227 elif WordCount == 0x03:
228 return SMBSession_Setup_AndX_Response
229 if self.Flags2.EXTENDED_SECURITY:
230 return SMBSession_Setup_AndX_Response_Extended_Security
231 else:
232 return SMBSession_Setup_AndX_Response
233 else:
234 if WordCount == 0x0C:
235 return SMBSession_Setup_AndX_Request_Extended_Security
236 elif WordCount == 0x0D:
237 return SMBSession_Setup_AndX_Request
238 if self.Flags2.EXTENDED_SECURITY:
239 return SMBSession_Setup_AndX_Request_Extended_Security
240 else:
241 return SMBSession_Setup_AndX_Request
242 elif self.Command == 0x25:
243 if self.Flags.REPLY:
244 if WordCount == 0x11:
245 return SMBMailslot_Write
246 else:
247 return SMBTransaction_Response
248 else:
249 if WordCount == 0x11:
250 return SMBMailslot_Write
251 else:
252 return SMBTransaction_Request
253 return super(SMB_Header, self).guess_payload_class(payload)
254
255 def answers(self, pkt):
256 return SMB_Header in pkt
257
258
259# SMB Negotiate Request
260
261
262class SMB_Dialect(Packet):
263 name = "SMB Dialect"
264 fields_desc = [
265 ByteField("BufferFormat", 0x02),
266 StrNullField("DialectString", "NT LM 0.12"),
267 ]
268
269 def default_payload_class(self, payload):
270 return conf.padding_layer
271
272
273class SMBNegotiate_Request(Packet):
274 name = "SMB Negotiate Request"
275 fields_desc = [
276 ByteField("WordCount", 0),
277 LEFieldLenField("ByteCount", None, length_of="Dialects"),
278 PacketListField(
279 "Dialects",
280 [SMB_Dialect()],
281 SMB_Dialect,
282 length_from=lambda pkt: pkt.ByteCount,
283 ),
284 ]
285
286
287bind_layers(SMB_Header, SMBNegotiate_Request, Command=0x72)
288
289# SMBNegociate Protocol Response
290
291
292def _SMBStrNullField(name, default):
293 """
294 Returns a StrNullField that is either normal or UTF-16 depending
295 on the SMB headers.
296 """
297
298 def _isUTF16(pkt):
299 while not hasattr(pkt, "Flags2") and pkt.underlayer:
300 pkt = pkt.underlayer
301 return hasattr(pkt, "Flags2") and pkt.Flags2.UNICODE
302
303 return MultipleTypeField(
304 [(StrNullFieldUtf16(name, default), _isUTF16)],
305 StrNullField(name, default),
306 )
307
308
309def _len(pkt, name):
310 """
311 Returns the length of a field, works with Unicode strings.
312 """
313 fld, v = pkt.getfield_and_val(name)
314 return len(fld.addfield(pkt, v, b""))
315
316
317class _SMBNegotiate_Response(Packet):
318 @classmethod
319 def dispatch_hook(cls, _pkt=None, *args, **kargs):
320 if _pkt and len(_pkt) >= 2:
321 # Yes this is inspired by
322 # https://github.com/wireshark/wireshark/blob/925e01b23fd5aad2fa929fafd894128a88832e74/epan/dissectors/packet-smb.c#L2902
323 wc = struct.unpack("<H", _pkt[:1])
324 # dialect = struct.unpack("<H", _pkt[1:3])
325 if wc == 1:
326 # Core Protocol
327 return SMBNegotiate_Response_NoSecurity
328 elif wc == 0xD:
329 # LAN Manager 1.0 - LAN Manager 2.1
330 # TODO
331 pass
332 elif wc == 0x11:
333 # NT LAN Manager
334 return cls
335 return cls
336
337
338_SMB_ServerCapabilities = [
339 "RAW_MODE",
340 "MPX_MODE",
341 "UNICODE",
342 "LARGE_FILES",
343 "NT_SMBS",
344 "RPC_REMOTE_APIS",
345 "STATUS32",
346 "LEVEL_II_OPLOCKS",
347 "LOCK_AND_READ",
348 "NT_FIND",
349 "res",
350 "res",
351 "DFS",
352 "INFOLEVEL_PASSTHRU",
353 "LARGE_READX",
354 "LARGE_WRITEX",
355 "LWIO",
356 "res",
357 "res",
358 "res",
359 "res",
360 "res",
361 "res",
362 "UNIX",
363 "res",
364 "COMPRESSED_DATA",
365 "res",
366 "res",
367 "res",
368 "DYNAMIC_REAUTH",
369 "PERSISTENT_HANDLES",
370 "EXTENDED_SECURITY",
371]
372
373
374# CIFS sect 2.2.4.52.2
375
376
377class SMBNegotiate_Response_NoSecurity(_SMBNegotiate_Response):
378 name = "SMB Negotiate No-Security Response (CIFS)"
379 fields_desc = [
380 ByteField("WordCount", 0x1),
381 LEShortField("DialectIndex", 7),
382 FlagsField(
383 "SecurityMode",
384 0x03,
385 8,
386 [
387 "USER_SECURITY",
388 "ENCRYPT_PASSWORDS",
389 "SECURITY_SIGNATURES_ENABLED",
390 "SECURITY_SIGNATURES_REQUIRED",
391 ],
392 ),
393 LEShortField("MaxMpxCount", 50),
394 LEShortField("MaxNumberVC", 1),
395 LEIntField("MaxBufferSize", 16144), # Windows: 4356
396 LEIntField("MaxRawSize", 65536),
397 LEIntField("SessionKey", 0x0000),
398 FlagsField("ServerCapabilities", 0xF3F9, -32, _SMB_ServerCapabilities),
399 UTCTimeField(
400 "ServerTime",
401 None,
402 fmt="<Q",
403 epoch=[1601, 1, 1, 0, 0, 0],
404 custom_scaling=1e7,
405 ),
406 ScalingField("ServerTimeZone", 0x3C, fmt="<h", unit="min-UTC"),
407 FieldLenField(
408 "ChallengeLength",
409 None,
410 # aka EncryptionKeyLength
411 length_of="Challenge",
412 fmt="<B",
413 ),
414 LEFieldLenField(
415 "ByteCount",
416 None,
417 length_of="DomainName",
418 adjust=lambda pkt, x: x + len(pkt.Challenge),
419 ),
420 XStrLenField(
421 "Challenge",
422 b"", # aka EncryptionKey
423 length_from=lambda pkt: pkt.ChallengeLength,
424 ),
425 StrNullField("DomainName", "WORKGROUP"),
426 ]
427
428
429bind_top_down(SMB_Header, SMBNegotiate_Response_NoSecurity, Command=0x72, Flags=0x80)
430
431# SMB sect 2.2.4.5.2.1
432
433
434class SMBNegotiate_Response_Extended_Security(_SMBNegotiate_Response):
435 name = "SMB Negotiate Extended Security Response (SMB)"
436 WordCount = 0x11
437 fields_desc = SMBNegotiate_Response_NoSecurity.fields_desc[:12] + [
438 LEFieldLenField(
439 "ByteCount", None, length_of="SecurityBlob", adjust=lambda _, x: x + 16
440 ),
441 SMBNegotiate_Response_NoSecurity.fields_desc[13],
442 UUIDField("GUID", None, uuid_fmt=UUIDField.FORMAT_LE),
443 PacketLenField(
444 "SecurityBlob", None, GSSAPI_BLOB, length_from=lambda x: x.ByteCount - 16
445 ),
446 ]
447
448
449bind_top_down(
450 SMB_Header,
451 SMBNegotiate_Response_Extended_Security,
452 Command=0x72,
453 Flags=0x80,
454 Flags2=0x800,
455)
456
457# SMB sect 2.2.4.5.2.2
458
459
460class SMBNegotiate_Response_Security(_SMBNegotiate_Response):
461 name = "SMB Negotiate Non-Extended Security Response (SMB)"
462 WordCount = 0x11
463 fields_desc = SMBNegotiate_Response_NoSecurity.fields_desc[:12] + [
464 LEFieldLenField(
465 "ByteCount",
466 None,
467 length_of="DomainName",
468 adjust=lambda pkt, x: x
469 + 2
470 + _len(pkt, "Challenge")
471 + _len(pkt, "ServerName"),
472 ),
473 XStrLenField(
474 "Challenge",
475 b"", # aka EncryptionKey
476 length_from=lambda pkt: pkt.ChallengeLength,
477 ),
478 _SMBStrNullField("DomainName", "WORKGROUP"),
479 _SMBStrNullField("ServerName", "RMFF1"),
480 ]
481
482
483bind_top_down(SMB_Header, SMBNegotiate_Response_Security, Command=0x72, Flags=0x80)
484
485# Session Setup AndX Request
486
487# CIFS sect 2.2.4.53
488
489
490class SMBSession_Setup_AndX_Request(Packet):
491 name = "Session Setup AndX Request (CIFS)"
492 fields_desc = [
493 ByteField("WordCount", 0x0D),
494 ByteEnumField("AndXCommand", 0xFF, SMB_COM),
495 ByteField("AndXReserved", 0),
496 LEShortField("AndXOffset", None),
497 LEShortField("MaxBufferSize", 16144), # Windows: 4356
498 LEShortField("MaxMPXCount", 50),
499 LEShortField("VCNumber", 0),
500 LEIntField("SessionKey", 0),
501 LEFieldLenField("OEMPasswordLength", None, length_of="OEMPassword"),
502 LEFieldLenField("UnicodePasswordLength", None, length_of="UnicodePassword"),
503 LEIntField("Reserved", 0),
504 FlagsField("ServerCapabilities", 0x05, -32, _SMB_ServerCapabilities),
505 LEShortField("ByteCount", None),
506 XStrLenField("OEMPassword", "Pass", length_from=lambda x: x.OEMPasswordLength),
507 XStrLenField(
508 "UnicodePassword", "Pass", length_from=lambda x: x.UnicodePasswordLength
509 ),
510 ReversePadField(_SMBStrNullField("AccountName", "GUEST"), 2, b"\0"),
511 _SMBStrNullField("PrimaryDomain", ""),
512 _SMBStrNullField("NativeOS", "Windows 4.0"),
513 _SMBStrNullField("NativeLanMan", "Windows 4.0"),
514 ]
515
516 def post_build(self, pkt, pay):
517 if self.AndXOffset is None and self.AndXCommand != 0xFF:
518 pkt = pkt[:3] + struct.pack("<H", len(pkt) + 32) + pkt[5:]
519 if self.ByteCount is None:
520 pkt = pkt[:27] + struct.pack("<H", len(pkt) - 29) + pkt[29:]
521 if self.payload and hasattr(self.payload, "AndXOffset") and pay:
522 pay = pay[:3] + struct.pack("<H", len(pkt) + len(pay) + 32) + pay[5:]
523 return pkt + pay
524
525
526bind_top_down(SMB_Header, SMBSession_Setup_AndX_Request, Command=0x73)
527
528# SMB sect 2.2.4.7
529
530
531class SMBTree_Connect_AndX(Packet):
532 name = "Session Tree Connect AndX"
533 WordCount = 0x04
534 fields_desc = SMBSession_Setup_AndX_Request.fields_desc[:4] + [
535 FlagsField(
536 "Flags",
537 "",
538 -16,
539 ["DISCONNECT_TID", "r2", "EXTENDED_SIGNATURES", "EXTENDED_RESPONSE"],
540 ),
541 FieldLenField("PasswordLength", None, length_of="Password", fmt="<H"),
542 LEShortField("ByteCount", None),
543 XStrLenField("Password", b"", length_from=lambda pkt: pkt.PasswordLength),
544 ReversePadField(_SMBStrNullField("Path", "\\\\WIN2K\\IPC$"), 2),
545 StrNullField("Service", "?????"),
546 ]
547
548 def post_build(self, pkt, pay):
549 pkt += pay
550 if self.ByteCount is None:
551 pkt = pkt[:9] + struct.pack("<H", len(pkt) - 11) + pkt[11:]
552 return pkt
553
554
555bind_layers(SMB_Header, SMBTree_Connect_AndX, Command=0x75)
556bind_layers(SMBSession_Setup_AndX_Request, SMBTree_Connect_AndX, AndXCommand=0x75)
557
558# SMB sect 2.2.4.6.1
559
560
561class SMBSession_Setup_AndX_Request_Extended_Security(Packet):
562 name = "Session Setup AndX Extended Security Request (SMB)"
563 WordCount = 0x0C
564 fields_desc = (
565 SMBSession_Setup_AndX_Request.fields_desc[:8]
566 + [
567 LEFieldLenField("SecurityBlobLength", None, length_of="SecurityBlob"),
568 ]
569 + SMBSession_Setup_AndX_Request.fields_desc[10:12]
570 + [
571 LEShortField("ByteCount", None),
572 PacketLenField(
573 "SecurityBlob",
574 None,
575 GSSAPI_BLOB,
576 length_from=lambda x: x.SecurityBlobLength,
577 ),
578 ReversePadField(
579 _SMBStrNullField("NativeOS", "Windows 4.0"),
580 2,
581 b"\0",
582 ),
583 _SMBStrNullField("NativeLanMan", "Windows 4.0"),
584 ]
585 )
586
587 def post_build(self, pkt, pay):
588 if self.ByteCount is None:
589 pkt = pkt[:25] + struct.pack("<H", len(pkt) - 27) + pkt[27:]
590 return pkt + pay
591
592
593bind_top_down(
594 SMB_Header,
595 SMBSession_Setup_AndX_Request_Extended_Security,
596 Command=0x73,
597 Flags2=0x800,
598)
599
600# Session Setup AndX Response
601
602
603# CIFS sect 2.2.4.53.2
604
605
606class SMBSession_Setup_AndX_Response(Packet):
607 name = "Session Setup AndX Response (CIFS)"
608 fields_desc = [
609 ByteField("WordCount", 0x3),
610 ByteEnumField("AndXCommand", 0xFF, SMB_COM),
611 ByteField("AndXReserved", 0),
612 LEShortField("AndXOffset", None),
613 FlagsField(
614 "Action",
615 0,
616 -16,
617 {
618 0x0001: "SMB_SETUP_GUEST",
619 0x0002: "SMB_SETUP_USE_LANMAN_KEY",
620 },
621 ),
622 LEShortField("ByteCount", 25),
623 _SMBStrNullField("NativeOS", "Windows 4.0"),
624 _SMBStrNullField("NativeLanMan", "Windows 4.0"),
625 _SMBStrNullField("PrimaryDomain", ""),
626 # Off spec?
627 ByteField("WordCount2", 3),
628 ByteEnumField("AndXCommand2", 0xFF, SMB_COM),
629 ByteField("Reserved3", 0),
630 LEShortField("AndXOffset2", 80),
631 LEShortField("OptionalSupport", 0x01),
632 LEShortField("ByteCount2", 5),
633 StrNullField("Service", "IPC"),
634 StrNullField("NativeFileSystem", ""),
635 ]
636
637 def post_build(self, pkt, pay):
638 if self.AndXOffset is None:
639 pkt = pkt[:3] + struct.pack("<H", len(pkt) + 32) + pkt[5:]
640 return pkt + pay
641
642
643bind_top_down(SMB_Header, SMBSession_Setup_AndX_Response, Command=0x73, Flags=0x80)
644
645# SMB sect 2.2.4.6.2
646
647
648class SMBSession_Setup_AndX_Response_Extended_Security(
649 SMBSession_Setup_AndX_Response
650): # noqa: E501
651 name = "Session Setup AndX Extended Security Response (SMB)"
652 WordCount = 0x4
653 fields_desc = (
654 SMBSession_Setup_AndX_Response.fields_desc[:5]
655 + [SMBSession_Setup_AndX_Request_Extended_Security.fields_desc[8]]
656 + SMBSession_Setup_AndX_Request_Extended_Security.fields_desc[11:]
657 )
658
659 def post_build(self, pkt, pay):
660 if self.ByteCount is None:
661 pkt = pkt[:9] + struct.pack("<H", len(pkt) - 11) + pkt[11:]
662 return super(SMBSession_Setup_AndX_Response_Extended_Security, self).post_build(
663 pkt, pay
664 )
665
666
667bind_top_down(
668 SMB_Header,
669 SMBSession_Setup_AndX_Response_Extended_Security,
670 Command=0x73,
671 Flags=0x80,
672 Flags2=0x800,
673)
674
675# SMB null (no wordcount)
676
677
678class SMBSession_Null(Packet):
679 fields_desc = [ByteField("WordCount", 0), LEShortField("ByteCount", 0)]
680
681
682bind_top_down(SMB_Header, SMBSession_Null, Command=0x73)
683
684# [MS-CIFS] sect 2.2.4.33.1
685
686_SMB_CONFIG = [
687 ("Len", _NTLM_ENUM.LEN),
688 ("BufferOffset", _NTLM_ENUM.OFFSET),
689]
690
691
692class _SMB_TransactionRequest_Data(PacketLenField):
693 def m2i(self, pkt, m):
694 if pkt.Name == b"\\MAILSLOT\\NET\\NETLOGON":
695 return NETLOGON(m)
696 elif pkt.Name == b"\\MAILSLOT\\BROWSE" or pkt.name == b"\\MAILSLOT\\LANMAN":
697 return BRWS(m)
698 return conf.raw_layer(m)
699
700
701def _optlen(pkt, x):
702 try:
703 return len(getattr(pkt, x))
704 except AttributeError:
705 return 0
706
707
708class SMBTransaction_Request(_NTLMPayloadPacket):
709 name = "SMB COM Transaction Request"
710 _NTLM_PAYLOAD_FIELD_NAME = "Buffer"
711
712 fields_desc = [
713 FieldLenField(
714 "WordCount",
715 None,
716 length_of="SetupCount",
717 adjust=lambda pkt, x: x + 0x0E,
718 fmt="B",
719 ),
720 FieldLenField(
721 "TotalParamCount",
722 None,
723 length_of="Buffer",
724 fmt="<H",
725 adjust=lambda pkt, _: _optlen(pkt, "Parameter"),
726 ),
727 FieldLenField(
728 "TotalDataCount",
729 None,
730 length_of="Buffer",
731 fmt="<H",
732 adjust=lambda pkt, _: _optlen(pkt, "Data"),
733 ),
734 LEShortField("MaxParamCount", 0),
735 LEShortField("MaxDataCount", 0),
736 ByteField("MaxSetupCount", 0),
737 ByteField("Reserved1", 0),
738 FlagsField("Flags", 0, -16, {0x1: "DISCONNECT_TID", 0x2: "NO_RESPONSE"}),
739 LEIntField("Timeout", 1000),
740 ShortField("Reserved2", 0),
741 LEShortField("ParameterLen", None),
742 LEShortField("ParameterBufferOffset", None),
743 LEShortField("DataLen", None),
744 LEShortField("DataBufferOffset", None),
745 FieldLenField("SetupCount", 3, count_of="Setup", fmt="B"),
746 ByteField("Reserved3", 0),
747 FieldListField(
748 "Setup",
749 [1, 1, 2],
750 LEShortField("", 0),
751 count_from=lambda pkt: pkt.SetupCount,
752 ),
753 # SMB Data
754 FieldLenField(
755 "ByteCount",
756 None,
757 length_of="Name",
758 fmt="<H",
759 adjust=lambda pkt, x: x + _optlen(pkt, "Parameter") + _optlen(pkt, "Data"),
760 ),
761 StrNullField("Name", "\\MAILSLOT\\NET\\NETLOGON"),
762 _NTLMPayloadField(
763 "Buffer",
764 lambda pkt: 32 + 31 + len(pkt.Setup) * 2 + len(pkt.Name) + 1,
765 [
766 XStrLenField(
767 "Parameter", b"", length_from=lambda pkt: pkt.ParameterLen
768 ),
769 _SMB_TransactionRequest_Data(
770 "Data", None, conf.raw_layer, length_from=lambda pkt: pkt.DataLen
771 ),
772 ],
773 ),
774 ]
775
776 def post_build(self, pkt, pay):
777 # type: (bytes, bytes) -> bytes
778 return (
779 _NTLM_post_build(
780 self,
781 pkt,
782 32 + 31 + len(self.Setup) * 2 + len(self.Name) + 1,
783 {
784 "Parameter": 19,
785 "Data": 23,
786 },
787 config=_SMB_CONFIG,
788 )
789 + pay
790 )
791
792 def mysummary(self):
793 if getattr(self, "Data", None) is not None:
794 return self.sprintf("Tran %Name% ") + self.Data.mysummary()
795 return self.sprintf("Tran %Name%")
796
797
798bind_top_down(SMB_Header, SMBTransaction_Request, Command=0x25)
799
800
801class SMBMailslot_Write(SMBTransaction_Request):
802 WordCount = 0x11
803
804
805# [MS-CIFS] sect 2.2.4.33.2
806
807
808class SMBTransaction_Response(_NTLMPayloadPacket):
809 name = "SMB COM Transaction Response"
810 _NTLM_PAYLOAD_FIELD_NAME = "Buffer"
811 fields_desc = [
812 FieldLenField(
813 "WordCount",
814 None,
815 length_of="SetupCount",
816 adjust=lambda pkt, x: x + 0x0A,
817 fmt="B",
818 ),
819 FieldLenField(
820 "TotalParamCount",
821 None,
822 length_of="Buffer",
823 fmt="<H",
824 adjust=lambda pkt, _: _optlen(pkt, "Parameter"),
825 ),
826 FieldLenField(
827 "TotalDataCount",
828 None,
829 length_of="Buffer",
830 fmt="<H",
831 adjust=lambda pkt, _: _optlen(pkt, "Data"),
832 ),
833 LEShortField("Reserved1", None),
834 LEShortField("ParameterLen", None),
835 LEShortField("ParameterBufferOffset", None),
836 LEShortField("ParameterDisplacement", 0),
837 LEShortField("DataLen", None),
838 LEShortField("DataBufferOffset", None),
839 LEShortField("DataDisplacement", 0),
840 FieldLenField("SetupCount", 3, count_of="Setup", fmt="B"),
841 ByteField("Reserved2", 0),
842 FieldListField(
843 "Setup",
844 [1, 1, 2],
845 LEShortField("", 0),
846 count_from=lambda pkt: pkt.SetupCount,
847 ),
848 # SMB Data
849 FieldLenField(
850 "ByteCount",
851 None,
852 length_of="Buffer",
853 fmt="<H",
854 adjust=lambda pkt, x: _optlen(pkt, "Parameter") + _optlen(pkt, "Data"),
855 ),
856 _NTLMPayloadField(
857 "Buffer",
858 lambda pkt: 32 + 22 + len(pkt.Setup) * 2,
859 [
860 XStrLenField(
861 "Parameter", b"", length_from=lambda pkt: pkt.ParameterLen
862 ),
863 XStrLenField("Data", b"", length_from=lambda pkt: pkt.DataLen),
864 ],
865 ),
866 ]
867
868 def post_build(self, pkt, pay):
869 # type: (bytes, bytes) -> bytes
870 return (
871 _NTLM_post_build(
872 self,
873 pkt,
874 32 + 22 + len(self.Setup) * 2,
875 {
876 "Parameter": 7,
877 "Data": 13,
878 },
879 config=_SMB_CONFIG,
880 )
881 + pay
882 )
883
884
885bind_top_down(SMB_Header, SMBTransaction_Response, Command=0x25, Flags=0x80)
886
887
888# [MS-ADTS] sect 6.3.1.4
889
890_NETLOGON_opcodes = {
891 0x7: "LOGON_PRIMARY_QUERY",
892 0x12: "LOGON_SAM_LOGON_REQUEST",
893 0x13: "LOGON_SAM_LOGON_RESPONSE",
894 0x15: "LOGON_SAM_USER_UNKNOWN",
895 0x17: "LOGON_SAM_LOGON_RESPONSE_EX",
896 0x19: "LOGON_SAM_USER_UNKNOWN_EX",
897}
898
899_NV_VERSION = {
900 0x00000001: "V1",
901 0x00000002: "V5",
902 0x00000004: "V5EX",
903 0x00000008: "V5EX_WITH_IP",
904 0x00000010: "V5EX_WITH_CLOSEST_SITE",
905 0x01000000: "AVOID_NT4EMUL",
906 0x10000000: "PDC",
907 0x20000000: "IP",
908 0x40000000: "LOCAL",
909 0x80000000: "GC",
910}
911
912
913class NETLOGON(Packet):
914 @classmethod
915 def dispatch_hook(cls, _pkt=None, *args, **kargs):
916 if _pkt:
917 if _pkt[0] == 0x07: # LOGON_PRIMARY_QUERY
918 return NETLOGON_LOGON_QUERY
919 elif _pkt[0] == 0x12: # LOGON_SAM_LOGON_REQUEST
920 return NETLOGON_SAM_LOGON_REQUEST
921 elif _pkt[0] == 0x13: # LOGON_SAM_USER_RESPONSE
922 try:
923 i = _pkt.index(b"\xff\xff\xff\xff")
924 NtVersion = NETLOGON_SAM_LOGON_RESPONSE_NT40.fields_desc[
925 -3
926 ].getfield(None, _pkt[i - 4 : i])[1]
927 if NtVersion.V1 and not NtVersion.V5:
928 return NETLOGON_SAM_LOGON_RESPONSE_NT40
929 except Exception:
930 pass
931 return NETLOGON_SAM_LOGON_RESPONSE
932 elif _pkt[0] == 0x15: # LOGON_SAM_USER_UNKNOWN
933 return NETLOGON_SAM_LOGON_RESPONSE
934 elif _pkt[0] == 0x17: # LOGON_SAM_LOGON_RESPONSE_EX
935 return NETLOGON_SAM_LOGON_RESPONSE_EX
936 elif _pkt[0] == 0x19: # LOGON_SAM_USER_UNKNOWN_EX
937 return NETLOGON_SAM_LOGON_RESPONSE
938 return cls
939
940
941class NETLOGON_LOGON_QUERY(NETLOGON):
942 fields_desc = [
943 LEShortEnumField("OpCode", 0x7, _NETLOGON_opcodes),
944 StrNullField("ComputerName", ""),
945 StrNullField("MailslotName", ""),
946 StrNullFieldUtf16("UnicodeComputerName", ""),
947 FlagsField("NtVersion", 0xB, -32, _NV_VERSION),
948 XLEShortField("LmNtToken", 0xFFFF),
949 XLEShortField("Lm20Token", 0xFFFF),
950 ]
951
952
953# [MS-ADTS] sect 6.3.1.6
954
955
956class NETLOGON_SAM_LOGON_REQUEST(NETLOGON):
957 fields_desc = [
958 LEShortEnumField("OpCode", 0x12, _NETLOGON_opcodes),
959 LEShortField("RequestCount", 0),
960 StrNullFieldUtf16("UnicodeComputerName", ""),
961 StrNullFieldUtf16("UnicodeUserName", ""),
962 StrNullField("MailslotName", "\\MAILSLOT\\NET\\GETDC701253F9"),
963 LEIntField("AllowableAccountControlBits", 0),
964 FieldLenField("DomainSidSize", None, fmt="<I", length_of="DomainSid"),
965 XStrLenField("DomainSid", b"", length_from=lambda pkt: pkt.DomainSidSize),
966 FlagsField("NtVersion", 0xB, -32, _NV_VERSION),
967 XLEShortField("LmNtToken", 0xFFFF),
968 XLEShortField("Lm20Token", 0xFFFF),
969 ]
970
971
972# [MS-ADTS] sect 6.3.1.7
973
974
975class NETLOGON_SAM_LOGON_RESPONSE_NT40(NETLOGON):
976 fields_desc = [
977 LEShortEnumField("OpCode", 0x13, _NETLOGON_opcodes),
978 StrNullFieldUtf16("UnicodeLogonServer", ""),
979 StrNullFieldUtf16("UnicodeUserName", ""),
980 StrNullFieldUtf16("UnicodeDomainName", ""),
981 FlagsField("NtVersion", 0x1, -32, _NV_VERSION),
982 XLEShortField("LmNtToken", 0xFFFF),
983 XLEShortField("Lm20Token", 0xFFFF),
984 ]
985
986
987# [MS-ADTS] sect 6.3.1.2
988
989
990_NETLOGON_FLAGS = {
991 0x00000001: "PDC",
992 0x00000004: "GC",
993 0x00000008: "LDAP",
994 0x00000010: "DC",
995 0x00000020: "KDC",
996 0x00000040: "TIMESERV",
997 0x00000080: "CLOSEST",
998 0x00000100: "RODC",
999 0x00000200: "GOOD_TIMESERV",
1000 0x00000400: "NC",
1001 0x00000800: "SELECT_SECRET_DOMAIN_6",
1002 0x00001000: "FULL_SECRET_DOMAIN_6",
1003 0x00002000: "WS",
1004 0x00004000: "DS_8",
1005 0x00008000: "DS_9",
1006 0x00010000: "DS_10", # guess
1007 0x00020000: "DS_11", # guess
1008 0x20000000: "DNS_CONTROLLER",
1009 0x40000000: "DNS_DOMAIN",
1010 0x80000000: "DNS_FOREST",
1011}
1012
1013
1014# [MS-ADTS] sect 6.3.1.8
1015
1016
1017class NETLOGON_SAM_LOGON_RESPONSE(NETLOGON, DNSCompressedPacket):
1018 fields_desc = [
1019 LEShortEnumField("OpCode", 0x17, _NETLOGON_opcodes),
1020 StrNullFieldUtf16("UnicodeLogonServer", ""),
1021 StrNullFieldUtf16("UnicodeUserName", ""),
1022 StrNullFieldUtf16("UnicodeDomainName", ""),
1023 UUIDField("DomainGuid", None, uuid_fmt=UUIDField.FORMAT_LE),
1024 UUIDField("NullGuid", None, uuid_fmt=UUIDField.FORMAT_LE),
1025 DNSStrField("DnsForestName", ""),
1026 DNSStrField("DnsDomainName", ""),
1027 DNSStrField("DnsHostName", ""),
1028 IPField("DcIpAddress", "0.0.0.0"),
1029 FlagsField("Flags", 0, -32, _NETLOGON_FLAGS),
1030 FlagsField("NtVersion", 0x1, -32, _NV_VERSION),
1031 XLEShortField("LmNtToken", 0xFFFF),
1032 XLEShortField("Lm20Token", 0xFFFF),
1033 ]
1034
1035 def get_full(self):
1036 return self.original
1037
1038
1039# [MS-ADTS] sect 6.3.1.9
1040
1041
1042class DcSockAddr(Packet):
1043 fields_desc = [
1044 LEShortField("sin_family", 2),
1045 LEShortField("sin_port", 0),
1046 IPField("sin_addr", None),
1047 LELongField("sin_zero", 0),
1048 ]
1049
1050 def default_payload_class(self, payload):
1051 return conf.padding_layer
1052
1053
1054class NETLOGON_SAM_LOGON_RESPONSE_EX(NETLOGON, DNSCompressedPacket):
1055 fields_desc = [
1056 LEShortEnumField("OpCode", 0x17, _NETLOGON_opcodes),
1057 LEShortField("Sbz", 0),
1058 FlagsField("Flags", 0, -32, _NETLOGON_FLAGS),
1059 UUIDField("DomainGuid", None, uuid_fmt=UUIDField.FORMAT_LE),
1060 DNSStrField("DnsForestName", ""),
1061 DNSStrField("DnsDomainName", ""),
1062 DNSStrField("DnsHostName", ""),
1063 DNSStrField("NetbiosDomainName", ""),
1064 DNSStrField("NetbiosComputerName", ""),
1065 DNSStrField("UserName", ""),
1066 DNSStrField("DcSiteName", "Default-First-Site-Name"),
1067 DNSStrField("ClientSiteName", "Default-First-Site-Name"),
1068 ConditionalField(
1069 ByteField("DcSockAddrSize", 0x10),
1070 lambda pkt: pkt.NtVersion.V5EX_WITH_IP,
1071 ),
1072 ConditionalField(
1073 PacketField("DcSockAddr", DcSockAddr(), DcSockAddr),
1074 lambda pkt: pkt.NtVersion.V5EX_WITH_IP,
1075 ),
1076 ConditionalField(
1077 DNSStrField("NextClosestSiteName", ""),
1078 lambda pkt: pkt.NtVersion.V5EX_WITH_CLOSEST_SITE,
1079 ),
1080 FlagsField("NtVersion", 0xB, -32, _NV_VERSION),
1081 XLEShortField("LmNtToken", 0xFFFF),
1082 XLEShortField("Lm20Token", 0xFFFF),
1083 ]
1084
1085 def pre_dissect(self, s):
1086 try:
1087 i = s.index(b"\xff\xff\xff\xff")
1088 self.fields["NtVersion"] = self.fields_desc[-3].getfield(
1089 self, s[i - 4 : i]
1090 )[1]
1091 except Exception:
1092 self.NtVersion = 0xB
1093 return s
1094
1095 def get_full(self):
1096 return self.original
1097
1098
1099# [MS-BRWS] sect 2.2
1100
1101
1102class BRWS(Packet):
1103 fields_desc = [
1104 ByteEnumField(
1105 "OpCode",
1106 0x00,
1107 {
1108 0x01: "HostAnnouncement",
1109 0x02: "AnnouncementRequest",
1110 0x08: "RequestElection",
1111 0x09: "GetBackupListRequest",
1112 0x0A: "GetBackupListResponse",
1113 0x0B: "BecomeBackup",
1114 0x0C: "DomainAnnouncement",
1115 0x0D: "MasterAnnouncement",
1116 0x0E: "ResetStateRequest",
1117 0x0F: "LocalMasterAnnouncement",
1118 },
1119 ),
1120 ]
1121
1122 def mysummary(self):
1123 return self.sprintf("%OpCode%")
1124
1125 registered_opcodes = {}
1126
1127 @classmethod
1128 def register_variant(cls):
1129 cls.registered_opcodes[cls.OpCode.default] = cls
1130
1131 @classmethod
1132 def dispatch_hook(cls, _pkt=None, *args, **kargs):
1133 if _pkt:
1134 return cls.registered_opcodes.get(_pkt[0], cls)
1135 return cls
1136
1137 def default_payload_class(self, payload):
1138 return conf.padding_layer
1139
1140
1141# [MS-BRWS] sect 2.2.1
1142
1143
1144class BRWS_HostAnnouncement(BRWS):
1145 OpCode = 0x01
1146 fields_desc = [
1147 BRWS,
1148 ByteField("UpdateCount", 0),
1149 LEIntField("Periodicity", 128000),
1150 StrFixedLenField("ServerName", b"", length=16),
1151 ByteField("OSVersionMajor", 6),
1152 ByteField("OSVersionMinor", 1),
1153 LEIntField("ServerType", 4611),
1154 ByteField("BrowserConfigVersionMajor", 21),
1155 ByteField("BrowserConfigVersionMinor", 1),
1156 XLEShortField("Signature", 0xAA55),
1157 StrNullField("Comment", ""),
1158 ]
1159
1160 def mysummary(self):
1161 return self.sprintf("%OpCode% for %ServerName%")
1162
1163
1164# [MS-BRWS] sect 2.2.6
1165
1166
1167class BRWS_BecomeBackup(BRWS):
1168 OpCode = 0x0B
1169 fields_desc = [
1170 BRWS,
1171 StrNullField("BrowserToPromote", b""),
1172 ]
1173
1174 def mysummary(self):
1175 return self.sprintf("%OpCode% from %BrowserToPromote%")
1176
1177
1178# [MS-BRWS] sect 2.2.10
1179
1180
1181class BRWS_LocalMasterAnnouncement(BRWS_HostAnnouncement):
1182 OpCode = 0x0F
1183
1184
1185# SMB dispatcher
1186
1187
1188class _SMBGeneric(Packet):
1189 name = "SMB Generic dispatcher"
1190 fields_desc = [StrFixedLenField("Start", b"\xffSMB", 4)]
1191
1192 @classmethod
1193 def dispatch_hook(cls, _pkt=None, *args, **kargs):
1194 """
1195 Depending on the first 4 bytes of the packet,
1196 dispatch to the correct version of Header
1197 (either SMB or SMB2)
1198 """
1199 if _pkt and len(_pkt) >= 4:
1200 if _pkt[:4] == b"\xffSMB":
1201 return SMB_Header
1202 if _pkt[:4] == b"\xfeSMB":
1203 return SMB2_Header
1204 if _pkt[:4] == b"\xfdSMB":
1205 return SMB2_Transform_Header
1206 if _pkt[:4] == b"\xfcSMB":
1207 return SMB2_Compression_Transform_Header
1208 return cls
1209
1210
1211bind_layers(NBTSession, _SMBGeneric)
1212bind_layers(NBTDatagram, _SMBGeneric)