Coverage for /root/GitHubProjects/impacket/impacket/smbserver.py : 5%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Impacket - Collection of Python classes for working with network protocols.
2#
3# SECUREAUTH LABS. Copyright (C) 2021 SecureAuth Corporation. All rights reserved.
4#
5# This software is provided under a slightly modified version
6# of the Apache Software License. See the accompanying LICENSE file
7# for more information.
8#
9# Author:
10# Alberto Solino (@agsolino)
11#
12# TODO:
13# [-] Functions should return NT error codes
14# [-] Handling errors in all situations, right now it's just raising exceptions.
15# [*] Standard authentication support
16# [ ] Organize the connectionData stuff
17# [*] Add capability to send a bad user ID if the user is not authenticated,
18# right now you can ask for any command without actually being authenticated
19# [ ] PATH TRAVERSALS EVERYWHERE.. BE WARNED!
20# [ ] Check error situation (now many places assume the right data is coming)
21# [ ] Implement IPC to the main process so the connectionData is on a single place
22# [ ] Hence.. implement locking
23# estamos en la B
25import calendar
26import socket
27import time
28import datetime
29import struct
30import threading
31import logging
32import logging.config
33import ntpath
34import os
35import fnmatch
36import errno
37import sys
38import random
39import shutil
40import string
41import hashlib
42import hmac
44from binascii import unhexlify, hexlify, a2b_hex
45from six import PY2, b, text_type
46from six.moves import configparser, socketserver
48# For signing
49from impacket import smb, nmb, ntlm, uuid
50from impacket import smb3structs as smb2
51from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, MechTypes, SPNEGO_NegTokenResp, ASN1_AID, \
52 ASN1_SUPPORTED_MECH
53from impacket.nt_errors import STATUS_NO_MORE_FILES, STATUS_NETWORK_NAME_DELETED, STATUS_INVALID_PARAMETER, \
54 STATUS_FILE_CLOSED, STATUS_MORE_PROCESSING_REQUIRED, STATUS_OBJECT_PATH_NOT_FOUND, STATUS_DIRECTORY_NOT_EMPTY, \
55 STATUS_FILE_IS_A_DIRECTORY, STATUS_NOT_IMPLEMENTED, STATUS_INVALID_HANDLE, STATUS_OBJECT_NAME_COLLISION, \
56 STATUS_NO_SUCH_FILE, STATUS_CANCELLED, STATUS_OBJECT_NAME_NOT_FOUND, STATUS_SUCCESS, STATUS_ACCESS_DENIED, \
57 STATUS_NOT_SUPPORTED, STATUS_INVALID_DEVICE_REQUEST, STATUS_FS_DRIVER_REQUIRED, STATUS_INVALID_INFO_CLASS, \
58 STATUS_LOGON_FAILURE, STATUS_OBJECT_PATH_SYNTAX_BAD
60# Setting LOG to current's module name
61LOG = logging.getLogger(__name__)
63# These ones not defined in nt_errors
64STATUS_SMB_BAD_UID = 0x005B0002
65STATUS_SMB_BAD_TID = 0x00050002
68# Utility functions
69# and general functions.
70# There are some common functions that can be accessed from more than one SMB
71# command (or either TRANSACTION). That's why I'm putting them here
72# TODO: Return NT ERROR Codes
74def computeNTLMv2(identity, lmhash, nthash, serverChallenge, authenticateMessage, ntlmChallenge, type1):
75 # Let's calculate the NTLMv2 Response
77 responseKeyNT = ntlm.NTOWFv2(identity, '', authenticateMessage['domain_name'].decode('utf-16le'), nthash)
78 responseKeyLM = ntlm.LMOWFv2(identity, '', authenticateMessage['domain_name'].decode('utf-16le'), lmhash)
80 ntProofStr = authenticateMessage['ntlm'][:16]
81 temp = authenticateMessage['ntlm'][16:]
82 ntProofStr2 = ntlm.hmac_md5(responseKeyNT, serverChallenge + temp)
83 lmChallengeResponse = authenticateMessage['lanman']
84 sessionBaseKey = ntlm.hmac_md5(responseKeyNT, ntProofStr)
86 responseFlags = type1['flags']
88 # Let's check the return flags
89 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) == 0:
90 # No extended session security, taking it out
91 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
92 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_128) == 0:
93 # No support for 128 key len, taking it out
94 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_128
95 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH) == 0:
96 # No key exchange supported, taking it out
97 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
98 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_SEAL) == 0:
99 # No sign available, taking it out
100 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_SEAL
101 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_SIGN) == 0:
102 # No sign available, taking it out
103 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_SIGN
104 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN) == 0:
105 # No sign available, taking it out
106 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN
108 keyExchangeKey = ntlm.KXKEY(ntlmChallenge['flags'], sessionBaseKey, lmChallengeResponse,
109 ntlmChallenge['challenge'], '',
110 lmhash, nthash, True)
112 # If we set up key exchange, let's fill the right variables
113 if ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
114 exportedSessionKey = authenticateMessage['session_key']
115 exportedSessionKey = ntlm.generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey)
116 else:
117 encryptedRandomSessionKey = None
118 # [MS-NLMP] page 46
119 exportedSessionKey = keyExchangeKey
121 # Do they match?
122 if ntProofStr == ntProofStr2:
123 # Yes!, process login
124 return STATUS_SUCCESS, exportedSessionKey
125 else:
126 return STATUS_LOGON_FAILURE, exportedSessionKey
129def outputToJohnFormat(challenge, username, domain, lmresponse, ntresponse):
130 # We don't want to add a possible failure here, since this is an
131 # extra bonus. We try, if it fails, returns nothing
132 # ToDo: Document the parameter's types (bytes / string) and check all the places where it's called
133 ret_value = ''
134 if type(challenge) is not bytes:
135 challenge = challenge.decode('latin-1')
137 try:
138 if len(ntresponse) > 24:
139 # Extended Security - NTLMv2
140 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (
141 username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(challenge).decode('latin-1'),
142 hexlify(ntresponse).decode('latin-1')[:32],
143 hexlify(ntresponse).decode()[32:]), 'hash_version': 'ntlmv2'}
144 else:
145 # NTLMv1
146 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (
147 username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(lmresponse).decode('latin-1'),
148 hexlify(ntresponse).decode('latin-1'),
149 hexlify(challenge).decode()), 'hash_version': 'ntlm'}
150 except:
151 # Let's try w/o decoding Unicode
152 try:
153 if len(ntresponse) > 24:
154 # Extended Security - NTLMv2
155 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (
156 username.decode('latin-1'), domain.decode('latin-1'), hexlify(challenge).decode('latin-1'),
157 hexlify(ntresponse)[:32].decode('latin-1'), hexlify(ntresponse)[32:].decode('latin-1')),
158 'hash_version': 'ntlmv2'}
159 else:
160 # NTLMv1
161 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (
162 username, domain, hexlify(lmresponse).decode('latin-1'), hexlify(ntresponse).decode('latin-1'),
163 hexlify(challenge).decode('latin-1')), 'hash_version': 'ntlm'}
164 except Exception as e:
165 import traceback
166 traceback.print_exc()
167 LOG.error("outputToJohnFormat: %s" % e)
168 pass
170 return ret_value
173def writeJohnOutputToFile(hash_string, hash_version, file_name):
174 fn_data = os.path.splitext(file_name)
175 if hash_version == "ntlmv2":
176 output_filename = fn_data[0] + "_ntlmv2" + fn_data[1]
177 else:
178 output_filename = fn_data[0] + "_ntlm" + fn_data[1]
180 with open(output_filename, "a") as f:
181 f.write(hash_string)
182 f.write('\n')
185def decodeSMBString(flags, text):
186 if flags & smb.SMB.FLAGS2_UNICODE:
187 return text.decode('utf-16le')
188 else:
189 return text
192def encodeSMBString(flags, text):
193 if flags & smb.SMB.FLAGS2_UNICODE:
194 return (text).encode('utf-16le')
195 else:
196 return text.encode('ascii')
199def getFileTime(t):
200 t *= 10000000
201 t += 116444736000000000
202 return t
205def getUnixTime(t):
206 t -= 116444736000000000
207 t //= 10000000
208 return t
211def getSMBDate(t):
212 # TODO: Fix this :P
213 d = datetime.date.fromtimestamp(t)
214 year = d.year - 1980
215 ret = (year << 8) + (d.month << 4) + d.day
216 return ret
219def getSMBTime(t):
220 # TODO: Fix this :P
221 d = datetime.datetime.fromtimestamp(t)
222 return (d.hour << 8) + (d.minute << 4) + d.second
225def getShares(connId, smbServer):
226 config = smbServer.getServerConfig()
227 sections = config.sections()
228 # Remove the global one
229 del (sections[sections.index('global')])
230 shares = {}
231 for i in sections:
232 shares[i] = dict(config.items(i))
233 return shares
236def searchShare(connId, share, smbServer):
237 config = smbServer.getServerConfig()
238 if config.has_section(share):
239 return dict(config.items(share))
240 else:
241 return None
244def normalize_path(file_name, path=None):
245 """Normalizes a path by replacing "\" with "/" and stripping potential
246 leading "/" chars. If a path is provided, only strip leading '/' when
247 the path is empty.
249 :param file_name: file name to normalize
250 :type file_name: string
252 :param path: path to normalize
253 :type path: string
255 :return normalized file name
256 :rtype string
257 """
258 file_name = os.path.normpath(file_name.replace('\\', '/'))
259 if len(file_name) > 0 and (file_name[0] == '/' or file_name[0] == '\\'):
260 if path is None or path != '':
261 # Strip leading "/"
262 file_name = file_name[1:]
263 return file_name
266def isInFileJail(path, file_name):
267 """Validates if a provided file name path is inside a path. This function is used
268 to check for path traversals.
270 :param path: base path to check
271 :type path: string
272 :param file_name: file name to validate
273 :type file_name: string
275 :return whether the file name is inside the base path or not
276 :rtype bool
277 """
278 path_name = os.path.join(path, file_name)
279 share_real_path = os.path.realpath(path)
280 return os.path.commonprefix((os.path.realpath(path_name), share_real_path)) == share_real_path
283def openFile(path, fileName, accessMode, fileAttributes, openMode):
284 fileName = normalize_path(fileName)
285 pathName = os.path.join(path, fileName)
286 errorCode = 0
287 mode = 0
289 if not isInFileJail(path, fileName):
290 LOG.error("Path not in current working directory")
291 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD
292 return 0, mode, pathName, errorCode
294 # Check the Open Mode
295 if openMode & 0x10:
296 # If the file does not exist, create it.
297 mode = os.O_CREAT
298 else:
299 # If file does not exist, return an error
300 if os.path.exists(pathName) is not True:
301 errorCode = STATUS_NO_SUCH_FILE
302 return 0, mode, pathName, errorCode
304 if os.path.isdir(pathName) and (fileAttributes & smb.ATTR_DIRECTORY) == 0:
305 # Request to open a normal file and this is actually a directory
306 errorCode = STATUS_FILE_IS_A_DIRECTORY
307 return 0, mode, pathName, errorCode
308 # Check the Access Mode
309 if accessMode & 0x7 == 1:
310 mode |= os.O_WRONLY
311 elif accessMode & 0x7 == 2:
312 mode |= os.O_RDWR
313 else:
314 mode = os.O_RDONLY
316 try:
317 if sys.platform == 'win32':
318 mode |= os.O_BINARY
319 fid = os.open(pathName, mode)
320 except Exception as e:
321 LOG.error("openFile: %s,%s" % (pathName, mode), e)
322 fid = 0
323 errorCode = STATUS_ACCESS_DENIED
325 return fid, mode, pathName, errorCode
328def queryFsInformation(path, filename, level=None, pktFlags=smb.SMB.FLAGS2_UNICODE):
329 if pktFlags & smb.SMB.FLAGS2_UNICODE:
330 encoding = 'utf-16le'
331 else:
332 encoding = 'ascii'
334 fileName = normalize_path(filename)
335 pathName = os.path.join(path, fileName)
336 fileSize = os.path.getsize(pathName)
337 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)
339 if level is None:
340 lastWriteTime = mtime
341 attribs = 0
342 if os.path.isdir(pathName):
343 attribs |= smb.SMB_FILE_ATTRIBUTE_DIRECTORY
344 if os.path.isfile(pathName):
345 attribs |= smb.SMB_FILE_ATTRIBUTE_NORMAL
346 fileAttributes = attribs
347 return fileSize, lastWriteTime, fileAttributes
349 elif level == smb.SMB_QUERY_FS_ATTRIBUTE_INFO or level == smb2.SMB2_FILESYSTEM_ATTRIBUTE_INFO:
350 data = smb.SMBQueryFsAttributeInfo()
351 data['FileSystemAttributes'] = smb.FILE_CASE_SENSITIVE_SEARCH | smb.FILE_CASE_PRESERVED_NAMES
352 data['MaxFilenNameLengthInBytes'] = 255
353 data['LengthOfFileSystemName'] = len('XTFS') * 2
354 data['FileSystemName'] = 'XTFS'.encode('utf-16le')
355 return data.getData()
356 elif level == smb.SMB_INFO_VOLUME:
357 data = smb.SMBQueryFsInfoVolume(flags=pktFlags)
358 data['VolumeLabel'] = 'SHARE'.encode(encoding)
359 return data.getData()
360 elif level == smb.SMB_QUERY_FS_VOLUME_INFO or level == smb2.SMB2_FILESYSTEM_VOLUME_INFO:
361 data = smb.SMBQueryFsVolumeInfo()
362 data['VolumeLabel'] = ''
363 data['VolumeCreationTime'] = getFileTime(ctime)
364 return data.getData()
365 elif level == smb.SMB_QUERY_FS_SIZE_INFO:
366 data = smb.SMBQueryFsSizeInfo()
367 return data.getData()
368 elif level == smb.SMB_QUERY_FS_DEVICE_INFO or level == smb2.SMB2_FILESYSTEM_DEVICE_INFO:
369 data = smb.SMBQueryFsDeviceInfo()
370 data['DeviceType'] = smb.FILE_DEVICE_DISK
371 return data.getData()
372 elif level == smb.FILE_FS_FULL_SIZE_INFORMATION:
373 data = smb.SMBFileFsFullSizeInformation()
374 return data.getData()
375 elif level == smb.FILE_FS_SIZE_INFORMATION:
376 data = smb.FileFsSizeInformation()
377 return data.getData()
378 else:
379 return None
382def findFirst2(path, fileName, level, searchAttributes, pktFlags=smb.SMB.FLAGS2_UNICODE, isSMB2=False):
383 # TODO: Depending on the level, this could be done much simpler
385 # Let's choose the right encoding depending on the request
386 if pktFlags & smb.SMB.FLAGS2_UNICODE:
387 encoding = 'utf-16le'
388 else:
389 encoding = 'ascii'
391 fileName = normalize_path(fileName)
392 pathName = os.path.join(path, fileName)
394 if not isInFileJail(path, fileName):
395 LOG.error("Path not in current working directory")
396 return [], 0, STATUS_OBJECT_PATH_SYNTAX_BAD
398 files = []
400 if pathName.find('*') == -1 and pathName.find('?') == -1:
401 # No search patterns
402 pattern = ''
403 else:
404 pattern = os.path.basename(pathName)
405 dirName = os.path.dirname(pathName)
407 # Always add . and .. Not that important for Windows, but Samba whines if
408 # not present (for * search only)
409 if pattern == '*':
410 files.append(os.path.join(dirName, '.'))
411 files.append(os.path.join(dirName, '..'))
413 if pattern != '':
414 for file in os.listdir(dirName):
415 if fnmatch.fnmatch(file.lower(), pattern.lower()):
416 entry = os.path.join(dirName, file)
417 if os.path.isdir(entry):
418 if searchAttributes & smb.ATTR_DIRECTORY:
419 files.append(entry)
420 else:
421 files.append(entry)
422 else:
423 if os.path.exists(pathName):
424 files.append(pathName)
426 searchResult = []
427 searchCount = len(files)
428 errorCode = STATUS_SUCCESS
430 for i in files:
431 if level == smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_BOTH_DIRECTORY_INFO:
432 item = smb.SMBFindFileBothDirectoryInfo(flags=pktFlags)
433 elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO or level == smb2.SMB2_FILE_DIRECTORY_INFO:
434 item = smb.SMBFindFileDirectoryInfo(flags=pktFlags)
435 elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO:
436 item = smb.SMBFindFileFullDirectoryInfo(flags=pktFlags)
437 elif level == smb.SMB_FIND_INFO_STANDARD:
438 item = smb.SMBFindInfoStandard(flags=pktFlags)
439 elif level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_FULL_DIRECTORY_INFO:
440 item = smb.SMBFindFileIdFullDirectoryInfo(flags=pktFlags)
441 elif level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO:
442 item = smb.SMBFindFileIdBothDirectoryInfo(flags=pktFlags)
443 elif level == smb.SMB_FIND_FILE_NAMES_INFO or level == smb2.SMB2_FILE_NAMES_INFO:
444 item = smb.SMBFindFileNamesInfo(flags=pktFlags)
445 else:
446 LOG.error("Wrong level %d!" % level)
447 return searchResult, searchCount, STATUS_NOT_SUPPORTED
449 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(i)
450 if os.path.isdir(i):
451 item['ExtFileAttributes'] = smb.ATTR_DIRECTORY
452 else:
453 item['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
455 item['FileName'] = os.path.basename(i).encode(encoding)
457 if level in [smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO, smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO,
458 smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO]:
459 item['EaSize'] = 0
460 item['EndOfFile'] = size
461 item['AllocationSize'] = size
462 item['CreationTime'] = getFileTime(ctime)
463 item['LastAccessTime'] = getFileTime(atime)
464 item['LastWriteTime'] = getFileTime(mtime)
465 item['LastChangeTime'] = getFileTime(mtime)
466 item['ShortName'] = '\x00' * 24
467 item['FileName'] = os.path.basename(i).encode(encoding)
468 padLen = (8 - (len(item) % 8)) % 8
469 item['NextEntryOffset'] = len(item) + padLen
470 elif level in [smb.SMB_FIND_FILE_DIRECTORY_INFO, smb2.SMB2_FILE_DIRECTORY_INFO]:
471 item['EndOfFile'] = size
472 item['AllocationSize'] = size
473 item['CreationTime'] = getFileTime(ctime)
474 item['LastAccessTime'] = getFileTime(atime)
475 item['LastWriteTime'] = getFileTime(mtime)
476 item['LastChangeTime'] = getFileTime(mtime)
477 item['FileName'] = os.path.basename(i).encode(encoding)
478 padLen = (8 - (len(item) % 8)) % 8
479 item['NextEntryOffset'] = len(item) + padLen
480 elif level in [smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO, smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO,
481 smb2.SMB2_FULL_DIRECTORY_INFO, smb2.SMB2_FILE_ID_FULL_DIRECTORY_INFO]:
482 item['EaSize'] = 0
483 item['EndOfFile'] = size
484 item['AllocationSize'] = size
485 item['CreationTime'] = getFileTime(ctime)
486 item['LastAccessTime'] = getFileTime(atime)
487 item['LastWriteTime'] = getFileTime(mtime)
488 item['LastChangeTime'] = getFileTime(mtime)
489 padLen = (8 - (len(item) % 8)) % 8
490 item['NextEntryOffset'] = len(item) + padLen
491 elif level == smb.SMB_FIND_INFO_STANDARD:
492 item['EaSize'] = size
493 item['CreationDate'] = getSMBDate(ctime)
494 item['CreationTime'] = getSMBTime(ctime)
495 item['LastAccessDate'] = getSMBDate(atime)
496 item['LastAccessTime'] = getSMBTime(atime)
497 item['LastWriteDate'] = getSMBDate(mtime)
498 item['LastWriteTime'] = getSMBTime(mtime)
499 searchResult.append(item)
501 # No more files
502 if (level >= smb.SMB_FIND_FILE_DIRECTORY_INFO or isSMB2 is True) and searchCount > 0:
503 searchResult[-1]['NextEntryOffset'] = 0
505 return searchResult, searchCount, errorCode
508def queryFileInformation(path, filename, level):
509 # print "queryFileInfo path: %s, filename: %s, level:0x%x" % (path,filename,level)
510 return queryPathInformation(path, filename, level)
513def queryPathInformation(path, filename, level):
514 # TODO: Depending on the level, this could be done much simpler
515 try:
516 errorCode = 0
517 fileName = normalize_path(filename, path)
518 pathName = os.path.join(path, fileName)
520 if not isInFileJail(path, fileName):
521 LOG.error("Path not in current working directory")
522 return None, STATUS_OBJECT_PATH_SYNTAX_BAD
524 if os.path.exists(pathName):
525 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)
526 if level == smb.SMB_QUERY_FILE_BASIC_INFO:
527 infoRecord = smb.SMBQueryFileBasicInfo()
528 infoRecord['CreationTime'] = getFileTime(ctime)
529 infoRecord['LastAccessTime'] = getFileTime(atime)
530 infoRecord['LastWriteTime'] = getFileTime(mtime)
531 infoRecord['LastChangeTime'] = getFileTime(mtime)
532 if os.path.isdir(pathName):
533 infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY
534 else:
535 infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
536 elif level == smb.SMB_QUERY_FILE_STANDARD_INFO or level == smb2.SMB2_FILE_STANDARD_INFO:
537 infoRecord = smb.SMBQueryFileStandardInfo()
538 infoRecord['AllocationSize'] = size
539 infoRecord['EndOfFile'] = size
540 if os.path.isdir(pathName):
541 infoRecord['Directory'] = 1
542 else:
543 infoRecord['Directory'] = 0
544 elif level == smb.SMB_QUERY_FILE_ALL_INFO:
545 infoRecord = smb.SMBQueryFileAllInfo()
546 infoRecord['CreationTime'] = getFileTime(ctime)
547 infoRecord['LastAccessTime'] = getFileTime(atime)
548 infoRecord['LastWriteTime'] = getFileTime(mtime)
549 infoRecord['LastChangeTime'] = getFileTime(mtime)
550 if os.path.isdir(pathName):
551 infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY
552 else:
553 infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
554 infoRecord['AllocationSize'] = size
555 infoRecord['EndOfFile'] = size
556 if os.path.isdir(pathName):
557 infoRecord['Directory'] = 1
558 else:
559 infoRecord['Directory'] = 0
560 infoRecord['FileName'] = filename.encode('utf-16le')
561 elif level == smb2.SMB2_FILE_ALL_INFO:
562 infoRecord = smb2.FILE_ALL_INFORMATION()
563 infoRecord['BasicInformation'] = smb2.FILE_BASIC_INFORMATION()
564 infoRecord['StandardInformation'] = smb2.FILE_STANDARD_INFORMATION()
565 infoRecord['InternalInformation'] = smb2.FILE_INTERNAL_INFORMATION()
566 infoRecord['EaInformation'] = smb2.FILE_EA_INFORMATION()
567 infoRecord['AccessInformation'] = smb2.FILE_ACCESS_INFORMATION()
568 infoRecord['PositionInformation'] = smb2.FILE_POSITION_INFORMATION()
569 infoRecord['ModeInformation'] = smb2.FILE_MODE_INFORMATION()
570 infoRecord['AlignmentInformation'] = smb2.FILE_ALIGNMENT_INFORMATION()
571 infoRecord['NameInformation'] = smb2.FILE_NAME_INFORMATION()
572 infoRecord['BasicInformation']['CreationTime'] = getFileTime(ctime)
573 infoRecord['BasicInformation']['LastAccessTime'] = getFileTime(atime)
574 infoRecord['BasicInformation']['LastWriteTime'] = getFileTime(mtime)
575 infoRecord['BasicInformation']['ChangeTime'] = getFileTime(mtime)
576 if os.path.isdir(pathName):
577 infoRecord['BasicInformation']['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY
578 infoRecord['StandardInformation']['Directory'] = 1
579 infoRecord['EaInformation']['EaSize'] = smb.ATTR_DIRECTORY
580 else:
581 infoRecord['BasicInformation']['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_NORMAL | smb.SMB_FILE_ATTRIBUTE_ARCHIVE
582 infoRecord['StandardInformation']['Directory'] = 0
583 infoRecord['EaInformation']['EaSize'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
584 infoRecord['StandardInformation']['AllocationSize'] = size
585 infoRecord['StandardInformation']['EndOfFile'] = size
586 infoRecord['StandardInformation']['NumberOfLinks'] = nlink
587 infoRecord['StandardInformation']['DeletePending'] = 0
588 infoRecord['InternalInformation']['IndexNumber'] = ino
589 infoRecord['AccessInformation']['AccessFlags'] = 0 #
590 infoRecord['PositionInformation']['CurrentByteOffset'] = 0 #
591 infoRecord['ModeInformation']['mode'] = mode
592 infoRecord['AlignmentInformation']['AlignmentRequirement'] = 0 #
593 infoRecord['NameInformation']['FileName'] = fileName
594 infoRecord['NameInformation']['FileNameLength'] = len(fileName)
595 elif level == smb2.SMB2_FILE_NETWORK_OPEN_INFO:
596 infoRecord = smb.SMBFileNetworkOpenInfo()
597 infoRecord['CreationTime'] = getFileTime(ctime)
598 infoRecord['LastAccessTime'] = getFileTime(atime)
599 infoRecord['LastWriteTime'] = getFileTime(mtime)
600 infoRecord['ChangeTime'] = getFileTime(mtime)
601 infoRecord['AllocationSize'] = size
602 infoRecord['EndOfFile'] = size
603 if os.path.isdir(pathName):
604 infoRecord['FileAttributes'] = smb.ATTR_DIRECTORY
605 else:
606 infoRecord['FileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
607 elif level == smb.SMB_QUERY_FILE_EA_INFO or level == smb2.SMB2_FILE_EA_INFO:
608 infoRecord = smb.SMBQueryFileEaInfo()
609 elif level == smb2.SMB2_FILE_STREAM_INFO:
610 infoRecord = smb.SMBFileStreamInformation()
611 else:
612 LOG.error('Unknown level for query path info! 0x%x' % level)
613 # UNSUPPORTED
614 return None, STATUS_NOT_SUPPORTED
616 return infoRecord, errorCode
617 else:
618 # NOT FOUND
619 return None, STATUS_OBJECT_NAME_NOT_FOUND
620 except Exception as e:
621 LOG.error('queryPathInfo: %s' % e)
622 raise
625def queryDiskInformation(path):
626 # TODO: Do something useful here :)
627 # For now we just return fake values
628 totalUnits = 65535
629 freeUnits = 65535
630 return totalUnits, freeUnits
633# Here we implement the NT transaction handlers
634class NTTRANSCommands:
635 def default(self, connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
636 pass
639# Here we implement the NT transaction handlers
640class TRANSCommands:
641 @staticmethod
642 def lanMan(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
643 # Minimal [MS-RAP] implementation, just to return the shares
644 connData = smbServer.getConnectionData(connId)
646 respSetup = b''
647 respParameters = b''
648 respData = b''
649 errorCode = STATUS_SUCCESS
650 if struct.unpack('<H', parameters[:2])[0] == 0:
651 # NetShareEnum Request
652 netShareEnum = smb.SMBNetShareEnum(parameters)
653 if netShareEnum['InfoLevel'] == 1:
654 shares = getShares(connId, smbServer)
655 respParameters = smb.SMBNetShareEnumResponse()
656 respParameters['EntriesReturned'] = len(shares)
657 respParameters['EntriesAvailable'] = len(shares)
658 tailData = ''
659 for i in shares:
660 # NetShareInfo1 len == 20
661 entry = smb.NetShareInfo1()
662 entry['NetworkName'] = i + '\x00' * (13 - len(i))
663 entry['Type'] = int(shares[i]['share type'])
664 # (beto) If offset == 0 it crashes explorer.exe on windows 7
665 entry['RemarkOffsetLow'] = 20 * len(shares) + len(tailData)
666 respData += entry.getData()
667 if 'comment' in shares[i]:
668 tailData += shares[i]['comment'] + '\x00'
669 else:
670 tailData += '\x00'
671 respData += tailData
672 else:
673 # We don't support other info levels
674 errorCode = STATUS_NOT_SUPPORTED
675 elif struct.unpack('<H', parameters[:2])[0] == 13:
676 # NetrServerGetInfo Request
677 respParameters = smb.SMBNetServerGetInfoResponse()
678 netServerInfo = smb.SMBNetServerInfo1()
679 netServerInfo['ServerName'] = smbServer.getServerName()
680 respData = netServerInfo.getData()
681 respParameters['TotalBytesAvailable'] = len(respData)
682 elif struct.unpack('<H', parameters[:2])[0] == 1:
683 # NetrShareGetInfo Request
684 request = smb.SMBNetShareGetInfo(parameters)
685 respParameters = smb.SMBNetShareGetInfoResponse()
686 shares = getShares(connId, smbServer)
687 share = shares[request['ShareName'].upper()]
688 shareInfo = smb.NetShareInfo1()
689 shareInfo['NetworkName'] = request['ShareName'].upper() + '\x00'
690 shareInfo['Type'] = int(share['share type'])
691 respData = shareInfo.getData()
692 if 'comment' in share:
693 shareInfo['RemarkOffsetLow'] = len(respData)
694 respData += share['comment'] + '\x00'
695 respParameters['TotalBytesAvailable'] = len(respData)
697 else:
698 # We don't know how to handle anything else
699 errorCode = STATUS_NOT_SUPPORTED
701 smbServer.setConnectionData(connId, connData)
703 return respSetup, respParameters, respData, errorCode
705 @staticmethod
706 def transactNamedPipe(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
707 connData = smbServer.getConnectionData(connId)
709 respSetup = b''
710 respParameters = b''
711 respData = b''
712 errorCode = STATUS_SUCCESS
713 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
714 transParameters = smb.SMBTransaction_Parameters(SMBCommand['Parameters'])
716 # Extract the FID
717 fid = struct.unpack('<H', transParameters['Setup'][2:])[0]
719 if fid in connData['OpenedFiles']:
720 fileHandle = connData['OpenedFiles'][fid]['FileHandle']
721 if fileHandle != PIPE_FILE_DESCRIPTOR:
722 os.write(fileHandle, data)
723 respData = os.read(fileHandle, data)
724 else:
725 sock = connData['OpenedFiles'][fid]['Socket']
726 sock.send(data)
727 respData = sock.recv(maxDataCount)
728 else:
729 errorCode = STATUS_INVALID_HANDLE
731 smbServer.setConnectionData(connId, connData)
733 return respSetup, respParameters, respData, errorCode
736# Here we implement the transaction2 handlers
737class TRANS2Commands:
738 # All these commands return setup, parameters, data, errorCode
739 @staticmethod
740 def setPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
741 connData = smbServer.getConnectionData(connId)
743 respSetup = b''
744 respParameters = b''
745 respData = b''
746 errorCode = STATUS_SUCCESS
747 setPathInfoParameters = smb.SMBSetPathInformation_Parameters(flags=recvPacket['Flags2'], data=parameters)
748 if recvPacket['Tid'] in connData['ConnectedShares']:
749 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
750 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], setPathInfoParameters['FileName']), path)
751 pathName = os.path.join(path, fileName)
753 if isInFileJail(path, fileName):
754 smbServer.log("Path not in current working directory")
755 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD
757 elif os.path.exists(pathName):
758 informationLevel = setPathInfoParameters['InformationLevel']
759 if informationLevel == smb.SMB_SET_FILE_BASIC_INFO:
760 infoRecord = smb.SMBSetFileBasicInfo(data)
761 # Creation time won't be set, the other ones we play with.
762 atime = infoRecord['LastAccessTime']
763 if atime == 0:
764 atime = -1
765 else:
766 atime = getUnixTime(atime)
767 mtime = infoRecord['LastWriteTime']
768 if mtime == 0:
769 mtime = -1
770 else:
771 mtime = getUnixTime(mtime)
772 if mtime != -1 or atime != -1:
773 os.utime(pathName, (atime, mtime))
774 else:
775 smbServer.log('Unknown level for set path info! 0x%x' % setPathInfoParameters['InformationLevel'],
776 logging.ERROR)
777 # UNSUPPORTED
778 errorCode = STATUS_NOT_SUPPORTED
779 else:
780 errorCode = STATUS_OBJECT_NAME_NOT_FOUND
782 if errorCode == STATUS_SUCCESS:
783 respParameters = smb.SMBSetPathInformationResponse_Parameters()
785 else:
786 errorCode = STATUS_SMB_BAD_TID
788 smbServer.setConnectionData(connId, connData)
790 return respSetup, respParameters, respData, errorCode
792 @staticmethod
793 def setFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
794 connData = smbServer.getConnectionData(connId)
796 respSetup = b''
797 respParameters = b''
798 respData = b''
799 errorCode = STATUS_SUCCESS
800 setFileInfoParameters = smb.SMBSetFileInformation_Parameters(parameters)
802 if recvPacket['Tid'] in connData['ConnectedShares']:
803 if setFileInfoParameters['FID'] in connData['OpenedFiles']:
804 fileName = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileName']
805 informationLevel = setFileInfoParameters['InformationLevel']
806 if informationLevel == smb.SMB_SET_FILE_DISPOSITION_INFO:
807 infoRecord = smb.SMBSetFileDispositionInfo(parameters)
808 if infoRecord['DeletePending'] > 0:
809 # Mark this file for removal after closed
810 connData['OpenedFiles'][setFileInfoParameters['FID']]['DeleteOnClose'] = True
811 respParameters = smb.SMBSetFileInformationResponse_Parameters()
812 elif informationLevel == smb.SMB_SET_FILE_BASIC_INFO:
813 infoRecord = smb.SMBSetFileBasicInfo(data)
814 # Creation time won't be set, the other ones we play with.
815 atime = infoRecord['LastAccessTime']
816 if atime == 0:
817 atime = -1
818 else:
819 atime = getUnixTime(atime)
820 mtime = infoRecord['LastWriteTime']
821 if mtime == 0:
822 mtime = -1
823 else:
824 mtime = getUnixTime(mtime)
825 os.utime(fileName, (atime, mtime))
826 elif informationLevel == smb.SMB_SET_FILE_END_OF_FILE_INFO:
827 fileHandle = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileHandle']
828 infoRecord = smb.SMBSetFileEndOfFileInfo(data)
829 if infoRecord['EndOfFile'] > 0:
830 os.lseek(fileHandle, infoRecord['EndOfFile'] - 1, 0)
831 os.write(fileHandle, b'\x00')
832 else:
833 smbServer.log('Unknown level for set file info! 0x%x' % setFileInfoParameters['InformationLevel'],
834 logging.ERROR)
835 # UNSUPPORTED
836 errorCode = STATUS_NOT_SUPPORTED
837 else:
838 errorCode = STATUS_NO_SUCH_FILE
840 if errorCode == STATUS_SUCCESS:
841 respParameters = smb.SMBSetFileInformationResponse_Parameters()
842 else:
843 errorCode = STATUS_SMB_BAD_TID
845 smbServer.setConnectionData(connId, connData)
847 return respSetup, respParameters, respData, errorCode
849 @staticmethod
850 def queryFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
851 connData = smbServer.getConnectionData(connId)
853 respSetup = b''
854 respParameters = b''
855 respData = b''
857 queryFileInfoParameters = smb.SMBQueryFileInformation_Parameters(parameters)
859 if recvPacket['Tid'] in connData['ConnectedShares']:
860 if queryFileInfoParameters['FID'] in connData['OpenedFiles']:
861 pathName = connData['OpenedFiles'][queryFileInfoParameters['FID']]['FileName']
863 infoRecord, errorCode = queryFileInformation(os.path.dirname(pathName), os.path.basename(pathName),
864 queryFileInfoParameters['InformationLevel'])
866 if infoRecord is not None:
867 respParameters = smb.SMBQueryFileInformationResponse_Parameters()
868 respData = infoRecord
869 else:
870 errorCode = STATUS_INVALID_HANDLE
871 else:
872 errorCode = STATUS_SMB_BAD_TID
874 smbServer.setConnectionData(connId, connData)
876 return respSetup, respParameters, respData, errorCode
878 @staticmethod
879 def queryPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
880 connData = smbServer.getConnectionData(connId)
882 respSetup = b''
883 respParameters = b''
884 respData = b''
885 errorCode = 0
887 queryPathInfoParameters = smb.SMBQueryPathInformation_Parameters(flags=recvPacket['Flags2'], data=parameters)
889 if recvPacket['Tid'] in connData['ConnectedShares']:
890 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
891 try:
892 infoRecord, errorCode = queryPathInformation(path, decodeSMBString(recvPacket['Flags2'],
893 queryPathInfoParameters['FileName']),
894 queryPathInfoParameters['InformationLevel'])
895 except Exception as e:
896 smbServer.log("queryPathInformation: %s" % e, logging.ERROR)
898 if infoRecord is not None:
899 respParameters = smb.SMBQueryPathInformationResponse_Parameters()
900 respData = infoRecord
901 else:
902 errorCode = STATUS_SMB_BAD_TID
904 smbServer.setConnectionData(connId, connData)
906 return respSetup, respParameters, respData, errorCode
908 @staticmethod
909 def queryFsInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
910 connData = smbServer.getConnectionData(connId)
911 errorCode = 0
912 # Get the Tid associated
913 if recvPacket['Tid'] in connData['ConnectedShares']:
914 data = queryFsInformation(connData['ConnectedShares'][recvPacket['Tid']]['path'], '',
915 struct.unpack('<H', parameters)[0], pktFlags=recvPacket['Flags2'])
917 smbServer.setConnectionData(connId, connData)
919 return b'', b'', data, errorCode
921 @staticmethod
922 def findNext2(connId, smbServer, recvPacket, parameters, data, maxDataCount):
923 connData = smbServer.getConnectionData(connId)
925 respSetup = b''
926 respParameters = b''
927 respData = b''
928 errorCode = STATUS_SUCCESS
929 findNext2Parameters = smb.SMBFindNext2_Parameters(flags=recvPacket['Flags2'], data=parameters)
931 sid = findNext2Parameters['SID']
932 if recvPacket['Tid'] in connData['ConnectedShares']:
933 if sid in connData['SIDs']:
934 searchResult = connData['SIDs'][sid]
935 respParameters = smb.SMBFindNext2Response_Parameters()
936 endOfSearch = 1
937 searchCount = 1
938 totalData = 0
939 for i in enumerate(searchResult):
940 data = i[1].getData()
941 lenData = len(data)
942 if (totalData + lenData) >= maxDataCount or (i[0] + 1) >= findNext2Parameters['SearchCount']:
943 # We gotta stop here and continue on a find_next2
944 endOfSearch = 0
945 connData['SIDs'][sid] = searchResult[i[0]:]
946 respParameters['LastNameOffset'] = totalData
947 break
948 else:
949 searchCount += 1
950 respData += data
951 totalData += lenData
953 # Have we reached the end of the search or still stuff to send?
954 if endOfSearch > 0:
955 # Let's remove the SID from our ConnData
956 del (connData['SIDs'][sid])
958 respParameters['EndOfSearch'] = endOfSearch
959 respParameters['SearchCount'] = searchCount
960 else:
961 errorCode = STATUS_INVALID_HANDLE
962 else:
963 errorCode = STATUS_SMB_BAD_TID
965 smbServer.setConnectionData(connId, connData)
967 return respSetup, respParameters, respData, errorCode
969 @staticmethod
970 def findFirst2(connId, smbServer, recvPacket, parameters, data, maxDataCount):
971 connData = smbServer.getConnectionData(connId)
973 respSetup = b''
974 respParameters = b''
975 respData = b''
976 findFirst2Parameters = smb.SMBFindFirst2_Parameters(recvPacket['Flags2'], data=parameters)
978 if recvPacket['Tid'] in connData['ConnectedShares']:
979 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
981 searchResult, searchCount, errorCode = findFirst2(path,
982 decodeSMBString(recvPacket['Flags2'],
983 findFirst2Parameters['FileName']),
984 findFirst2Parameters['InformationLevel'],
985 findFirst2Parameters['SearchAttributes'],
986 pktFlags=recvPacket['Flags2'])
988 if searchCount > 0:
989 respParameters = smb.SMBFindFirst2Response_Parameters()
990 endOfSearch = 1
991 sid = 0x80 # default SID
992 searchCount = 0
993 totalData = 0
994 for i in enumerate(searchResult):
995 # i[1].dump()
996 data = i[1].getData()
997 lenData = len(data)
998 if (totalData + lenData) >= maxDataCount or (i[0] + 1) > findFirst2Parameters['SearchCount']:
999 # We gotta stop here and continue on a find_next2
1000 endOfSearch = 0
1001 # Simple way to generate a fid
1002 if len(connData['SIDs']) == 0:
1003 sid = 1
1004 else:
1005 sid = list(connData['SIDs'].keys())[-1] + 1
1006 # Store the remaining search results in the ConnData SID
1007 connData['SIDs'][sid] = searchResult[i[0]:]
1008 respParameters['LastNameOffset'] = totalData
1009 break
1010 else:
1011 searchCount += 1
1012 respData += data
1014 padLen = (8 - (lenData % 8)) % 8
1015 respData += b'\xaa' * padLen
1016 totalData += lenData + padLen
1018 respParameters['SID'] = sid
1019 respParameters['EndOfSearch'] = endOfSearch
1020 respParameters['SearchCount'] = searchCount
1022 # If we've empty files and errorCode was not already set, we return NO_SUCH_FILE
1023 elif errorCode == 0:
1024 errorCode = STATUS_NO_SUCH_FILE
1025 else:
1026 errorCode = STATUS_SMB_BAD_TID
1028 smbServer.setConnectionData(connId, connData)
1030 return respSetup, respParameters, respData, errorCode
1033# Here we implement the commands handlers
1034class SMBCommands:
1036 @staticmethod
1037 def smbTransaction(connId, smbServer, SMBCommand, recvPacket, transCommands):
1038 connData = smbServer.getConnectionData(connId)
1040 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
1042 transParameters = smb.SMBTransaction_Parameters(SMBCommand['Parameters'])
1044 # Do the stuff
1045 if transParameters['ParameterCount'] != transParameters['TotalParameterCount']:
1046 # TODO: Handle partial parameters
1047 raise Exception("Unsupported partial parameters in TRANSACT2!")
1048 else:
1049 transData = smb.SMBTransaction_SData(flags=recvPacket['Flags2'])
1050 # Standard says servers shouldn't trust Parameters and Data comes
1051 # in order, so we have to parse the offsets, ugly
1053 paramCount = transParameters['ParameterCount']
1054 transData['Trans_ParametersLength'] = paramCount
1055 dataCount = transParameters['DataCount']
1056 transData['Trans_DataLength'] = dataCount
1057 transData.fromString(SMBCommand['Data'])
1058 if transParameters['ParameterOffset'] > 0:
1059 paramOffset = transParameters['ParameterOffset'] - 63 - transParameters['SetupLength']
1060 transData['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset + paramCount]
1061 else:
1062 transData['Trans_Parameters'] = b''
1064 if transParameters['DataOffset'] > 0:
1065 dataOffset = transParameters['DataOffset'] - 63 - transParameters['SetupLength']
1066 transData['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
1067 else:
1068 transData['Trans_Data'] = b''
1070 # Call the handler for this TRANSACTION
1071 if transParameters['SetupCount'] == 0:
1072 # No subcommand, let's play with the Name
1073 command = decodeSMBString(recvPacket['Flags2'], transData['Name'])
1074 else:
1075 command = struct.unpack('<H', transParameters['Setup'][:2])[0]
1077 if command in transCommands:
1078 # Call the TRANS subcommand
1079 setup = b''
1080 parameters = b''
1081 data = b''
1082 try:
1083 setup, parameters, data, errorCode = transCommands[command](connId,
1084 smbServer,
1085 recvPacket,
1086 transData['Trans_Parameters'],
1087 transData['Trans_Data'],
1088 transParameters['MaxDataCount'])
1089 except Exception as e:
1090 # print 'Transaction: %s' % e,e
1091 smbServer.log('Transaction: (%r,%s)' % (command, e), logging.ERROR)
1092 errorCode = STATUS_ACCESS_DENIED
1093 # raise
1095 if setup == b'' and parameters == b'' and data == b'':
1096 # Something wen't wrong
1097 respParameters = b''
1098 respData = b''
1099 else:
1100 # Build the answer
1101 if hasattr(data, 'getData'):
1102 data = data.getData()
1103 remainingData = len(data)
1104 if hasattr(parameters, 'getData'):
1105 parameters = parameters.getData()
1106 remainingParameters = len(parameters)
1107 commands = []
1108 dataDisplacement = 0
1109 while remainingData > 0 or remainingParameters > 0:
1110 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
1111 respParameters = smb.SMBTransactionResponse_Parameters()
1112 respData = smb.SMBTransaction2Response_Data()
1114 respParameters['TotalParameterCount'] = len(parameters)
1115 respParameters['ParameterCount'] = len(parameters)
1116 respData['Trans_ParametersLength'] = len(parameters)
1117 respParameters['TotalDataCount'] = len(data)
1118 respParameters['DataDisplacement'] = dataDisplacement
1120 # TODO: Do the same for parameters
1121 if len(data) > transParameters['MaxDataCount']:
1122 # Answer doesn't fit in this packet
1123 LOG.debug("Lowering answer from %d to %d" % (len(data), transParameters['MaxDataCount']))
1124 respParameters['DataCount'] = transParameters['MaxDataCount']
1125 else:
1126 respParameters['DataCount'] = len(data)
1128 respData['Trans_DataLength'] = respParameters['DataCount']
1129 respParameters['SetupCount'] = len(setup)
1130 respParameters['Setup'] = setup
1131 # TODO: Make sure we're calculating the pad right
1132 if len(parameters) > 0:
1133 # padLen = 4 - (55 + len(setup)) % 4
1134 padLen = (4 - (55 + len(setup)) % 4) % 4
1135 padBytes = b'\xFF' * padLen
1136 respData['Pad1'] = padBytes
1137 respParameters['ParameterOffset'] = 55 + len(setup) + padLen
1138 else:
1139 padLen = 0
1140 respParameters['ParameterOffset'] = 0
1141 respData['Pad1'] = b''
1143 if len(data) > 0:
1144 # pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4
1145 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4
1146 respData['Pad2'] = b'\xFF' * pad2Len
1147 respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len
1148 else:
1149 respParameters['DataOffset'] = 0
1150 respData['Pad2'] = b''
1152 respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']]
1153 respData['Trans_Data'] = data[:respParameters['DataCount']]
1154 respSMBCommand['Parameters'] = respParameters
1155 respSMBCommand['Data'] = respData
1157 data = data[respParameters['DataCount']:]
1158 remainingData -= respParameters['DataCount']
1159 dataDisplacement += respParameters['DataCount'] + 1
1161 parameters = parameters[respParameters['ParameterCount']:]
1162 remainingParameters -= respParameters['ParameterCount']
1163 commands.append(respSMBCommand)
1165 smbServer.setConnectionData(connId, connData)
1166 return commands, None, errorCode
1168 else:
1169 smbServer.log("Unsupported Transact command %r" % command, logging.ERROR)
1170 respParameters = b''
1171 respData = b''
1172 errorCode = STATUS_NOT_IMPLEMENTED
1174 respSMBCommand['Parameters'] = respParameters
1175 respSMBCommand['Data'] = respData
1176 smbServer.setConnectionData(connId, connData)
1178 return [respSMBCommand], None, errorCode
1180 @staticmethod
1181 def smbNTTransact(connId, smbServer, SMBCommand, recvPacket, transCommands):
1182 connData = smbServer.getConnectionData(connId)
1184 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
1186 NTTransParameters = smb.SMBNTTransaction_Parameters(SMBCommand['Parameters'])
1187 # Do the stuff
1188 if NTTransParameters['ParameterCount'] != NTTransParameters['TotalParameterCount']:
1189 # TODO: Handle partial parameters
1190 raise Exception("Unsupported partial parameters in NTTrans!")
1191 else:
1192 NTTransData = smb.SMBNTTransaction_Data()
1193 # Standard says servers shouldn't trust Parameters and Data comes
1194 # in order, so we have to parse the offsets, ugly
1196 paramCount = NTTransParameters['ParameterCount']
1197 NTTransData['NT_Trans_ParametersLength'] = paramCount
1198 dataCount = NTTransParameters['DataCount']
1199 NTTransData['NT_Trans_DataLength'] = dataCount
1201 if NTTransParameters['ParameterOffset'] > 0:
1202 paramOffset = NTTransParameters['ParameterOffset'] - 73 - NTTransParameters['SetupLength']
1203 NTTransData['NT_Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset + paramCount]
1204 else:
1205 NTTransData['NT_Trans_Parameters'] = b''
1207 if NTTransParameters['DataOffset'] > 0:
1208 dataOffset = NTTransParameters['DataOffset'] - 73 - NTTransParameters['SetupLength']
1209 NTTransData['NT_Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
1210 else:
1211 NTTransData['NT_Trans_Data'] = b''
1213 # Call the handler for this TRANSACTION
1214 command = NTTransParameters['Function']
1215 if command in transCommands:
1216 # Call the NT TRANS subcommand
1217 setup = b''
1218 parameters = b''
1219 data = b''
1220 try:
1221 setup, parameters, data, errorCode = transCommands[command](connId,
1222 smbServer,
1223 recvPacket,
1224 NTTransData['NT_Trans_Parameters'],
1225 NTTransData['NT_Trans_Data'],
1226 NTTransParameters['MaxDataCount'])
1227 except Exception as e:
1228 smbServer.log('NTTransaction: (0x%x,%s)' % (command, e), logging.ERROR)
1229 errorCode = STATUS_ACCESS_DENIED
1230 # raise
1232 if setup == b'' and parameters == b'' and data == b'':
1233 # Something wen't wrong
1234 respParameters = b''
1235 respData = b''
1236 if errorCode == STATUS_SUCCESS:
1237 errorCode = STATUS_ACCESS_DENIED
1238 else:
1239 # Build the answer
1240 if hasattr(data, 'getData'):
1241 data = data.getData()
1242 remainingData = len(data)
1243 if hasattr(parameters, 'getData'):
1244 parameters = parameters.getData()
1245 remainingParameters = len(parameters)
1246 commands = []
1247 dataDisplacement = 0
1248 while remainingData > 0 or remainingParameters > 0:
1249 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
1250 respParameters = smb.SMBNTTransactionResponse_Parameters()
1251 respData = smb.SMBNTTransactionResponse_Data()
1253 respParameters['TotalParameterCount'] = len(parameters)
1254 respParameters['ParameterCount'] = len(parameters)
1255 respData['Trans_ParametersLength'] = len(parameters)
1256 respParameters['TotalDataCount'] = len(data)
1257 respParameters['DataDisplacement'] = dataDisplacement
1258 # TODO: Do the same for parameters
1259 if len(data) > NTTransParameters['MaxDataCount']:
1260 # Answer doesn't fit in this packet
1261 LOG.debug("Lowering answer from %d to %d" % (len(data), NTTransParameters['MaxDataCount']))
1262 respParameters['DataCount'] = NTTransParameters['MaxDataCount']
1263 else:
1264 respParameters['DataCount'] = len(data)
1266 respData['NT_Trans_DataLength'] = respParameters['DataCount']
1267 respParameters['SetupCount'] = len(setup)
1268 respParameters['Setup'] = setup
1269 # TODO: Make sure we're calculating the pad right
1270 if len(parameters) > 0:
1271 # padLen = 4 - (71 + len(setup)) % 4
1272 padLen = (4 - (73 + len(setup)) % 4) % 4
1273 padBytes = b'\xFF' * padLen
1274 respData['Pad1'] = padBytes
1275 respParameters['ParameterOffset'] = 73 + len(setup) + padLen
1276 else:
1277 padLen = 0
1278 respParameters['ParameterOffset'] = 0
1279 respData['Pad1'] = b''
1281 if len(data) > 0:
1282 # pad2Len = 4 - (71 + len(setup) + padLen + len(parameters)) % 4
1283 pad2Len = (4 - (73 + len(setup) + padLen + len(parameters)) % 4) % 4
1284 respData['Pad2'] = b'\xFF' * pad2Len
1285 respParameters['DataOffset'] = 73 + len(setup) + padLen + len(parameters) + pad2Len
1286 else:
1287 respParameters['DataOffset'] = 0
1288 respData['Pad2'] = b''
1290 respData['NT_Trans_Parameters'] = parameters[:respParameters['ParameterCount']]
1291 respData['NT_Trans_Data'] = data[:respParameters['DataCount']]
1292 respSMBCommand['Parameters'] = respParameters
1293 respSMBCommand['Data'] = respData
1295 data = data[respParameters['DataCount']:]
1296 remainingData -= respParameters['DataCount']
1297 dataDisplacement += respParameters['DataCount'] + 1
1299 parameters = parameters[respParameters['ParameterCount']:]
1300 remainingParameters -= respParameters['ParameterCount']
1301 commands.append(respSMBCommand)
1303 smbServer.setConnectionData(connId, connData)
1304 return commands, None, errorCode
1306 else:
1307 # smbServer.log("Unsupported NTTransact command 0x%x" % command, logging.ERROR)
1308 respParameters = b''
1309 respData = b''
1310 errorCode = STATUS_NOT_IMPLEMENTED
1312 respSMBCommand['Parameters'] = respParameters
1313 respSMBCommand['Data'] = respData
1315 smbServer.setConnectionData(connId, connData)
1316 return [respSMBCommand], None, errorCode
1318 @staticmethod
1319 def smbTransaction2(connId, smbServer, SMBCommand, recvPacket, transCommands):
1320 connData = smbServer.getConnectionData(connId)
1322 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
1324 trans2Parameters = smb.SMBTransaction2_Parameters(SMBCommand['Parameters'])
1326 # Do the stuff
1327 if trans2Parameters['ParameterCount'] != trans2Parameters['TotalParameterCount']:
1328 # TODO: Handle partial parameters
1329 # print "Unsupported partial parameters in TRANSACT2!"
1330 raise Exception("Unsupported partial parameters in TRANSACT2!")
1331 else:
1332 trans2Data = smb.SMBTransaction2_Data()
1333 # Standard says servers shouldn't trust Parameters and Data comes
1334 # in order, so we have to parse the offsets, ugly
1336 paramCount = trans2Parameters['ParameterCount']
1337 trans2Data['Trans_ParametersLength'] = paramCount
1338 dataCount = trans2Parameters['DataCount']
1339 trans2Data['Trans_DataLength'] = dataCount
1341 if trans2Parameters['ParameterOffset'] > 0:
1342 paramOffset = trans2Parameters['ParameterOffset'] - 63 - trans2Parameters['SetupLength']
1343 trans2Data['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset + paramCount]
1344 else:
1345 trans2Data['Trans_Parameters'] = b''
1347 if trans2Parameters['DataOffset'] > 0:
1348 dataOffset = trans2Parameters['DataOffset'] - 63 - trans2Parameters['SetupLength']
1349 trans2Data['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
1350 else:
1351 trans2Data['Trans_Data'] = b''
1353 # Call the handler for this TRANSACTION
1354 command = struct.unpack('<H', trans2Parameters['Setup'])[0]
1355 if command in transCommands:
1356 # Call the TRANS2 subcommand
1357 try:
1358 setup, parameters, data, errorCode = transCommands[command](connId,
1359 smbServer,
1360 recvPacket,
1361 trans2Data['Trans_Parameters'],
1362 trans2Data['Trans_Data'],
1363 trans2Parameters['MaxDataCount'])
1364 except Exception as e:
1365 smbServer.log('Transaction2: (0x%x,%s)' % (command, e), logging.ERROR)
1366 # import traceback
1367 # traceback.print_exc()
1368 raise
1370 if setup == b'' and parameters == b'' and data == b'':
1371 # Something wen't wrong
1372 respParameters = b''
1373 respData = b''
1374 else:
1375 # Build the answer
1376 if hasattr(data, 'getData'):
1377 data = data.getData()
1378 remainingData = len(data)
1379 if hasattr(parameters, 'getData'):
1380 parameters = parameters.getData()
1381 remainingParameters = len(parameters)
1382 commands = []
1383 dataDisplacement = 0
1384 while remainingData > 0 or remainingParameters > 0:
1385 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
1386 respParameters = smb.SMBTransaction2Response_Parameters()
1387 respData = smb.SMBTransaction2Response_Data()
1389 respParameters['TotalParameterCount'] = len(parameters)
1390 respParameters['ParameterCount'] = len(parameters)
1391 respData['Trans_ParametersLength'] = len(parameters)
1392 respParameters['TotalDataCount'] = len(data)
1393 respParameters['DataDisplacement'] = dataDisplacement
1394 # TODO: Do the same for parameters
1395 if len(data) > trans2Parameters['MaxDataCount']:
1396 # Answer doesn't fit in this packet
1397 LOG.debug("Lowering answer from %d to %d" % (len(data), trans2Parameters['MaxDataCount']))
1398 respParameters['DataCount'] = trans2Parameters['MaxDataCount']
1399 else:
1400 respParameters['DataCount'] = len(data)
1402 respData['Trans_DataLength'] = respParameters['DataCount']
1403 respParameters['SetupCount'] = len(setup)
1404 respParameters['Setup'] = setup
1405 # TODO: Make sure we're calculating the pad right
1406 if len(parameters) > 0:
1407 # padLen = 4 - (55 + len(setup)) % 4
1408 padLen = (4 - (55 + len(setup)) % 4) % 4
1409 padBytes = b'\xFF' * padLen
1410 respData['Pad1'] = padBytes
1411 respParameters['ParameterOffset'] = 55 + len(setup) + padLen
1412 else:
1413 padLen = 0
1414 respParameters['ParameterOffset'] = 0
1415 respData['Pad1'] = b''
1417 if len(data) > 0:
1418 # pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4
1419 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4
1420 respData['Pad2'] = b'\xFF' * pad2Len
1421 respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len
1422 else:
1423 respParameters['DataOffset'] = 0
1424 respData['Pad2'] = b''
1426 respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']]
1427 respData['Trans_Data'] = data[:respParameters['DataCount']]
1428 respSMBCommand['Parameters'] = respParameters
1429 respSMBCommand['Data'] = respData
1431 data = data[respParameters['DataCount']:]
1432 remainingData -= respParameters['DataCount']
1433 dataDisplacement += respParameters['DataCount'] + 1
1435 parameters = parameters[respParameters['ParameterCount']:]
1436 remainingParameters -= respParameters['ParameterCount']
1437 commands.append(respSMBCommand)
1439 smbServer.setConnectionData(connId, connData)
1440 return commands, None, errorCode
1442 else:
1443 smbServer.log("Unsupported Transact/2 command 0x%x" % command, logging.ERROR)
1444 respParameters = b''
1445 respData = b''
1446 errorCode = STATUS_NOT_IMPLEMENTED
1448 respSMBCommand['Parameters'] = respParameters
1449 respSMBCommand['Data'] = respData
1451 smbServer.setConnectionData(connId, connData)
1452 return [respSMBCommand], None, errorCode
1454 @staticmethod
1455 def smbComLockingAndX(connId, smbServer, SMBCommand, recvPacket):
1456 connData = smbServer.getConnectionData(connId)
1458 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOCKING_ANDX)
1459 respParameters = b''
1460 respData = b''
1462 # I'm actually doing nothing.. just make MacOS happy ;)
1463 errorCode = STATUS_SUCCESS
1465 respSMBCommand['Parameters'] = respParameters
1466 respSMBCommand['Data'] = respData
1467 smbServer.setConnectionData(connId, connData)
1469 return [respSMBCommand], None, errorCode
1471 @staticmethod
1472 def smbComClose(connId, smbServer, SMBCommand, recvPacket):
1473 connData = smbServer.getConnectionData(connId)
1475 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CLOSE)
1476 respParameters = b''
1477 respData = b''
1479 comClose = smb.SMBClose_Parameters(SMBCommand['Parameters'])
1481 # Get the Tid associated
1482 if recvPacket['Tid'] in connData['ConnectedShares']:
1483 if comClose['FID'] in connData['OpenedFiles']:
1484 errorCode = STATUS_SUCCESS
1485 fileHandle = connData['OpenedFiles'][comClose['FID']]['FileHandle']
1486 try:
1487 if fileHandle == PIPE_FILE_DESCRIPTOR:
1488 connData['OpenedFiles'][comClose['FID']]['Socket'].close()
1489 elif fileHandle != VOID_FILE_DESCRIPTOR:
1490 os.close(fileHandle)
1491 except Exception as e:
1492 smbServer.log("comClose %s" % e, logging.ERROR)
1493 errorCode = STATUS_ACCESS_DENIED
1494 else:
1495 # Check if the file was marked for removal
1496 if connData['OpenedFiles'][comClose['FID']]['DeleteOnClose'] is True:
1497 try:
1498 os.remove(connData['OpenedFiles'][comClose['FID']]['FileName'])
1499 except Exception as e:
1500 smbServer.log("comClose %s" % e, logging.ERROR)
1501 errorCode = STATUS_ACCESS_DENIED
1502 del (connData['OpenedFiles'][comClose['FID']])
1503 else:
1504 errorCode = STATUS_INVALID_HANDLE
1505 else:
1506 errorCode = STATUS_SMB_BAD_TID
1508 if errorCode > 0:
1509 respParameters = b''
1510 respData = b''
1512 respSMBCommand['Parameters'] = respParameters
1513 respSMBCommand['Data'] = respData
1514 smbServer.setConnectionData(connId, connData)
1516 return [respSMBCommand], None, errorCode
1518 @staticmethod
1519 def smbComWrite(connId, smbServer, SMBCommand, recvPacket):
1520 connData = smbServer.getConnectionData(connId)
1522 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE)
1523 respParameters = smb.SMBWriteResponse_Parameters()
1524 respData = b''
1526 comWriteParameters = smb.SMBWrite_Parameters(SMBCommand['Parameters'])
1527 comWriteData = smb.SMBWrite_Data(SMBCommand['Data'])
1529 # Get the Tid associated
1530 if recvPacket['Tid'] in connData['ConnectedShares']:
1531 if comWriteParameters['Fid'] in connData['OpenedFiles']:
1532 fileHandle = connData['OpenedFiles'][comWriteParameters['Fid']]['FileHandle']
1533 errorCode = STATUS_SUCCESS
1534 try:
1535 if fileHandle != PIPE_FILE_DESCRIPTOR:
1536 # TODO: Handle big size files
1537 # If we're trying to write past the file end we just skip the write call (Vista does this)
1538 if os.lseek(fileHandle, 0, 2) >= comWriteParameters['Offset']:
1539 os.lseek(fileHandle, comWriteParameters['Offset'], 0)
1540 os.write(fileHandle, comWriteData['Data'])
1541 else:
1542 sock = connData['OpenedFiles'][comWriteParameters['Fid']]['Socket']
1543 sock.send(comWriteData['Data'])
1544 respParameters['Count'] = comWriteParameters['Count']
1545 except Exception as e:
1546 smbServer.log('smbComWrite: %s' % e, logging.ERROR)
1547 errorCode = STATUS_ACCESS_DENIED
1548 else:
1549 errorCode = STATUS_INVALID_HANDLE
1550 else:
1551 errorCode = STATUS_SMB_BAD_TID
1553 if errorCode > 0:
1554 respParameters = b''
1555 respData = b''
1557 respSMBCommand['Parameters'] = respParameters
1558 respSMBCommand['Data'] = respData
1559 smbServer.setConnectionData(connId, connData)
1561 return [respSMBCommand], None, errorCode
1563 @staticmethod
1564 def smbComFlush(connId, smbServer, SMBCommand, recvPacket):
1565 connData = smbServer.getConnectionData(connId)
1567 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_FLUSH)
1568 respParameters = b''
1569 respData = b''
1571 comFlush = smb.SMBFlush_Parameters(SMBCommand['Parameters'])
1573 # Get the Tid associated
1574 if recvPacket['Tid'] in connData['ConnectedShares']:
1575 if comFlush['FID'] in connData['OpenedFiles']:
1576 errorCode = STATUS_SUCCESS
1577 fileHandle = connData['OpenedFiles'][comFlush['FID']]['FileHandle']
1578 try:
1579 os.fsync(fileHandle)
1580 except Exception as e:
1581 smbServer.log("comFlush %s" % e, logging.ERROR)
1582 errorCode = STATUS_ACCESS_DENIED
1583 else:
1584 errorCode = STATUS_INVALID_HANDLE
1585 else:
1586 errorCode = STATUS_SMB_BAD_TID
1588 if errorCode > 0:
1589 respParameters = b''
1590 respData = b''
1592 respSMBCommand['Parameters'] = respParameters
1593 respSMBCommand['Data'] = respData
1594 smbServer.setConnectionData(connId, connData)
1596 return [respSMBCommand], None, errorCode
1598 @staticmethod
1599 def smbComCreateDirectory(connId, smbServer, SMBCommand, recvPacket):
1600 connData = smbServer.getConnectionData(connId)
1602 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY)
1603 respParameters = b''
1604 respData = b''
1606 comCreateDirectoryData = smb.SMBCreateDirectory_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
1608 # Get the Tid associated
1609 if recvPacket['Tid'] in connData['ConnectedShares']:
1610 errorCode = STATUS_SUCCESS
1611 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
1612 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], comCreateDirectoryData['DirectoryName']))
1613 pathName = os.path.join(path, fileName)
1615 if not isInFileJail(path, fileName):
1616 smbServer.log("Path not in current working directory", logging.ERROR)
1617 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD
1619 elif os.path.exists(pathName):
1620 errorCode = STATUS_OBJECT_NAME_COLLISION
1622 else:
1623 try:
1624 os.mkdir(pathName)
1625 except Exception as e:
1626 smbServer.log("smbComCreateDirectory: %s" % e, logging.ERROR)
1627 errorCode = STATUS_ACCESS_DENIED
1628 else:
1629 errorCode = STATUS_SMB_BAD_TID
1631 if errorCode > 0:
1632 respParameters = b''
1633 respData = b''
1635 respSMBCommand['Parameters'] = respParameters
1636 respSMBCommand['Data'] = respData
1637 smbServer.setConnectionData(connId, connData)
1639 return [respSMBCommand], None, errorCode
1641 @staticmethod
1642 def smbComRename(connId, smbServer, SMBCommand, recvPacket):
1643 connData = smbServer.getConnectionData(connId)
1645 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_RENAME)
1646 respParameters = b''
1647 respData = b''
1649 comRenameData = smb.SMBRename_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
1651 # Get the Tid associated
1652 if recvPacket['Tid'] in connData['ConnectedShares']:
1653 errorCode = STATUS_SUCCESS
1654 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
1655 oldFileName = normalize_path(decodeSMBString(recvPacket['Flags2'], comRenameData['OldFileName']))
1656 oldPathName = os.path.join(path, oldFileName)
1657 newFileName = normalize_path(decodeSMBString(recvPacket['Flags2'], comRenameData['NewFileName']))
1658 newPathName = os.path.join(path, newFileName)
1660 if not isInFileJail(path, oldFileName) or not isInFileJail(path, newFileName):
1661 smbServer.log("Path not in current working directory", logging.ERROR)
1662 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD
1664 elif not os.path.exists(oldPathName):
1665 errorCode = STATUS_NO_SUCH_FILE
1667 else:
1668 try:
1669 os.rename(oldPathName, newPathName)
1670 except OSError as e:
1671 smbServer.log("smbComRename: %s" % e, logging.ERROR)
1672 errorCode = STATUS_ACCESS_DENIED
1673 else:
1674 errorCode = STATUS_SMB_BAD_TID
1676 if errorCode > 0:
1677 respParameters = b''
1678 respData = b''
1680 respSMBCommand['Parameters'] = respParameters
1681 respSMBCommand['Data'] = respData
1682 smbServer.setConnectionData(connId, connData)
1684 return [respSMBCommand], None, errorCode
1686 @staticmethod
1687 def smbComDelete(connId, smbServer, SMBCommand, recvPacket):
1688 connData = smbServer.getConnectionData(connId)
1690 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE)
1691 respParameters = b''
1692 respData = b''
1694 comDeleteData = smb.SMBDelete_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
1696 # Get the Tid associated
1697 if recvPacket['Tid'] in connData['ConnectedShares']:
1698 errorCode = STATUS_SUCCESS
1699 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
1700 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], comDeleteData['FileName']))
1701 pathName = os.path.join(path, fileName)
1703 if not isInFileJail(path, fileName):
1704 smbServer.log("Path not in current working directory", logging.ERROR)
1705 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD
1707 elif not os.path.exists(pathName):
1708 errorCode = STATUS_NO_SUCH_FILE
1710 else:
1711 try:
1712 os.remove(pathName)
1713 except OSError as e:
1714 smbServer.log("smbComDelete: %s" % e, logging.ERROR)
1715 errorCode = STATUS_ACCESS_DENIED
1716 else:
1717 errorCode = STATUS_SMB_BAD_TID
1719 if errorCode > 0:
1720 respParameters = b''
1721 respData = b''
1723 respSMBCommand['Parameters'] = respParameters
1724 respSMBCommand['Data'] = respData
1725 smbServer.setConnectionData(connId, connData)
1727 return [respSMBCommand], None, errorCode
1729 @staticmethod
1730 def smbComDeleteDirectory(connId, smbServer, SMBCommand, recvPacket):
1731 connData = smbServer.getConnectionData(connId)
1733 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY)
1734 respParameters = b''
1735 respData = b''
1737 comDeleteDirectoryData = smb.SMBDeleteDirectory_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
1739 # Get the Tid associated
1740 if recvPacket['Tid'] in connData['ConnectedShares']:
1741 errorCode = STATUS_SUCCESS
1742 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
1743 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], comDeleteDirectoryData['DirectoryName']))
1744 pathName = os.path.join(path, fileName)
1746 if not isInFileJail(path, fileName):
1747 smbServer.log("Path not in current working directory", logging.ERROR)
1748 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD
1750 if os.path.exists(pathName) is not True:
1751 errorCode = STATUS_NO_SUCH_FILE
1753 else:
1754 try:
1755 os.rmdir(pathName)
1756 except OSError as e:
1757 smbServer.log("smbComDeleteDirectory: %s" % e, logging.ERROR)
1758 if e.errno == errno.ENOTEMPTY:
1759 errorCode = STATUS_DIRECTORY_NOT_EMPTY
1760 else:
1761 errorCode = STATUS_ACCESS_DENIED
1762 else:
1763 errorCode = STATUS_SMB_BAD_TID
1765 if errorCode > 0:
1766 respParameters = b''
1767 respData = b''
1769 respSMBCommand['Parameters'] = respParameters
1770 respSMBCommand['Data'] = respData
1771 smbServer.setConnectionData(connId, connData)
1773 return [respSMBCommand], None, errorCode
1775 @staticmethod
1776 def smbComWriteAndX(connId, smbServer, SMBCommand, recvPacket):
1777 connData = smbServer.getConnectionData(connId)
1779 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE_ANDX)
1780 respParameters = smb.SMBWriteAndXResponse_Parameters()
1781 respData = b''
1783 if SMBCommand['WordCount'] == 0x0C:
1784 writeAndX = smb.SMBWriteAndX_Parameters_Short(SMBCommand['Parameters'])
1785 writeAndXData = smb.SMBWriteAndX_Data_Short()
1786 else:
1787 writeAndX = smb.SMBWriteAndX_Parameters(SMBCommand['Parameters'])
1788 writeAndXData = smb.SMBWriteAndX_Data()
1789 writeAndXData['DataLength'] = writeAndX['DataLength']
1790 writeAndXData['DataOffset'] = writeAndX['DataOffset']
1791 writeAndXData.fromString(SMBCommand['Data'])
1793 # Get the Tid associated
1794 if recvPacket['Tid'] in connData['ConnectedShares']:
1795 if writeAndX['Fid'] in connData['OpenedFiles']:
1796 fileHandle = connData['OpenedFiles'][writeAndX['Fid']]['FileHandle']
1797 errorCode = STATUS_SUCCESS
1798 try:
1799 if fileHandle != PIPE_FILE_DESCRIPTOR:
1800 offset = writeAndX['Offset']
1801 if 'HighOffset' in writeAndX.fields:
1802 offset += (writeAndX['HighOffset'] << 32)
1803 # If we're trying to write past the file end we just skip the write call (Vista does this)
1804 if os.lseek(fileHandle, 0, 2) >= offset:
1805 os.lseek(fileHandle, offset, 0)
1806 os.write(fileHandle, writeAndXData['Data'])
1807 else:
1808 sock = connData['OpenedFiles'][writeAndX['Fid']]['Socket']
1809 sock.send(writeAndXData['Data'])
1811 respParameters['Count'] = writeAndX['DataLength']
1812 respParameters['Available'] = 0xff
1813 except Exception as e:
1814 smbServer.log('smbComWriteAndx: %s' % e, logging.ERROR)
1815 errorCode = STATUS_ACCESS_DENIED
1816 else:
1817 errorCode = STATUS_INVALID_HANDLE
1818 else:
1819 errorCode = STATUS_SMB_BAD_TID
1821 if errorCode > 0:
1822 respParameters = b''
1823 respData = b''
1825 respSMBCommand['Parameters'] = respParameters
1826 respSMBCommand['Data'] = respData
1827 smbServer.setConnectionData(connId, connData)
1829 return [respSMBCommand], None, errorCode
1831 @staticmethod
1832 def smbComRead(connId, smbServer, SMBCommand, recvPacket):
1833 connData = smbServer.getConnectionData(connId)
1835 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ)
1836 respParameters = smb.SMBReadResponse_Parameters()
1837 respData = smb.SMBReadResponse_Data()
1839 comReadParameters = smb.SMBRead_Parameters(SMBCommand['Parameters'])
1841 # Get the Tid associated
1842 if recvPacket['Tid'] in connData['ConnectedShares']:
1843 if comReadParameters['Fid'] in connData['OpenedFiles']:
1844 fileHandle = connData['OpenedFiles'][comReadParameters['Fid']]['FileHandle']
1845 errorCode = STATUS_SUCCESS
1846 try:
1847 if fileHandle != PIPE_FILE_DESCRIPTOR:
1848 # TODO: Handle big size files
1849 os.lseek(fileHandle, comReadParameters['Offset'], 0)
1850 content = os.read(fileHandle, comReadParameters['Count'])
1851 else:
1852 sock = connData['OpenedFiles'][comReadParameters['Fid']]['Socket']
1853 content = sock.recv(comReadParameters['Count'])
1854 respParameters['Count'] = len(content)
1855 respData['DataLength'] = len(content)
1856 respData['Data'] = content
1857 except Exception as e:
1858 smbServer.log('smbComRead: %s ' % e, logging.ERROR)
1859 errorCode = STATUS_ACCESS_DENIED
1860 else:
1861 errorCode = STATUS_INVALID_HANDLE
1862 else:
1863 errorCode = STATUS_SMB_BAD_TID
1865 if errorCode > 0:
1866 respParameters = b''
1867 respData = b''
1869 respSMBCommand['Parameters'] = respParameters
1870 respSMBCommand['Data'] = respData
1871 smbServer.setConnectionData(connId, connData)
1873 return [respSMBCommand], None, errorCode
1875 @staticmethod
1876 def smbComReadAndX(connId, smbServer, SMBCommand, recvPacket):
1877 connData = smbServer.getConnectionData(connId)
1879 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ_ANDX)
1880 respParameters = smb.SMBReadAndXResponse_Parameters()
1881 respData = b''
1883 if SMBCommand['WordCount'] == 0x0A:
1884 readAndX = smb.SMBReadAndX_Parameters2(SMBCommand['Parameters'])
1885 else:
1886 readAndX = smb.SMBReadAndX_Parameters(SMBCommand['Parameters'])
1888 # Get the Tid associated
1889 if recvPacket['Tid'] in connData['ConnectedShares']:
1890 if readAndX['Fid'] in connData['OpenedFiles']:
1891 fileHandle = connData['OpenedFiles'][readAndX['Fid']]['FileHandle']
1892 errorCode = 0
1893 try:
1894 if fileHandle != PIPE_FILE_DESCRIPTOR:
1895 offset = readAndX['Offset']
1896 if 'HighOffset' in readAndX.fields:
1897 offset += (readAndX['HighOffset'] << 32)
1898 os.lseek(fileHandle, offset, 0)
1899 content = os.read(fileHandle, readAndX['MaxCount'])
1900 else:
1901 sock = connData['OpenedFiles'][readAndX['Fid']]['Socket']
1902 content = sock.recv(readAndX['MaxCount'])
1903 respParameters['Remaining'] = 0xffff
1904 respParameters['DataCount'] = len(content)
1905 respParameters['DataOffset'] = 59
1906 respParameters['DataCount_Hi'] = 0
1907 respData = content
1908 except Exception as e:
1909 smbServer.log('smbComReadAndX: %s ' % e, logging.ERROR)
1910 errorCode = STATUS_ACCESS_DENIED
1911 else:
1912 errorCode = STATUS_INVALID_HANDLE
1913 else:
1914 errorCode = STATUS_SMB_BAD_TID
1916 if errorCode > 0:
1917 respParameters = b''
1918 respData = b''
1920 respSMBCommand['Parameters'] = respParameters
1921 respSMBCommand['Data'] = respData
1922 smbServer.setConnectionData(connId, connData)
1924 return [respSMBCommand], None, errorCode
1926 @staticmethod
1927 def smbQueryInformation(connId, smbServer, SMBCommand, recvPacket):
1928 connData = smbServer.getConnectionData(connId)
1930 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION)
1931 respParameters = smb.SMBQueryInformationResponse_Parameters()
1932 respData = b''
1934 queryInformation = smb.SMBQueryInformation_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
1936 # Get the Tid associated
1937 if recvPacket['Tid'] in connData['ConnectedShares']:
1938 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
1939 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], queryInformation['FileName']))
1940 if not isInFileJail(path, fileName):
1941 smbServer.log("Path not in current working directory", logging.ERROR)
1942 errorCode = STATUS_OBJECT_PATH_SYNTAX_BAD
1944 else:
1945 fileSize, lastWriteTime, fileAttributes = queryFsInformation(path, fileName, pktFlags=recvPacket['Flags2'])
1947 respParameters['FileSize'] = fileSize
1948 respParameters['LastWriteTime'] = lastWriteTime
1949 respParameters['FileAttributes'] = fileAttributes
1950 errorCode = STATUS_SUCCESS
1951 else:
1952 errorCode = STATUS_SMB_BAD_TID
1954 if errorCode > 0:
1955 respParameters = b''
1956 respData = b''
1958 respSMBCommand['Parameters'] = respParameters
1959 respSMBCommand['Data'] = respData
1961 smbServer.setConnectionData(connId, connData)
1962 return [respSMBCommand], None, errorCode
1964 @staticmethod
1965 def smbQueryInformationDisk(connId, smbServer, SMBCommand, recvPacket):
1966 connData = smbServer.getConnectionData(connId)
1968 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION_DISK)
1969 respParameters = smb.SMBQueryInformationDiskResponse_Parameters()
1970 respData = b''
1972 # Get the Tid associated
1973 if recvPacket['Tid'] in connData['ConnectedShares']:
1974 totalUnits, freeUnits = queryDiskInformation(
1975 connData['ConnectedShares'][recvPacket['Tid']]['path'])
1977 respParameters['TotalUnits'] = totalUnits
1978 respParameters['BlocksPerUnit'] = 1
1979 respParameters['BlockSize'] = 1
1980 respParameters['FreeUnits'] = freeUnits
1981 errorCode = STATUS_SUCCESS
1982 else:
1983 errorCode = STATUS_SMB_BAD_TID
1985 if errorCode > 0:
1986 respData = b''
1987 respParameters = b''
1989 respSMBCommand['Parameters'] = respParameters
1990 respSMBCommand['Data'] = respData
1992 smbServer.setConnectionData(connId, connData)
1993 return [respSMBCommand], None, errorCode
1995 @staticmethod
1996 def smbComEcho(connId, smbServer, SMBCommand, recvPacket):
1997 connData = smbServer.getConnectionData(connId)
1999 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO)
2000 respParameters = smb.SMBEchoResponse_Parameters()
2001 respData = smb.SMBEchoResponse_Data()
2003 echoData = smb.SMBEcho_Data(SMBCommand['Data'])
2005 respParameters['SequenceNumber'] = 1
2006 respData['Data'] = echoData['Data']
2008 respSMBCommand['Parameters'] = respParameters
2009 respSMBCommand['Data'] = respData
2011 errorCode = STATUS_SUCCESS
2012 smbServer.setConnectionData(connId, connData)
2013 return [respSMBCommand], None, errorCode
2015 @staticmethod
2016 def smbComTreeDisconnect(connId, smbServer, SMBCommand, recvPacket):
2017 connData = smbServer.getConnectionData(connId)
2019 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_DISCONNECT)
2021 # Check if the Tid matches the Tid trying to disconnect
2022 respParameters = b''
2023 respData = b''
2025 if recvPacket['Tid'] in connData['ConnectedShares']:
2026 smbServer.log("Disconnecting Share(%d:%s)" % (
2027 recvPacket['Tid'], connData['ConnectedShares'][recvPacket['Tid']]['shareName']))
2028 del (connData['ConnectedShares'][recvPacket['Tid']])
2029 errorCode = STATUS_SUCCESS
2030 else:
2031 errorCode = STATUS_SMB_BAD_TID
2033 respSMBCommand['Parameters'] = respParameters
2034 respSMBCommand['Data'] = respData
2036 smbServer.setConnectionData(connId, connData)
2037 return [respSMBCommand], None, errorCode
2039 @staticmethod
2040 def smbComLogOffAndX(connId, smbServer, SMBCommand, recvPacket):
2041 connData = smbServer.getConnectionData(connId)
2043 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOGOFF_ANDX)
2045 # Check if the Uid matches the user trying to logoff
2046 respParameters = b''
2047 respData = b''
2048 if recvPacket['Uid'] != connData['Uid']:
2049 errorCode = STATUS_SMB_BAD_UID
2050 else:
2051 errorCode = STATUS_SUCCESS
2053 respSMBCommand['Parameters'] = respParameters
2054 respSMBCommand['Data'] = respData
2055 connData['Uid'] = 0
2056 connData['Authenticated'] = False
2058 smbServer.setConnectionData(connId, connData)
2060 return [respSMBCommand], None, errorCode
2062 @staticmethod
2063 def smbComQueryInformation2(connId, smbServer, SMBCommand, recvPacket):
2064 connData = smbServer.getConnectionData(connId)
2066 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION2)
2067 respParameters = smb.SMBQueryInformation2Response_Parameters()
2068 respData = b''
2070 queryInformation2 = smb.SMBQueryInformation2_Parameters(SMBCommand['Parameters'])
2071 errorCode = 0xFF
2073 # Get the Tid associated
2074 if recvPacket['Tid'] in connData['ConnectedShares']:
2075 if queryInformation2['Fid'] in connData['OpenedFiles']:
2076 errorCode = STATUS_SUCCESS
2077 pathName = connData['OpenedFiles'][queryInformation2['Fid']]['FileName']
2078 try:
2079 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)
2080 respParameters['CreateDate'] = getSMBDate(ctime)
2081 respParameters['CreationTime'] = getSMBTime(ctime)
2082 respParameters['LastAccessDate'] = getSMBDate(atime)
2083 respParameters['LastAccessTime'] = getSMBTime(atime)
2084 respParameters['LastWriteDate'] = getSMBDate(mtime)
2085 respParameters['LastWriteTime'] = getSMBTime(mtime)
2086 respParameters['FileDataSize'] = size
2087 respParameters['FileAllocationSize'] = size
2088 attribs = 0
2089 if os.path.isdir(pathName):
2090 attribs = smb.SMB_FILE_ATTRIBUTE_DIRECTORY
2091 if os.path.isfile(pathName):
2092 attribs = smb.SMB_FILE_ATTRIBUTE_NORMAL
2093 respParameters['FileAttributes'] = attribs
2094 except Exception as e:
2095 smbServer.log('smbComQueryInformation2 %s' % e, logging.ERROR)
2096 errorCode = STATUS_ACCESS_DENIED
2097 else:
2098 errorCode = STATUS_SMB_BAD_TID
2100 if errorCode > 0:
2101 respParameters = b''
2102 respData = b''
2104 respSMBCommand['Parameters'] = respParameters
2105 respSMBCommand['Data'] = respData
2106 smbServer.setConnectionData(connId, connData)
2108 return [respSMBCommand], None, errorCode
2110 @staticmethod
2111 def smbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket):
2112 # TODO: Fully implement this
2113 connData = smbServer.getConnectionData(connId)
2115 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX)
2116 respParameters = smb.SMBNtCreateAndXResponse_Parameters()
2117 respData = b''
2119 ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters'])
2120 ntCreateAndXData = smb.SMBNtCreateAndX_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
2122 # if ntCreateAndXParameters['CreateFlags'] & 0x10: # NT_CREATE_REQUEST_EXTENDED_RESPONSE
2123 # respParameters = smb.SMBNtCreateAndXExtendedResponse_Parameters()
2124 # respParameters['VolumeGUID'] = '\x00'
2126 # Get the Tid associated
2127 if recvPacket['Tid'] in connData['ConnectedShares']:
2128 # If we have a rootFid, the path is relative to that fid
2129 errorCode = STATUS_SUCCESS
2130 if ntCreateAndXParameters['RootFid'] > 0:
2131 path = connData['OpenedFiles'][ntCreateAndXParameters['RootFid']]['FileName']
2132 LOG.debug("RootFid present %s!" % path)
2133 else:
2134 if 'path' in connData['ConnectedShares'][recvPacket['Tid']]:
2135 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
2136 else:
2137 path = 'NONE'
2138 errorCode = STATUS_ACCESS_DENIED
2140 deleteOnClose = False
2142 fileName = normalize_path(decodeSMBString(recvPacket['Flags2'], ntCreateAndXData['FileName']))
2143 if not isInFileJail(path, fileName):
2144 LOG.error("Path not in current working directory")
2145 respSMBCommand['Parameters'] = b''
2146 respSMBCommand['Data'] = b''
2147 return [respSMBCommand], None, STATUS_OBJECT_PATH_SYNTAX_BAD
2149 pathName = os.path.join(path, fileName)
2150 createDisposition = ntCreateAndXParameters['Disposition']
2151 mode = 0
2153 if createDisposition == smb.FILE_SUPERSEDE:
2154 mode |= os.O_TRUNC | os.O_CREAT
2155 elif createDisposition & smb.FILE_OVERWRITE_IF == smb.FILE_OVERWRITE_IF:
2156 mode |= os.O_TRUNC | os.O_CREAT
2157 elif createDisposition & smb.FILE_OVERWRITE == smb.FILE_OVERWRITE:
2158 if os.path.exists(pathName) is True:
2159 mode |= os.O_TRUNC
2160 else:
2161 errorCode = STATUS_NO_SUCH_FILE
2162 elif createDisposition & smb.FILE_OPEN_IF == smb.FILE_OPEN_IF:
2163 if os.path.exists(pathName) is True:
2164 mode |= os.O_TRUNC
2165 else:
2166 mode |= os.O_TRUNC | os.O_CREAT
2167 elif createDisposition & smb.FILE_CREATE == smb.FILE_CREATE:
2168 if os.path.exists(pathName) is True:
2169 errorCode = STATUS_OBJECT_NAME_COLLISION
2170 else:
2171 mode |= os.O_CREAT
2172 elif createDisposition & smb.FILE_OPEN == smb.FILE_OPEN:
2173 if os.path.exists(pathName) is not True and (
2174 str(pathName) in smbServer.getRegisteredNamedPipes()) is not True:
2175 errorCode = STATUS_NO_SUCH_FILE
2177 if errorCode == STATUS_SUCCESS:
2178 desiredAccess = ntCreateAndXParameters['AccessMask']
2179 if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ):
2180 mode |= os.O_RDONLY
2181 if (desiredAccess & smb.FILE_WRITE_DATA) or (desiredAccess & smb.GENERIC_WRITE):
2182 if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ):
2183 mode |= os.O_RDWR # | os.O_APPEND
2184 else:
2185 mode |= os.O_WRONLY # | os.O_APPEND
2186 if desiredAccess & smb.GENERIC_ALL:
2187 mode |= os.O_RDWR # | os.O_APPEND
2189 createOptions = ntCreateAndXParameters['CreateOptions']
2190 if mode & os.O_CREAT == os.O_CREAT:
2191 if createOptions & smb.FILE_DIRECTORY_FILE == smb.FILE_DIRECTORY_FILE:
2192 try:
2193 # Let's create the directory
2194 os.mkdir(pathName)
2195 mode = os.O_RDONLY
2196 except Exception as e:
2197 smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName, mode, e), logging.ERROR)
2198 errorCode = STATUS_ACCESS_DENIED
2199 if createOptions & smb.FILE_NON_DIRECTORY_FILE == smb.FILE_NON_DIRECTORY_FILE:
2200 # If the file being opened is a directory, the server MUST fail the request with
2201 # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server
2202 # response.
2203 if os.path.isdir(pathName) is True:
2204 errorCode = STATUS_FILE_IS_A_DIRECTORY
2206 if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE:
2207 deleteOnClose = True
2209 if errorCode == STATUS_SUCCESS:
2210 try:
2211 if os.path.isdir(pathName) and sys.platform == 'win32':
2212 fid = VOID_FILE_DESCRIPTOR
2213 else:
2214 if sys.platform == 'win32':
2215 mode |= os.O_BINARY
2216 if str(pathName) in smbServer.getRegisteredNamedPipes():
2217 fid = PIPE_FILE_DESCRIPTOR
2218 sock = socket.socket()
2219 sock.connect(smbServer.getRegisteredNamedPipes()[str(pathName)])
2220 else:
2221 fid = os.open(pathName, mode)
2222 except Exception as e:
2223 smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName, mode, e), logging.ERROR)
2224 # print e
2225 fid = 0
2226 errorCode = STATUS_ACCESS_DENIED
2227 else:
2228 errorCode = STATUS_SMB_BAD_TID
2230 if errorCode == STATUS_SUCCESS:
2231 # Simple way to generate a fid
2232 if len(connData['OpenedFiles']) == 0:
2233 fakefid = 1
2234 else:
2235 fakefid = list(connData['OpenedFiles'].keys())[-1] + 1
2236 respParameters['Fid'] = fakefid
2237 respParameters['CreateAction'] = createDisposition
2238 if fid == PIPE_FILE_DESCRIPTOR:
2239 respParameters['FileAttributes'] = 0x80
2240 respParameters['IsDirectory'] = 0
2241 respParameters['CreateTime'] = 0
2242 respParameters['LastAccessTime'] = 0
2243 respParameters['LastWriteTime'] = 0
2244 respParameters['LastChangeTime'] = 0
2245 respParameters['AllocationSize'] = 4096
2246 respParameters['EndOfFile'] = 0
2247 respParameters['FileType'] = 2
2248 respParameters['IPCState'] = 0x5ff
2249 else:
2250 if os.path.isdir(pathName):
2251 respParameters['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY
2252 respParameters['IsDirectory'] = 1
2253 else:
2254 respParameters['IsDirectory'] = 0
2255 respParameters['FileAttributes'] = ntCreateAndXParameters['FileAttributes']
2256 # Let's get this file's information
2257 respInfo, errorCode = queryPathInformation(path, fileName, level=smb.SMB_QUERY_FILE_ALL_INFO)
2258 if errorCode == STATUS_SUCCESS:
2259 respParameters['CreateTime'] = respInfo['CreationTime']
2260 respParameters['LastAccessTime'] = respInfo['LastAccessTime']
2261 respParameters['LastWriteTime'] = respInfo['LastWriteTime']
2262 respParameters['LastChangeTime'] = respInfo['LastChangeTime']
2263 respParameters['FileAttributes'] = respInfo['ExtFileAttributes']
2264 respParameters['AllocationSize'] = respInfo['AllocationSize']
2265 respParameters['EndOfFile'] = respInfo['EndOfFile']
2266 else:
2267 respParameters = b''
2268 respData = b''
2270 if errorCode == STATUS_SUCCESS:
2271 # Let's store the fid for the connection
2272 # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode))
2273 connData['OpenedFiles'][fakefid] = {}
2274 connData['OpenedFiles'][fakefid]['FileHandle'] = fid
2275 connData['OpenedFiles'][fakefid]['FileName'] = pathName
2276 connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose
2277 if fid == PIPE_FILE_DESCRIPTOR:
2278 connData['OpenedFiles'][fakefid]['Socket'] = sock
2279 else:
2280 respParameters = b''
2281 respData = b''
2283 respSMBCommand['Parameters'] = respParameters
2284 respSMBCommand['Data'] = respData
2285 smbServer.setConnectionData(connId, connData)
2287 return [respSMBCommand], None, errorCode
2289 @staticmethod
2290 def smbComOpenAndX(connId, smbServer, SMBCommand, recvPacket):
2291 connData = smbServer.getConnectionData(connId)
2293 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_OPEN_ANDX)
2294 respParameters = smb.SMBOpenAndXResponse_Parameters()
2295 respData = b''
2297 openAndXParameters = smb.SMBOpenAndX_Parameters(SMBCommand['Parameters'])
2298 openAndXData = smb.SMBOpenAndX_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
2300 # Get the Tid associated
2301 if recvPacket['Tid'] in connData['ConnectedShares']:
2302 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
2303 openedFile, mode, pathName, errorCode = openFile(path,
2304 decodeSMBString(recvPacket['Flags2'],
2305 openAndXData['FileName']),
2306 openAndXParameters['DesiredAccess'],
2307 openAndXParameters['FileAttributes'],
2308 openAndXParameters['OpenMode'])
2309 else:
2310 errorCode = STATUS_SMB_BAD_TID
2312 if errorCode == STATUS_SUCCESS:
2313 # Simple way to generate a fid
2314 fid = len(connData['OpenedFiles']) + 1
2315 if len(connData['OpenedFiles']) == 0:
2316 fid = 1
2317 else:
2318 fid = list(connData['OpenedFiles'].keys())[-1] + 1
2319 respParameters['Fid'] = fid
2320 if mode & os.O_CREAT:
2321 # File did not exist and was created
2322 respParameters['Action'] = 0x2
2323 elif mode & os.O_RDONLY:
2324 # File existed and was opened
2325 respParameters['Action'] = 0x1
2326 elif mode & os.O_APPEND:
2327 # File existed and was opened
2328 respParameters['Action'] = 0x1
2329 else:
2330 # File existed and was truncated
2331 respParameters['Action'] = 0x3
2333 # Let's store the fid for the connection
2334 # smbServer.log('Opening file %s' % pathName)
2335 connData['OpenedFiles'][fid] = {}
2336 connData['OpenedFiles'][fid]['FileHandle'] = openedFile
2337 connData['OpenedFiles'][fid]['FileName'] = pathName
2338 connData['OpenedFiles'][fid]['DeleteOnClose'] = False
2339 else:
2340 respParameters = b''
2341 respData = b''
2343 respSMBCommand['Parameters'] = respParameters
2344 respSMBCommand['Data'] = respData
2345 smbServer.setConnectionData(connId, connData)
2347 return [respSMBCommand], None, errorCode
2349 @staticmethod
2350 def smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket):
2351 connData = smbServer.getConnectionData(connId)
2353 resp = smb.NewSMBPacket()
2354 resp['Flags1'] = smb.SMB.FLAGS1_REPLY
2355 resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \
2356 recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE
2358 resp['Tid'] = recvPacket['Tid']
2359 resp['Mid'] = recvPacket['Mid']
2360 resp['Pid'] = connData['Pid']
2362 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX)
2363 respParameters = smb.SMBTreeConnectAndXResponse_Parameters()
2364 respData = smb.SMBTreeConnectAndXResponse_Data()
2366 treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters'])
2368 if treeConnectAndXParameters['Flags'] & 0x8:
2369 respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters()
2371 treeConnectAndXData = smb.SMBTreeConnectAndX_Data(flags=recvPacket['Flags2'])
2372 treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength']
2373 treeConnectAndXData.fromString(SMBCommand['Data'])
2375 errorCode = STATUS_SUCCESS
2377 ## Process here the request, does the share exist?
2378 UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])
2380 # Is this a UNC?
2381 if ntpath.ismount(UNCOrShare):
2382 path = UNCOrShare.split('\\')[3]
2383 else:
2384 path = ntpath.basename(UNCOrShare)
2386 share = searchShare(connId, path, smbServer)
2387 if share is not None:
2388 # Simple way to generate a Tid
2389 if len(connData['ConnectedShares']) == 0:
2390 tid = 1
2391 else:
2392 tid = list(connData['ConnectedShares'].keys())[-1] + 1
2393 connData['ConnectedShares'][tid] = share
2394 connData['ConnectedShares'][tid]['shareName'] = path
2395 resp['Tid'] = tid
2396 # smbServer.log("Connecting Share(%d:%s)" % (tid,path))
2397 else:
2398 smbServer.log("TreeConnectAndX not found %s" % path, logging.ERROR)
2399 errorCode = STATUS_OBJECT_PATH_NOT_FOUND
2400 resp['ErrorCode'] = errorCode >> 16
2401 resp['ErrorClass'] = errorCode & 0xff
2402 ##
2403 respParameters['OptionalSupport'] = smb.SMB.SMB_SUPPORT_SEARCH_BITS
2405 if path == 'IPC$':
2406 respData['Service'] = 'IPC'
2407 else:
2408 respData['Service'] = path
2409 respData['PadLen'] = 0
2410 respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS').decode()
2412 respSMBCommand['Parameters'] = respParameters
2413 respSMBCommand['Data'] = respData
2415 resp['Uid'] = connData['Uid']
2416 resp.addCommand(respSMBCommand)
2418 # Sign the packet if needed
2419 if connData['SignatureEnabled']:
2420 smbServer.signSMBv1(connData, resp, connData['SigningSessionKey'], connData['SigningChallengeResponse'])
2421 smbServer.setConnectionData(connId, connData)
2423 return None, [resp], errorCode
2425 @staticmethod
2426 def smbComSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket):
2427 connData = smbServer.getConnectionData(connId, checkStatus=False)
2429 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)
2431 # From [MS-SMB]
2432 # When extended security is being used (see section 3.2.4.2.4), the
2433 # request MUST take the following form
2434 # [..]
2435 # WordCount (1 byte): The value of this field MUST be 0x0C.
2436 if SMBCommand['WordCount'] == 12:
2437 # Extended security. Here we deal with all SPNEGO stuff
2438 respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters()
2439 respData = smb.SMBSessionSetupAndX_Extended_Response_Data(flags=recvPacket['Flags2'])
2440 sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters'])
2441 sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data()
2442 sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength']
2443 sessionSetupData.fromString(SMBCommand['Data'])
2444 connData['Capabilities'] = sessionSetupParameters['Capabilities']
2446 rawNTLM = False
2447 if struct.unpack('B', sessionSetupData['SecurityBlob'][0:1])[0] == ASN1_AID:
2448 # NEGOTIATE packet
2449 blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob'])
2450 token = blob['MechToken']
2451 if len(blob['MechTypes'][0]) > 0:
2452 # Is this GSSAPI NTLM or something else we don't support?
2453 mechType = blob['MechTypes'][0]
2454 if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']:
2455 # Nope, do we know it?
2456 if mechType in MechTypes:
2457 mechStr = MechTypes[mechType]
2458 else:
2459 mechStr = hexlify(mechType)
2460 smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL)
2461 # We don't know the token, we answer back again saying
2462 # we just support NTLM.
2463 # ToDo: Build this into a SPNEGO_NegTokenResp()
2464 respToken = b'\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a'
2465 respParameters['SecurityBlobLength'] = len(respToken)
2466 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']
2467 respData['SecurityBlob'] = respToken
2468 respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
2469 respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
2470 respSMBCommand['Parameters'] = respParameters
2471 respSMBCommand['Data'] = respData
2472 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED
2474 elif struct.unpack('B', sessionSetupData['SecurityBlob'][0:1])[0] == ASN1_SUPPORTED_MECH:
2475 # AUTH packet
2476 blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob'])
2477 token = blob['ResponseToken']
2478 else:
2479 # No GSSAPI stuff, raw NTLMSSP
2480 rawNTLM = True
2481 token = sessionSetupData['SecurityBlob']
2483 # Here we only handle NTLMSSP, depending on what stage of the
2484 # authentication we are, we act on it
2485 messageType = struct.unpack('<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0]
2487 if messageType == 0x01:
2488 # NEGOTIATE_MESSAGE
2489 negotiateMessage = ntlm.NTLMAuthNegotiate()
2490 negotiateMessage.fromString(token)
2491 # Let's store it in the connection data
2492 connData['NEGOTIATE_MESSAGE'] = negotiateMessage
2493 # Let's build the answer flags
2494 # TODO: Parse all the flags. With this we're leaving some clients out
2496 ansFlags = 0
2498 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56:
2499 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56
2500 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128:
2501 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128
2502 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
2503 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
2504 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
2505 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
2506 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
2507 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE
2508 if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM:
2509 ansFlags |= ntlm.NTLM_NEGOTIATE_OEM
2511 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET
2513 # Generate the AV_PAIRS
2514 av_pairs = ntlm.AV_PAIRS()
2515 # TODO: Put the proper data from SMBSERVER config
2516 av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[
2517 ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le')
2518 av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[
2519 ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le')
2520 av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (
2521 116444736000000000 + calendar.timegm(time.gmtime()) * 10000000))
2523 challengeMessage = ntlm.NTLMAuthChallenge()
2524 challengeMessage['flags'] = ansFlags
2525 challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le'))
2526 challengeMessage['domain_max_len'] = challengeMessage['domain_len']
2527 challengeMessage['domain_offset'] = 40 + 16
2528 challengeMessage['challenge'] = smbServer.getSMBChallenge()
2529 challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le')
2530 challengeMessage['TargetInfoFields_len'] = len(av_pairs)
2531 challengeMessage['TargetInfoFields_max_len'] = len(av_pairs)
2532 challengeMessage['TargetInfoFields'] = av_pairs
2533 challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name'])
2534 challengeMessage['Version'] = b'\xff' * 8
2535 challengeMessage['VersionLen'] = 8
2537 if rawNTLM is False:
2538 respToken = SPNEGO_NegTokenResp()
2539 # accept-incomplete. We want more data
2540 respToken['NegState'] = b'\x01'
2541 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
2543 respToken['ResponseToken'] = challengeMessage.getData()
2544 else:
2545 respToken = challengeMessage
2547 # Setting the packet to STATUS_MORE_PROCESSING
2548 errorCode = STATUS_MORE_PROCESSING_REQUIRED
2549 # Let's set up an UID for this connection and store it
2550 # in the connection's data
2551 # Picking a fixed value
2552 # TODO: Manage more UIDs for the same session
2553 connData['Uid'] = 10
2554 # Let's store it in the connection data
2555 connData['CHALLENGE_MESSAGE'] = challengeMessage
2557 elif messageType == 0x02:
2558 # CHALLENGE_MESSAGE
2559 raise Exception('Challenge Message raise, not implemented!')
2560 elif messageType == 0x03:
2561 # AUTHENTICATE_MESSAGE, here we deal with authentication
2562 authenticateMessage = ntlm.NTLMAuthChallengeResponse()
2563 authenticateMessage.fromString(token)
2564 smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (
2565 authenticateMessage['domain_name'].decode('utf-16le'),
2566 authenticateMessage['user_name'].decode('utf-16le'),
2567 authenticateMessage['host_name'].decode('utf-16le')))
2568 # Do we have credentials to check?
2569 if len(smbServer.getCredentials()) > 0:
2570 identity = authenticateMessage['user_name'].decode('utf-16le').lower()
2571 # Do we have this user's credentials?
2572 if identity in smbServer.getCredentials():
2573 # Process data:
2574 # Let's parse some data and keep it to ourselves in case it is asked
2575 uid, lmhash, nthash = smbServer.getCredentials()[identity]
2577 errorCode, sessionKey = computeNTLMv2(identity, lmhash, nthash, smbServer.getSMBChallenge(),
2578 authenticateMessage, connData['CHALLENGE_MESSAGE'],
2579 connData['NEGOTIATE_MESSAGE'])
2581 if sessionKey is not None:
2582 connData['SignatureEnabled'] = False
2583 connData['SigningSessionKey'] = sessionKey
2584 connData['SignSequenceNumber'] = 1
2585 else:
2586 errorCode = STATUS_LOGON_FAILURE
2587 else:
2588 # No credentials provided, let's grant access
2589 errorCode = STATUS_SUCCESS
2591 if errorCode == STATUS_SUCCESS:
2592 connData['Authenticated'] = True
2593 respToken = SPNEGO_NegTokenResp()
2594 # accept-completed
2595 respToken['NegState'] = b'\x00'
2597 smbServer.log(
2598 'User %s\\%s authenticated successfully' % (authenticateMessage['host_name'].decode('utf-16le'),
2599 authenticateMessage['user_name'].decode(
2600 'utf-16le')))
2601 # Let's store it in the connection data
2602 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
2603 try:
2604 jtr_dump_path = smbServer.getJTRdumpPath()
2605 ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],
2606 authenticateMessage['user_name'],
2607 authenticateMessage['domain_name'],
2608 authenticateMessage['lanman'], authenticateMessage['ntlm'])
2609 smbServer.log(ntlm_hash_data['hash_string'])
2610 if jtr_dump_path != '':
2611 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
2612 jtr_dump_path)
2613 except:
2614 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
2615 else:
2616 respToken = SPNEGO_NegTokenResp()
2617 respToken['NegState'] = b'\x02'
2618 smbServer.log("Could not authenticate user!")
2619 else:
2620 raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
2622 respParameters['SecurityBlobLength'] = len(respToken)
2623 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']
2624 respData['SecurityBlob'] = respToken.getData()
2626 else:
2627 # Process Standard Security
2628 respParameters = smb.SMBSessionSetupAndXResponse_Parameters()
2629 respData = smb.SMBSessionSetupAndXResponse_Data()
2630 sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters'])
2631 sessionSetupData = smb.SMBSessionSetupAndX_Data()
2632 sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']
2633 sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']
2634 sessionSetupData.fromString(SMBCommand['Data'])
2635 connData['Capabilities'] = sessionSetupParameters['Capabilities']
2636 # Do the verification here, for just now we grant access
2637 # TODO: Manage more UIDs for the same session
2638 errorCode = STATUS_SUCCESS
2639 connData['Uid'] = 10
2640 connData['Authenticated'] = True
2641 respParameters['Action'] = 0
2642 smbServer.log('User %s\\%s authenticated successfully (basic)' % (
2643 sessionSetupData['PrimaryDomain'], sessionSetupData['Account']))
2644 try:
2645 jtr_dump_path = smbServer.getJTRdumpPath()
2646 ntlm_hash_data = outputToJohnFormat(b'', b(sessionSetupData['Account']),
2647 b(sessionSetupData['PrimaryDomain']), sessionSetupData['AnsiPwd'],
2648 sessionSetupData['UnicodePwd'])
2649 smbServer.log(ntlm_hash_data['hash_string'])
2650 if jtr_dump_path != '':
2651 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path)
2652 except:
2653 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
2655 respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
2656 respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
2657 respSMBCommand['Parameters'] = respParameters
2658 respSMBCommand['Data'] = respData
2660 # From now on, the client can ask for other commands
2661 connData['Authenticated'] = True
2662 # For now, just switching to nobody
2663 # os.setregid(65534,65534)
2664 # os.setreuid(65534,65534)
2665 smbServer.setConnectionData(connId, connData)
2667 return [respSMBCommand], None, errorCode
2669 @staticmethod
2670 def smbComNegotiate(connId, smbServer, SMBCommand, recvPacket):
2671 connData = smbServer.getConnectionData(connId, checkStatus=False)
2672 connData['Pid'] = recvPacket['Pid']
2674 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
2675 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NEGOTIATE)
2677 resp = smb.NewSMBPacket()
2678 resp['Flags1'] = smb.SMB.FLAGS1_REPLY
2679 resp['Pid'] = connData['Pid']
2680 resp['Tid'] = recvPacket['Tid']
2681 resp['Mid'] = recvPacket['Mid']
2683 # TODO: We support more dialects, and parse them accordingly
2684 dialects = SMBCommand['Data'].split(b'\x02')
2685 try:
2686 index = dialects.index(b'NT LM 0.12\x00') - 1
2687 # Let's fill the data for NTLM
2688 if recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY:
2689 resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE
2690 # resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS
2691 _dialects_data = smb.SMBExtended_Security_Data()
2692 _dialects_data['ServerGUID'] = b'A' * 16
2693 blob = SPNEGO_NegTokenInit()
2694 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
2695 _dialects_data['SecurityBlob'] = blob.getData()
2697 _dialects_parameters = smb.SMBExtended_Security_Parameters()
2698 _dialects_parameters[
2699 'Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY | smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS | smb.SMB.CAP_UNICODE
2700 _dialects_parameters['ChallengeLength'] = 0
2702 else:
2703 resp['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE
2704 _dialects_parameters = smb.SMBNTLMDialect_Parameters()
2705 _dialects_data = smb.SMBNTLMDialect_Data()
2706 _dialects_data['Payload'] = ''
2707 if 'EncryptionKey' in connData:
2708 _dialects_data['Challenge'] = connData['EncryptionKey']
2709 _dialects_parameters['ChallengeLength'] = len(_dialects_data.getData())
2710 else:
2711 # TODO: Handle random challenges, now one that can be used with rainbow tables
2712 _dialects_data['Challenge'] = b'\x11\x22\x33\x44\x55\x66\x77\x88'
2713 _dialects_parameters['ChallengeLength'] = 8
2714 _dialects_parameters['Capabilities'] = smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS
2716 # Let's see if we need to support RPC_REMOTE_APIS
2717 config = smbServer.getServerConfig()
2718 if config.has_option('global', 'rpc_apis'):
2719 if config.getboolean('global', 'rpc_apis') is True:
2720 _dialects_parameters['Capabilities'] |= smb.SMB.CAP_RPC_REMOTE_APIS
2722 _dialects_parameters['DialectIndex'] = index
2723 # _dialects_parameters['SecurityMode'] = smb.SMB.SECURITY_AUTH_ENCRYPTED | smb.SMB.SECURITY_SHARE_USER | smb.SMB.SECURITY_SIGNATURES_REQUIRED
2724 _dialects_parameters['SecurityMode'] = smb.SMB.SECURITY_AUTH_ENCRYPTED | smb.SMB.SECURITY_SHARE_USER
2725 _dialects_parameters['MaxMpxCount'] = 1
2726 _dialects_parameters['MaxNumberVcs'] = 1
2727 _dialects_parameters['MaxBufferSize'] = 64000
2728 _dialects_parameters['MaxRawSize'] = 65536
2729 _dialects_parameters['SessionKey'] = 0
2730 _dialects_parameters['LowDateTime'] = 0
2731 _dialects_parameters['HighDateTime'] = 0
2732 _dialects_parameters['ServerTimeZone'] = 0
2734 respSMBCommand['Data'] = _dialects_data
2735 respSMBCommand['Parameters'] = _dialects_parameters
2736 connData['_dialects_data'] = _dialects_data
2737 connData['_dialects_parameters'] = _dialects_parameters
2739 except Exception as e:
2740 # No NTLM throw an error
2741 smbServer.log('smbComNegotiate: %s' % e, logging.ERROR)
2742 respSMBCommand['Data'] = struct.pack('<H', 0xffff)
2744 smbServer.setConnectionData(connId, connData)
2746 resp.addCommand(respSMBCommand)
2748 return None, [resp], STATUS_SUCCESS
2750 @staticmethod
2751 def default(connId, smbServer, SMBCommand, recvPacket):
2752 # By default we return an SMB Packet with error not implemented
2753 smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'], logging.DEBUG)
2754 packet = smb.NewSMBPacket()
2755 packet['Flags1'] = smb.SMB.FLAGS1_REPLY
2756 packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS
2757 packet['Command'] = recvPacket['Command']
2758 packet['Pid'] = recvPacket['Pid']
2759 packet['Tid'] = recvPacket['Tid']
2760 packet['Mid'] = recvPacket['Mid']
2761 packet['Uid'] = recvPacket['Uid']
2762 packet['Data'] = b'\x00\x00\x00'
2763 errorCode = STATUS_NOT_IMPLEMENTED
2764 packet['ErrorCode'] = errorCode >> 16
2765 packet['ErrorClass'] = errorCode & 0xff
2767 return None, [packet], errorCode
2770class SMB2Commands:
2771 @staticmethod
2772 def smb2Negotiate(connId, smbServer, recvPacket, isSMB1=False):
2773 connData = smbServer.getConnectionData(connId, checkStatus=False)
2775 respPacket = smb2.SMB2Packet()
2776 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR
2777 respPacket['Status'] = STATUS_SUCCESS
2778 respPacket['CreditRequestResponse'] = 1
2779 respPacket['Command'] = smb2.SMB2_NEGOTIATE
2780 respPacket['SessionID'] = 0
2781 if isSMB1 is False:
2782 respPacket['MessageID'] = recvPacket['MessageID']
2783 else:
2784 respPacket['MessageID'] = 0
2785 respPacket['TreeID'] = 0
2787 respSMBCommand = smb2.SMB2Negotiate_Response()
2789 respSMBCommand['SecurityMode'] = 1
2790 if isSMB1 is True:
2791 # Let's first parse the packet to see if the client supports SMB2
2792 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
2794 dialects = SMBCommand['Data'].split(b'\x02')
2795 if b'SMB 2.002\x00' in dialects or b'SMB 2.???\x00' in dialects:
2796 respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002
2797 else:
2798 # Client does not support SMB2 fallbacking
2799 raise Exception('SMB2 not supported, fallbacking')
2800 else:
2801 respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002
2802 respSMBCommand['ServerGuid'] = b'A' * 16
2803 respSMBCommand['Capabilities'] = 0
2804 respSMBCommand['MaxTransactSize'] = 65536
2805 respSMBCommand['MaxReadSize'] = 65536
2806 respSMBCommand['MaxWriteSize'] = 65536
2807 respSMBCommand['SystemTime'] = getFileTime(calendar.timegm(time.gmtime()))
2808 respSMBCommand['ServerStartTime'] = getFileTime(calendar.timegm(time.gmtime()))
2809 respSMBCommand['SecurityBufferOffset'] = 0x80
2811 blob = SPNEGO_NegTokenInit()
2812 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
2814 respSMBCommand['Buffer'] = blob.getData()
2815 respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer'])
2817 respPacket['Data'] = respSMBCommand
2819 smbServer.setConnectionData(connId, connData)
2821 return None, [respPacket], STATUS_SUCCESS
2823 @staticmethod
2824 def smb2SessionSetup(connId, smbServer, recvPacket):
2825 connData = smbServer.getConnectionData(connId, checkStatus=False)
2827 respSMBCommand = smb2.SMB2SessionSetup_Response()
2829 sessionSetupData = smb2.SMB2SessionSetup(recvPacket['Data'])
2831 connData['Capabilities'] = sessionSetupData['Capabilities']
2833 securityBlob = sessionSetupData['Buffer']
2835 rawNTLM = False
2836 if struct.unpack('B', securityBlob[0:1])[0] == ASN1_AID:
2837 # NEGOTIATE packet
2838 blob = SPNEGO_NegTokenInit(securityBlob)
2839 token = blob['MechToken']
2840 if len(blob['MechTypes'][0]) > 0:
2841 # Is this GSSAPI NTLM or something else we don't support?
2842 mechType = blob['MechTypes'][0]
2843 if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']:
2844 # Nope, do we know it?
2845 if mechType in MechTypes:
2846 mechStr = MechTypes[mechType]
2847 else:
2848 mechStr = hexlify(mechType)
2849 smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL)
2850 # We don't know the token, we answer back again saying
2851 # we just support NTLM.
2852 # ToDo: Build this into a SPNEGO_NegTokenResp()
2853 respToken = b'\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a'
2854 respSMBCommand['SecurityBufferOffset'] = 0x48
2855 respSMBCommand['SecurityBufferLength'] = len(respToken)
2856 respSMBCommand['Buffer'] = respToken
2858 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED
2859 elif struct.unpack('B', securityBlob[0:1])[0] == ASN1_SUPPORTED_MECH:
2860 # AUTH packet
2861 blob = SPNEGO_NegTokenResp(securityBlob)
2862 token = blob['ResponseToken']
2863 else:
2864 # No GSSAPI stuff, raw NTLMSSP
2865 rawNTLM = True
2866 token = securityBlob
2868 # Here we only handle NTLMSSP, depending on what stage of the
2869 # authentication we are, we act on it
2870 messageType = struct.unpack('<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0]
2872 if messageType == 0x01:
2873 # NEGOTIATE_MESSAGE
2874 negotiateMessage = ntlm.NTLMAuthNegotiate()
2875 negotiateMessage.fromString(token)
2876 # Let's store it in the connection data
2877 connData['NEGOTIATE_MESSAGE'] = negotiateMessage
2878 # Let's build the answer flags
2879 # TODO: Parse all the flags. With this we're leaving some clients out
2881 ansFlags = 0
2883 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56:
2884 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56
2885 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128:
2886 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128
2887 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
2888 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
2889 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
2890 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
2891 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
2892 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE
2893 if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM:
2894 ansFlags |= ntlm.NTLM_NEGOTIATE_OEM
2896 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET
2898 # Generate the AV_PAIRS
2899 av_pairs = ntlm.AV_PAIRS()
2900 # TODO: Put the proper data from SMBSERVER config
2901 av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[
2902 ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le')
2903 av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[
2904 ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le')
2905 av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (
2906 116444736000000000 + calendar.timegm(time.gmtime()) * 10000000))
2908 challengeMessage = ntlm.NTLMAuthChallenge()
2909 challengeMessage['flags'] = ansFlags
2910 challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le'))
2911 challengeMessage['domain_max_len'] = challengeMessage['domain_len']
2912 challengeMessage['domain_offset'] = 40 + 16
2913 challengeMessage['challenge'] = smbServer.getSMBChallenge()
2914 challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le')
2915 challengeMessage['TargetInfoFields_len'] = len(av_pairs)
2916 challengeMessage['TargetInfoFields_max_len'] = len(av_pairs)
2917 challengeMessage['TargetInfoFields'] = av_pairs
2918 challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name'])
2919 challengeMessage['Version'] = b'\xff' * 8
2920 challengeMessage['VersionLen'] = 8
2922 if rawNTLM is False:
2923 respToken = SPNEGO_NegTokenResp()
2924 # accept-incomplete. We want more data
2925 respToken['NegState'] = b'\x01'
2926 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
2928 respToken['ResponseToken'] = challengeMessage.getData()
2929 else:
2930 respToken = challengeMessage
2932 # Setting the packet to STATUS_MORE_PROCESSING
2933 errorCode = STATUS_MORE_PROCESSING_REQUIRED
2934 # Let's set up an UID for this connection and store it
2935 # in the connection's data
2936 # Picking a fixed value
2937 # TODO: Manage more UIDs for the same session
2938 connData['Uid'] = random.randint(1, 0xffffffff)
2939 # Let's store it in the connection data
2940 connData['CHALLENGE_MESSAGE'] = challengeMessage
2942 elif messageType == 0x02:
2943 # CHALLENGE_MESSAGE
2944 raise Exception('Challenge Message raise, not implemented!')
2945 elif messageType == 0x03:
2946 # AUTHENTICATE_MESSAGE, here we deal with authentication
2947 authenticateMessage = ntlm.NTLMAuthChallengeResponse()
2948 authenticateMessage.fromString(token)
2949 smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (
2950 authenticateMessage['domain_name'].decode('utf-16le'),
2951 authenticateMessage['user_name'].decode('utf-16le'),
2952 authenticateMessage['host_name'].decode('utf-16le')))
2954 isGuest = False
2955 isAnonymus = False
2957 # TODO: Check the credentials! Now granting permissions
2958 # Do we have credentials to check?
2959 if len(smbServer.getCredentials()) > 0:
2960 identity = authenticateMessage['user_name'].decode('utf-16le').lower()
2961 # Do we have this user's credentials?
2962 if identity in smbServer.getCredentials():
2963 # Process data:
2964 # Let's parse some data and keep it to ourselves in case it is asked
2965 uid, lmhash, nthash = smbServer.getCredentials()[identity]
2967 errorCode, sessionKey = computeNTLMv2(identity, lmhash, nthash, smbServer.getSMBChallenge(),
2968 authenticateMessage, connData['CHALLENGE_MESSAGE'],
2969 connData['NEGOTIATE_MESSAGE'])
2971 if sessionKey is not None:
2972 connData['SignatureEnabled'] = True
2973 connData['SigningSessionKey'] = sessionKey
2974 connData['SignSequenceNumber'] = 1
2975 else:
2976 errorCode = STATUS_LOGON_FAILURE
2977 else:
2978 # No credentials provided, let's grant access
2979 if authenticateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_ANONYMOUS:
2980 isAnonymus = True
2981 if smbServer._SMBSERVER__anonymousLogon == False:
2982 errorCode = STATUS_ACCESS_DENIED
2983 else:
2984 errorCode = STATUS_SUCCESS
2985 else:
2986 isGuest = True
2987 errorCode = STATUS_SUCCESS
2989 if errorCode == STATUS_SUCCESS:
2990 connData['Authenticated'] = True
2991 respToken = SPNEGO_NegTokenResp()
2992 # accept-completed
2993 respToken['NegState'] = b'\x00'
2994 smbServer.log('User %s\\%s authenticated successfully' % (
2995 authenticateMessage['host_name'].decode('utf-16le'),
2996 authenticateMessage['user_name'].decode('utf-16le')))
2997 # Let's store it in the connection data
2998 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
2999 try:
3000 jtr_dump_path = smbServer.getJTRdumpPath()
3001 ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],
3002 authenticateMessage['user_name'],
3003 authenticateMessage['domain_name'],
3004 authenticateMessage['lanman'], authenticateMessage['ntlm'])
3005 smbServer.log(ntlm_hash_data['hash_string'])
3006 if jtr_dump_path != '':
3007 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
3008 jtr_dump_path)
3009 except:
3010 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
3012 if isGuest:
3013 respSMBCommand['SessionFlags'] = 1
3014 elif isAnonymus:
3015 respSMBCommand['SessionFlags'] = 2
3017 else:
3018 respToken = SPNEGO_NegTokenResp()
3019 respToken['NegState'] = b'\x02'
3020 smbServer.log("Could not authenticate user!")
3021 else:
3022 raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
3024 respSMBCommand['SecurityBufferOffset'] = 0x48
3025 respSMBCommand['SecurityBufferLength'] = len(respToken)
3026 respSMBCommand['Buffer'] = respToken.getData()
3028 # From now on, the client can ask for other commands
3029 connData['Authenticated'] = True
3030 # For now, just switching to nobody
3031 # os.setregid(65534,65534)
3032 # os.setreuid(65534,65534)
3033 smbServer.setConnectionData(connId, connData)
3035 return [respSMBCommand], None, errorCode
3037 @staticmethod
3038 def smb2TreeConnect(connId, smbServer, recvPacket):
3039 connData = smbServer.getConnectionData(connId)
3041 respPacket = smb2.SMB2Packet()
3042 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR
3043 respPacket['Status'] = STATUS_SUCCESS
3044 respPacket['CreditRequestResponse'] = 1
3045 respPacket['Command'] = recvPacket['Command']
3046 respPacket['SessionID'] = connData['Uid']
3047 respPacket['Reserved'] = recvPacket['Reserved']
3048 respPacket['MessageID'] = recvPacket['MessageID']
3049 respPacket['TreeID'] = recvPacket['TreeID']
3051 respSMBCommand = smb2.SMB2TreeConnect_Response()
3053 treeConnectRequest = smb2.SMB2TreeConnect(recvPacket['Data'])
3055 errorCode = STATUS_SUCCESS
3057 ## Process here the request, does the share exist?
3058 path = recvPacket.getData()[treeConnectRequest['PathOffset']:][:treeConnectRequest['PathLength']]
3059 UNCOrShare = path.decode('utf-16le')
3061 # Is this a UNC?
3062 if ntpath.ismount(UNCOrShare):
3063 path = UNCOrShare.split('\\')[3]
3064 else:
3065 path = ntpath.basename(UNCOrShare)
3067 share = searchShare(connId, path.upper(), smbServer)
3068 if share is not None:
3069 # Simple way to generate a Tid
3070 if len(connData['ConnectedShares']) == 0:
3071 tid = 1
3072 else:
3073 tid = list(connData['ConnectedShares'].keys())[-1] + 1
3074 connData['ConnectedShares'][tid] = share
3075 connData['ConnectedShares'][tid]['shareName'] = path
3076 respPacket['TreeID'] = tid
3077 smbServer.log("Connecting Share(%d:%s)" % (tid, path))
3078 else:
3079 smbServer.log("SMB2_TREE_CONNECT not found %s" % path, logging.ERROR)
3080 errorCode = STATUS_OBJECT_PATH_NOT_FOUND
3081 respPacket['Status'] = errorCode
3082 ##
3084 if path.upper() == 'IPC$':
3085 respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_PIPE
3086 respSMBCommand['ShareFlags'] = 0x30
3087 else:
3088 respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_DISK
3089 respSMBCommand['ShareFlags'] = 0x0
3091 respSMBCommand['Capabilities'] = 0
3092 respSMBCommand['MaximalAccess'] = 0x000f01ff
3094 respPacket['Data'] = respSMBCommand
3096 # Sign the packet if needed
3097 if connData['SignatureEnabled']:
3098 smbServer.signSMBv2(respPacket, connData['SigningSessionKey'])
3099 smbServer.setConnectionData(connId, connData)
3101 return None, [respPacket], errorCode
3103 @staticmethod
3104 def smb2Create(connId, smbServer, recvPacket):
3105 connData = smbServer.getConnectionData(connId)
3107 respSMBCommand = smb2.SMB2Create_Response()
3109 ntCreateRequest = smb2.SMB2Create(recvPacket['Data'])
3111 respSMBCommand['Buffer'] = b'\x00'
3112 # Get the Tid associated
3113 if recvPacket['TreeID'] in connData['ConnectedShares']:
3114 # If we have a rootFid, the path is relative to that fid
3115 errorCode = STATUS_SUCCESS
3116 if 'path' in connData['ConnectedShares'][recvPacket['TreeID']]:
3117 path = connData['ConnectedShares'][recvPacket['TreeID']]['path']
3118 else:
3119 path = 'NONE'
3120 errorCode = STATUS_ACCESS_DENIED
3122 deleteOnClose = False
3124 fileName = normalize_path(ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le'))
3126 if not isInFileJail(path, fileName):
3127 LOG.error("Path not in current working directory")
3128 return [smb2.SMB2Error()], None, STATUS_OBJECT_PATH_SYNTAX_BAD
3130 pathName = os.path.join(path, fileName)
3131 createDisposition = ntCreateRequest['CreateDisposition']
3132 mode = 0
3134 if createDisposition == smb2.FILE_SUPERSEDE:
3135 mode |= os.O_TRUNC | os.O_CREAT
3136 elif createDisposition & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF:
3137 mode |= os.O_TRUNC | os.O_CREAT
3138 elif createDisposition & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE:
3139 if os.path.exists(pathName) is True:
3140 mode |= os.O_TRUNC
3141 else:
3142 errorCode = STATUS_NO_SUCH_FILE
3143 elif createDisposition & smb2.FILE_OPEN_IF == smb2.FILE_OPEN_IF:
3144 if os.path.exists(pathName) is True:
3145 mode |= os.O_TRUNC
3146 else:
3147 mode |= os.O_TRUNC | os.O_CREAT
3148 elif createDisposition & smb2.FILE_CREATE == smb2.FILE_CREATE:
3149 if os.path.exists(pathName) is True:
3150 errorCode = STATUS_OBJECT_NAME_COLLISION
3151 else:
3152 mode |= os.O_CREAT
3153 elif createDisposition & smb2.FILE_OPEN == smb2.FILE_OPEN:
3154 if os.path.exists(pathName) is not True and (
3155 str(pathName) in smbServer.getRegisteredNamedPipes()) is not True:
3156 errorCode = STATUS_NO_SUCH_FILE
3158 if errorCode == STATUS_SUCCESS:
3159 desiredAccess = ntCreateRequest['DesiredAccess']
3160 if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):
3161 mode |= os.O_RDONLY
3162 if (desiredAccess & smb2.FILE_WRITE_DATA) or (desiredAccess & smb2.GENERIC_WRITE):
3163 if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):
3164 mode |= os.O_RDWR # | os.O_APPEND
3165 else:
3166 mode |= os.O_WRONLY # | os.O_APPEND
3167 if desiredAccess & smb2.GENERIC_ALL:
3168 mode |= os.O_RDWR # | os.O_APPEND
3170 createOptions = ntCreateRequest['CreateOptions']
3171 if mode & os.O_CREAT == os.O_CREAT:
3172 if createOptions & smb2.FILE_DIRECTORY_FILE == smb2.FILE_DIRECTORY_FILE:
3173 try:
3174 # Let's create the directory
3175 os.mkdir(pathName)
3176 mode = os.O_RDONLY
3177 except Exception as e:
3178 smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName, mode, e), logging.ERROR)
3179 errorCode = STATUS_ACCESS_DENIED
3180 if createOptions & smb2.FILE_NON_DIRECTORY_FILE == smb2.FILE_NON_DIRECTORY_FILE:
3181 # If the file being opened is a directory, the server MUST fail the request with
3182 # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server
3183 # response.
3184 if os.path.isdir(pathName) is True:
3185 errorCode = STATUS_FILE_IS_A_DIRECTORY
3187 if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE:
3188 deleteOnClose = True
3190 if errorCode == STATUS_SUCCESS:
3191 try:
3192 if os.path.isdir(pathName) and sys.platform == 'win32':
3193 fid = VOID_FILE_DESCRIPTOR
3194 else:
3195 if sys.platform == 'win32':
3196 mode |= os.O_BINARY
3197 if str(pathName) in smbServer.getRegisteredNamedPipes():
3198 fid = PIPE_FILE_DESCRIPTOR
3199 sock = socket.socket()
3200 sock.connect(smbServer.getRegisteredNamedPipes()[str(pathName)])
3201 else:
3202 fid = os.open(pathName, mode)
3203 except Exception as e:
3204 smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName, mode, e), logging.ERROR)
3205 # print e
3206 fid = 0
3207 errorCode = STATUS_ACCESS_DENIED
3208 else:
3209 errorCode = STATUS_SMB_BAD_TID
3211 if errorCode == STATUS_SUCCESS:
3212 # Simple way to generate a fid
3213 fakefid = uuid.generate()
3215 respSMBCommand['FileID'] = fakefid
3216 respSMBCommand['CreateAction'] = createDisposition
3218 if fid == PIPE_FILE_DESCRIPTOR:
3219 respSMBCommand['CreationTime'] = 0
3220 respSMBCommand['LastAccessTime'] = 0
3221 respSMBCommand['LastWriteTime'] = 0
3222 respSMBCommand['ChangeTime'] = 0
3223 respSMBCommand['AllocationSize'] = 4096
3224 respSMBCommand['EndOfFile'] = 0
3225 respSMBCommand['FileAttributes'] = 0x80
3227 else:
3228 if os.path.isdir(pathName):
3229 respSMBCommand['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY
3230 else:
3231 respSMBCommand['FileAttributes'] = ntCreateRequest['FileAttributes']
3232 # Let's get this file's information
3233 respInfo, errorCode = queryPathInformation(path, fileName, level=smb.SMB_QUERY_FILE_ALL_INFO)
3234 if errorCode == STATUS_SUCCESS:
3235 respSMBCommand['CreationTime'] = respInfo['CreationTime']
3236 respSMBCommand['LastAccessTime'] = respInfo['LastAccessTime']
3237 respSMBCommand['LastWriteTime'] = respInfo['LastWriteTime']
3238 respSMBCommand['LastChangeTime'] = respInfo['LastChangeTime']
3239 respSMBCommand['FileAttributes'] = respInfo['ExtFileAttributes']
3240 respSMBCommand['AllocationSize'] = respInfo['AllocationSize']
3241 respSMBCommand['EndOfFile'] = respInfo['EndOfFile']
3243 if errorCode == STATUS_SUCCESS:
3244 # Let's store the fid for the connection
3245 # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode))
3246 connData['OpenedFiles'][fakefid] = {}
3247 connData['OpenedFiles'][fakefid]['FileHandle'] = fid
3248 connData['OpenedFiles'][fakefid]['FileName'] = pathName
3249 connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose
3250 connData['OpenedFiles'][fakefid]['Open'] = {}
3251 connData['OpenedFiles'][fakefid]['Open']['EnumerationLocation'] = 0
3252 connData['OpenedFiles'][fakefid]['Open']['EnumerationSearchPattern'] = ''
3253 if fid == PIPE_FILE_DESCRIPTOR:
3254 connData['OpenedFiles'][fakefid]['Socket'] = sock
3255 else:
3256 respSMBCommand = smb2.SMB2Error()
3258 if errorCode == STATUS_SUCCESS:
3259 connData['LastRequest']['SMB2_CREATE'] = respSMBCommand
3260 smbServer.setConnectionData(connId, connData)
3262 return [respSMBCommand], None, errorCode
3264 @staticmethod
3265 def smb2Close(connId, smbServer, recvPacket):
3266 connData = smbServer.getConnectionData(connId)
3268 respSMBCommand = smb2.SMB2Close_Response()
3270 closeRequest = smb2.SMB2Close(recvPacket['Data'])
3272 if closeRequest['FileID'].getData() == b'\xff' * 16:
3273 # Let's take the data from the lastRequest
3274 if 'SMB2_CREATE' in connData['LastRequest']:
3275 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3276 else:
3277 fileID = closeRequest['FileID'].getData()
3278 else:
3279 fileID = closeRequest['FileID'].getData()
3281 # Get the Tid associated
3282 if recvPacket['TreeID'] in connData['ConnectedShares']:
3283 if fileID in connData['OpenedFiles']:
3284 errorCode = STATUS_SUCCESS
3285 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
3286 pathName = connData['OpenedFiles'][fileID]['FileName']
3287 infoRecord = None
3288 try:
3289 if fileHandle == PIPE_FILE_DESCRIPTOR:
3290 connData['OpenedFiles'][fileID]['Socket'].close()
3291 elif fileHandle != VOID_FILE_DESCRIPTOR:
3292 os.close(fileHandle)
3293 infoRecord, errorCode = queryFileInformation(os.path.dirname(pathName), os.path.basename(pathName),
3294 smb2.SMB2_FILE_NETWORK_OPEN_INFO)
3295 except Exception as e:
3296 smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR)
3297 errorCode = STATUS_INVALID_HANDLE
3298 else:
3299 # Check if the file was marked for removal
3300 if connData['OpenedFiles'][fileID]['DeleteOnClose'] is True:
3301 try:
3302 if os.path.isdir(pathName):
3303 shutil.rmtree(connData['OpenedFiles'][fileID]['FileName'])
3304 else:
3305 os.remove(connData['OpenedFiles'][fileID]['FileName'])
3306 except Exception as e:
3307 smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR)
3308 errorCode = STATUS_ACCESS_DENIED
3310 # Now fill out the response
3311 if infoRecord is not None:
3312 respSMBCommand['CreationTime'] = infoRecord['CreationTime']
3313 respSMBCommand['LastAccessTime'] = infoRecord['LastAccessTime']
3314 respSMBCommand['LastWriteTime'] = infoRecord['LastWriteTime']
3315 respSMBCommand['ChangeTime'] = infoRecord['ChangeTime']
3316 respSMBCommand['AllocationSize'] = infoRecord['AllocationSize']
3317 respSMBCommand['EndofFile'] = infoRecord['EndOfFile']
3318 respSMBCommand['FileAttributes'] = infoRecord['FileAttributes']
3319 if errorCode == STATUS_SUCCESS:
3320 del (connData['OpenedFiles'][fileID])
3321 else:
3322 errorCode = STATUS_INVALID_HANDLE
3323 else:
3324 errorCode = STATUS_SMB_BAD_TID
3326 smbServer.setConnectionData(connId, connData)
3327 return [respSMBCommand], None, errorCode
3329 @staticmethod
3330 def smb2QueryInfo(connId, smbServer, recvPacket):
3331 connData = smbServer.getConnectionData(connId)
3333 respSMBCommand = smb2.SMB2QueryInfo_Response()
3335 queryInfo = smb2.SMB2QueryInfo(recvPacket['Data'])
3337 errorCode = STATUS_SUCCESS
3339 respSMBCommand['OutputBufferOffset'] = 0x48
3340 respSMBCommand['Buffer'] = b'\x00'
3342 if queryInfo['FileID'].getData() == b'\xff' * 16:
3343 # Let's take the data from the lastRequest
3344 if 'SMB2_CREATE' in connData['LastRequest']:
3345 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3346 else:
3347 fileID = queryInfo['FileID'].getData()
3348 else:
3349 fileID = queryInfo['FileID'].getData()
3351 # Get the Tid associated
3352 if recvPacket['TreeID'] in connData['ConnectedShares']:
3353 if fileID in connData['OpenedFiles']:
3354 fileName = connData['OpenedFiles'][fileID]['FileName']
3356 if queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILE:
3357 if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_INTERNAL_INFO:
3358 # No need to call queryFileInformation, we have the data here
3359 infoRecord = smb2.FILE_INTERNAL_INFORMATION()
3360 infoRecord['IndexNumber'] = fileID
3361 else:
3362 infoRecord, errorCode = queryFileInformation(os.path.dirname(fileName),
3363 os.path.basename(fileName),
3364 queryInfo['FileInfoClass'])
3365 elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM:
3366 if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_EA_INFO:
3367 infoRecord = b'\x00' * 4
3368 else:
3369 infoRecord = queryFsInformation(os.path.dirname(fileName), os.path.basename(fileName),
3370 queryInfo['FileInfoClass'])
3371 elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY:
3372 # Failing for now, until we support it
3373 infoRecord = None
3374 errorCode = STATUS_ACCESS_DENIED
3375 else:
3376 smbServer.log("queryInfo not supported (%x)" % queryInfo['InfoType'], logging.ERROR)
3378 if infoRecord is not None:
3379 respSMBCommand['OutputBufferLength'] = len(infoRecord)
3380 respSMBCommand['Buffer'] = infoRecord
3381 else:
3382 errorCode = STATUS_INVALID_HANDLE
3383 else:
3384 errorCode = STATUS_SMB_BAD_TID
3386 smbServer.setConnectionData(connId, connData)
3387 return [respSMBCommand], None, errorCode
3389 @staticmethod
3390 def smb2SetInfo(connId, smbServer, recvPacket):
3391 connData = smbServer.getConnectionData(connId)
3393 respSMBCommand = smb2.SMB2SetInfo_Response()
3395 setInfo = smb2.SMB2SetInfo(recvPacket['Data'])
3397 errorCode = STATUS_SUCCESS
3399 if setInfo['FileID'].getData() == b'\xff' * 16:
3400 # Let's take the data from the lastRequest
3401 if 'SMB2_CREATE' in connData['LastRequest']:
3402 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3403 else:
3404 fileID = setInfo['FileID'].getData()
3405 else:
3406 fileID = setInfo['FileID'].getData()
3408 # Get the Tid associated
3409 if recvPacket['TreeID'] in connData['ConnectedShares']:
3410 path = connData['ConnectedShares'][recvPacket['TreeID']]['path']
3411 if fileID in connData['OpenedFiles']:
3412 pathName = connData['OpenedFiles'][fileID]['FileName']
3414 if setInfo['InfoType'] == smb2.SMB2_0_INFO_FILE:
3415 # The file information is being set
3416 informationLevel = setInfo['FileInfoClass']
3417 if informationLevel == smb2.SMB2_FILE_DISPOSITION_INFO:
3418 infoRecord = smb.SMBSetFileDispositionInfo(setInfo['Buffer'])
3419 if infoRecord['DeletePending'] > 0:
3420 # Mark this file for removal after closed
3421 connData['OpenedFiles'][fileID]['DeleteOnClose'] = True
3422 elif informationLevel == smb2.SMB2_FILE_BASIC_INFO:
3423 infoRecord = smb.SMBSetFileBasicInfo(setInfo['Buffer'])
3424 # Creation time won't be set, the other ones we play with.
3425 atime = infoRecord['LastWriteTime']
3426 if atime == 0:
3427 atime = -1
3428 else:
3429 atime = getUnixTime(atime)
3430 mtime = infoRecord['ChangeTime']
3431 if mtime == 0:
3432 mtime = -1
3433 else:
3434 mtime = getUnixTime(mtime)
3435 if atime > 0 and mtime > 0:
3436 os.utime(pathName, (atime, mtime))
3437 elif informationLevel == smb2.SMB2_FILE_END_OF_FILE_INFO:
3438 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
3439 infoRecord = smb.SMBSetFileEndOfFileInfo(setInfo['Buffer'])
3440 if infoRecord['EndOfFile'] > 0:
3441 os.lseek(fileHandle, infoRecord['EndOfFile'] - 1, 0)
3442 os.write(fileHandle, b'\x00')
3443 elif informationLevel == smb2.SMB2_FILE_RENAME_INFO:
3444 renameInfo = smb2.FILE_RENAME_INFORMATION_TYPE_2(setInfo['Buffer'])
3445 newFileName = normalize_path(renameInfo['FileName'].decode('utf-16le'))
3446 newPathName = os.path.join(path, newFileName)
3447 if not isInFileJail(path, newFileName):
3448 smbServer.log("Path not in current working directory", logging.ERROR)
3449 return [smb2.SMB2Error()], None, STATUS_OBJECT_PATH_SYNTAX_BAD
3451 if renameInfo['ReplaceIfExists'] == 0 and os.path.exists(newPathName):
3452 return [smb2.SMB2Error()], None, STATUS_OBJECT_NAME_COLLISION
3453 try:
3454 os.rename(pathName, newPathName)
3455 connData['OpenedFiles'][fileID]['FileName'] = newPathName
3456 except Exception as e:
3457 smbServer.log("smb2SetInfo: %s" % e, logging.ERROR)
3458 errorCode = STATUS_ACCESS_DENIED
3459 else:
3460 smbServer.log('Unknown level for set file info! 0x%x' % informationLevel, logging.ERROR)
3461 # UNSUPPORTED
3462 errorCode = STATUS_NOT_SUPPORTED
3463 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM:
3464 # # The underlying object store information is being set.
3465 # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass'])
3466 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY:
3467 # # The security information is being set.
3468 # # Failing for now, until we support it
3469 # infoRecord = None
3470 # errorCode = STATUS_ACCESS_DENIED
3471 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_QUOTA:
3472 # # The underlying object store quota information is being set.
3473 # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass'])
3474 else:
3475 smbServer.log("setInfo not supported (%x)" % setInfo['InfoType'], logging.ERROR)
3477 else:
3478 errorCode = STATUS_INVALID_HANDLE
3479 else:
3480 errorCode = STATUS_SMB_BAD_TID
3482 smbServer.setConnectionData(connId, connData)
3483 return [respSMBCommand], None, errorCode
3485 @staticmethod
3486 def smb2Write(connId, smbServer, recvPacket):
3487 connData = smbServer.getConnectionData(connId)
3489 respSMBCommand = smb2.SMB2Write_Response()
3490 writeRequest = smb2.SMB2Write(recvPacket['Data'])
3492 respSMBCommand['Buffer'] = b'\x00'
3494 if writeRequest['FileID'].getData() == b'\xff' * 16:
3495 # Let's take the data from the lastRequest
3496 if 'SMB2_CREATE' in connData['LastRequest']:
3497 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3498 else:
3499 fileID = writeRequest['FileID'].getData()
3500 else:
3501 fileID = writeRequest['FileID'].getData()
3503 # Get the Tid associated
3504 if recvPacket['TreeID'] in connData['ConnectedShares']:
3505 if fileID in connData['OpenedFiles']:
3506 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
3507 errorCode = STATUS_SUCCESS
3508 try:
3509 if fileHandle != PIPE_FILE_DESCRIPTOR:
3510 offset = writeRequest['Offset']
3511 # If we're trying to write past the file end we just skip the write call (Vista does this)
3512 if os.lseek(fileHandle, 0, 2) >= offset:
3513 os.lseek(fileHandle, offset, 0)
3514 os.write(fileHandle, writeRequest['Buffer'])
3515 else:
3516 sock = connData['OpenedFiles'][fileID]['Socket']
3517 sock.send(writeRequest['Buffer'])
3519 respSMBCommand['Count'] = writeRequest['Length']
3520 respSMBCommand['Remaining'] = 0xff
3521 except Exception as e:
3522 smbServer.log('SMB2_WRITE: %s' % e, logging.ERROR)
3523 errorCode = STATUS_ACCESS_DENIED
3524 else:
3525 errorCode = STATUS_INVALID_HANDLE
3526 else:
3527 errorCode = STATUS_SMB_BAD_TID
3529 smbServer.setConnectionData(connId, connData)
3530 return [respSMBCommand], None, errorCode
3532 @staticmethod
3533 def smb2Read(connId, smbServer, recvPacket):
3534 connData = smbServer.getConnectionData(connId)
3536 respSMBCommand = smb2.SMB2Read_Response()
3537 readRequest = smb2.SMB2Read(recvPacket['Data'])
3539 respSMBCommand['Buffer'] = b'\x00'
3541 if readRequest['FileID'].getData() == b'\xff' * 16:
3542 # Let's take the data from the lastRequest
3543 if 'SMB2_CREATE' in connData['LastRequest']:
3544 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3545 else:
3546 fileID = readRequest['FileID'].getData()
3547 else:
3548 fileID = readRequest['FileID'].getData()
3550 # Get the Tid associated
3551 if recvPacket['TreeID'] in connData['ConnectedShares']:
3552 if fileID in connData['OpenedFiles']:
3553 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
3554 errorCode = 0
3555 try:
3556 if fileHandle != PIPE_FILE_DESCRIPTOR:
3557 offset = readRequest['Offset']
3558 os.lseek(fileHandle, offset, 0)
3559 content = os.read(fileHandle, readRequest['Length'])
3560 else:
3561 sock = connData['OpenedFiles'][fileID]['Socket']
3562 content = sock.recv(readRequest['Length'])
3564 respSMBCommand['DataOffset'] = 0x50
3565 respSMBCommand['DataLength'] = len(content)
3566 respSMBCommand['DataRemaining'] = 0
3567 respSMBCommand['Buffer'] = content
3568 except Exception as e:
3569 smbServer.log('SMB2_READ: %s ' % e, logging.ERROR)
3570 errorCode = STATUS_ACCESS_DENIED
3571 else:
3572 errorCode = STATUS_INVALID_HANDLE
3573 else:
3574 errorCode = STATUS_SMB_BAD_TID
3576 smbServer.setConnectionData(connId, connData)
3577 return [respSMBCommand], None, errorCode
3579 @staticmethod
3580 def smb2Flush(connId, smbServer, recvPacket):
3581 connData = smbServer.getConnectionData(connId)
3583 respSMBCommand = smb2.SMB2Flush_Response()
3584 flushRequest = smb2.SMB2Flush(recvPacket['Data'])
3586 # Get the Tid associated
3587 if recvPacket['TreeID'] in connData['ConnectedShares']:
3588 if flushRequest['FileID'].getData() in connData['OpenedFiles']:
3589 fileHandle = connData['OpenedFiles'][flushRequest['FileID'].getData()]['FileHandle']
3590 errorCode = STATUS_SUCCESS
3591 try:
3592 os.fsync(fileHandle)
3593 except Exception as e:
3594 smbServer.log("SMB2_FLUSH %s" % e, logging.ERROR)
3595 errorCode = STATUS_ACCESS_DENIED
3596 else:
3597 errorCode = STATUS_INVALID_HANDLE
3598 else:
3599 errorCode = STATUS_SMB_BAD_TID
3601 smbServer.setConnectionData(connId, connData)
3602 return [respSMBCommand], None, errorCode
3604 @staticmethod
3605 def smb2QueryDirectory(connId, smbServer, recvPacket):
3606 connData = smbServer.getConnectionData(connId)
3607 respSMBCommand = smb2.SMB2QueryDirectory_Response()
3608 queryDirectoryRequest = smb2.SMB2QueryDirectory(recvPacket['Data'])
3610 respSMBCommand['Buffer'] = b'\x00'
3612 # The server MUST locate the tree connection, as specified in section 3.3.5.2.11.
3613 if (recvPacket['TreeID'] in connData['ConnectedShares']) is False:
3614 return [smb2.SMB2Error()], None, STATUS_NETWORK_NAME_DELETED
3616 # Next, the server MUST locate the open for the directory to be queried
3617 # If no open is found, the server MUST fail the request with STATUS_FILE_CLOSED
3618 if queryDirectoryRequest['FileID'].getData() == b'\xff' * 16:
3619 # Let's take the data from the lastRequest
3620 if 'SMB2_CREATE' in connData['LastRequest']:
3621 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3622 else:
3623 fileID = queryDirectoryRequest['FileID'].getData()
3624 else:
3625 fileID = queryDirectoryRequest['FileID'].getData()
3627 if (fileID in connData['OpenedFiles']) is False:
3628 return [smb2.SMB2Error()], None, STATUS_FILE_CLOSED
3630 # If the open is not an open to a directory, the request MUST be failed
3631 # with STATUS_INVALID_PARAMETER.
3632 if os.path.isdir(connData['OpenedFiles'][fileID]['FileName']) is False:
3633 return [smb2.SMB2Error()], None, STATUS_INVALID_PARAMETER
3635 # If any other information class is specified in the FileInformationClass
3636 # field of the SMB2 QUERY_DIRECTORY Request, the server MUST fail the
3637 # operation with STATUS_INVALID_INFO_CLASS.
3638 if queryDirectoryRequest['FileInformationClass'] not in (
3639 smb2.FILE_DIRECTORY_INFORMATION, smb2.FILE_FULL_DIRECTORY_INFORMATION,
3640 smb2.FILEID_FULL_DIRECTORY_INFORMATION,
3641 smb2.FILE_BOTH_DIRECTORY_INFORMATION, smb2.FILEID_BOTH_DIRECTORY_INFORMATION,
3642 smb2.FILENAMES_INFORMATION):
3643 return [smb2.SMB2Error()], None, STATUS_INVALID_INFO_CLASS
3645 # If SMB2_REOPEN is set in the Flags field of the SMB2 QUERY_DIRECTORY
3646 # Request, the server SHOULD<326> set Open.EnumerationLocation to 0
3647 # and Open.EnumerationSearchPattern to an empty string.
3648 if queryDirectoryRequest['Flags'] & smb2.SMB2_REOPEN:
3649 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0
3650 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = ''
3652 # If SMB2_RESTART_SCANS is set in the Flags field of the SMB2
3653 # QUERY_DIRECTORY Request, the server MUST set
3654 # Open.EnumerationLocation to 0.
3655 if queryDirectoryRequest['Flags'] & smb2.SMB2_RESTART_SCANS:
3656 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0
3658 # If Open.EnumerationLocation is 0 and Open.EnumerationSearchPattern
3659 # is an empty string, then Open.EnumerationSearchPattern MUST be set
3660 # to the search pattern specified in the SMB2 QUERY_DIRECTORY by
3661 # FileNameOffset and FileNameLength. If FileNameLength is 0, the server
3662 # SHOULD<327> set Open.EnumerationSearchPattern as "*" to search all entries.
3664 pattern = queryDirectoryRequest['Buffer'].decode('utf-16le')
3665 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0 and \
3666 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] == '':
3667 if pattern == '':
3668 pattern = '*'
3669 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern
3671 # If SMB2_INDEX_SPECIFIED is set and FileNameLength is not zero,
3672 # the server MUST set Open.EnumerationSearchPattern to the search pattern
3673 # specified in the request by FileNameOffset and FileNameLength.
3674 if queryDirectoryRequest['Flags'] & smb2.SMB2_INDEX_SPECIFIED and \
3675 queryDirectoryRequest['FileNameLength'] > 0:
3676 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern
3678 pathName = os.path.join(os.path.normpath(connData['OpenedFiles'][fileID]['FileName']), pattern)
3679 searchResult, searchCount, errorCode = findFirst2(os.path.dirname(pathName),
3680 os.path.basename(pathName),
3681 queryDirectoryRequest['FileInformationClass'],
3682 smb.ATTR_DIRECTORY, isSMB2=True)
3684 if errorCode != STATUS_SUCCESS:
3685 return [smb2.SMB2Error()], None, errorCode
3687 if searchCount > 2 and pattern == '*':
3688 # strip . and ..
3689 searchCount -= 2
3690 searchResult = searchResult[2:]
3692 if searchCount == 0 and connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0:
3693 return [smb2.SMB2Error()], None, STATUS_NO_SUCH_FILE
3695 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] < 0:
3696 return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES
3698 totalData = 0
3699 respData = b''
3700 for nItem in range(connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'], searchCount):
3701 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] += 1
3702 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY:
3703 # If single entry is requested we must clear the NextEntryOffset
3704 searchResult[nItem]['NextEntryOffset'] = 0
3705 data = searchResult[nItem].getData()
3706 lenData = len(data)
3707 padLen = (8 - (lenData % 8)) % 8
3709 if (totalData + lenData) >= queryDirectoryRequest['OutputBufferLength']:
3710 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] -= 1
3711 break
3712 else:
3713 respData += data + b'\x00' * padLen
3714 totalData += lenData + padLen
3716 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY:
3717 break
3719 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] >= searchCount:
3720 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = -1
3722 respSMBCommand['OutputBufferOffset'] = 0x48
3723 respSMBCommand['OutputBufferLength'] = totalData
3724 respSMBCommand['Buffer'] = respData
3726 smbServer.setConnectionData(connId, connData)
3727 return [respSMBCommand], None, errorCode
3729 @staticmethod
3730 def smb2ChangeNotify(connId, smbServer, recvPacket):
3732 return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED
3734 @staticmethod
3735 def smb2Echo(connId, smbServer, recvPacket):
3737 respSMBCommand = smb2.SMB2Echo_Response()
3739 return [respSMBCommand], None, STATUS_SUCCESS
3741 @staticmethod
3742 def smb2TreeDisconnect(connId, smbServer, recvPacket):
3743 connData = smbServer.getConnectionData(connId)
3745 respSMBCommand = smb2.SMB2TreeDisconnect_Response()
3747 # Get the Tid associated
3748 if recvPacket['TreeID'] in connData['ConnectedShares']:
3749 smbServer.log("Disconnecting Share(%d:%s)" % (
3750 recvPacket['TreeID'], connData['ConnectedShares'][recvPacket['TreeID']]['shareName']))
3751 del (connData['ConnectedShares'][recvPacket['TreeID']])
3752 errorCode = STATUS_SUCCESS
3753 else:
3754 errorCode = STATUS_SMB_BAD_TID
3756 smbServer.setConnectionData(connId, connData)
3757 return [respSMBCommand], None, errorCode
3759 @staticmethod
3760 def smb2Logoff(connId, smbServer, recvPacket):
3761 connData = smbServer.getConnectionData(connId)
3763 respSMBCommand = smb2.SMB2Logoff_Response()
3765 if recvPacket['SessionID'] != connData['Uid']:
3766 # STATUS_SMB_BAD_UID
3767 errorCode = STATUS_SMB_BAD_UID
3768 else:
3769 errorCode = STATUS_SUCCESS
3771 connData['Uid'] = 0
3772 connData['Authenticated'] = False
3774 smbServer.setConnectionData(connId, connData)
3775 return [respSMBCommand], None, errorCode
3777 @staticmethod
3778 def smb2Ioctl(connId, smbServer, recvPacket):
3779 connData = smbServer.getConnectionData(connId)
3781 respSMBCommand = smb2.SMB2Ioctl_Response()
3782 ioctlRequest = smb2.SMB2Ioctl(recvPacket['Data'])
3784 ioctls = smbServer.getIoctls()
3785 if ioctlRequest['CtlCode'] in ioctls:
3786 outputData, errorCode = ioctls[ioctlRequest['CtlCode']](connId, smbServer, ioctlRequest)
3787 if errorCode == STATUS_SUCCESS:
3788 respSMBCommand['CtlCode'] = ioctlRequest['CtlCode']
3789 respSMBCommand['FileID'] = ioctlRequest['FileID']
3790 respSMBCommand['InputOffset'] = 0
3791 respSMBCommand['InputCount'] = 0
3792 respSMBCommand['OutputOffset'] = 0x70
3793 respSMBCommand['OutputCount'] = len(outputData)
3794 respSMBCommand['Flags'] = 0
3795 respSMBCommand['Buffer'] = outputData
3796 else:
3797 respSMBCommand = outputData
3798 else:
3799 smbServer.log("Ioctl not implemented command: 0x%x" % ioctlRequest['CtlCode'], logging.DEBUG)
3800 errorCode = STATUS_INVALID_DEVICE_REQUEST
3801 respSMBCommand = smb2.SMB2Error()
3803 smbServer.setConnectionData(connId, connData)
3804 return [respSMBCommand], None, errorCode
3806 @staticmethod
3807 def smb2Lock(connId, smbServer, recvPacket):
3808 connData = smbServer.getConnectionData(connId)
3810 respSMBCommand = smb2.SMB2Lock_Response()
3812 # I'm actually doing nothing.. just make MacOS happy ;)
3813 errorCode = STATUS_SUCCESS
3815 smbServer.setConnectionData(connId, connData)
3816 return [respSMBCommand], None, errorCode
3818 @staticmethod
3819 def smb2Cancel(connId, smbServer, recvPacket):
3820 # I'm actually doing nothing
3821 return [smb2.SMB2Error()], None, STATUS_CANCELLED
3823 @staticmethod
3824 def default(connId, smbServer, recvPacket):
3825 # By default we return an SMB Packet with error not implemented
3826 smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'], logging.DEBUG)
3827 return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED
3830class Ioctls:
3831 @staticmethod
3832 def fsctlDfsGetReferrals(connId, smbServer, ioctlRequest):
3833 return smb2.SMB2Error(), STATUS_FS_DRIVER_REQUIRED
3835 @staticmethod
3836 def fsctlPipeTransceive(connId, smbServer, ioctlRequest):
3837 connData = smbServer.getConnectionData(connId)
3839 ioctlResponse = ''
3841 if ioctlRequest['FileID'].getData() in connData['OpenedFiles']:
3842 fileHandle = connData['OpenedFiles'][ioctlRequest['FileID'].getData()]['FileHandle']
3843 errorCode = STATUS_SUCCESS
3844 try:
3845 if fileHandle != PIPE_FILE_DESCRIPTOR:
3846 errorCode = STATUS_INVALID_DEVICE_REQUEST
3847 else:
3848 sock = connData['OpenedFiles'][ioctlRequest['FileID'].getData()]['Socket']
3849 sock.sendall(ioctlRequest['Buffer'])
3850 ioctlResponse = sock.recv(ioctlRequest['MaxOutputResponse'])
3851 except Exception as e:
3852 smbServer.log('fsctlPipeTransceive: %s ' % e, logging.ERROR)
3853 errorCode = STATUS_ACCESS_DENIED
3854 else:
3855 errorCode = STATUS_INVALID_DEVICE_REQUEST
3857 smbServer.setConnectionData(connId, connData)
3858 return ioctlResponse, errorCode
3860 @staticmethod
3861 def fsctlValidateNegotiateInfo(connId, smbServer, ioctlRequest):
3862 connData = smbServer.getConnectionData(connId)
3864 errorCode = STATUS_SUCCESS
3866 validateNegotiateInfo = smb2.VALIDATE_NEGOTIATE_INFO(ioctlRequest['Buffer'])
3867 validateNegotiateInfoResponse = smb2.VALIDATE_NEGOTIATE_INFO_RESPONSE()
3868 validateNegotiateInfoResponse['Capabilities'] = 0
3869 validateNegotiateInfoResponse['Guid'] = b'A' * 16
3870 validateNegotiateInfoResponse['SecurityMode'] = 1
3871 validateNegotiateInfoResponse['Dialect'] = smb2.SMB2_DIALECT_002
3873 smbServer.setConnectionData(connId, connData)
3874 return validateNegotiateInfoResponse.getData(), errorCode
3877class SMBSERVERHandler(socketserver.BaseRequestHandler):
3878 def __init__(self, request, client_address, server, select_poll=False):
3879 self.__SMB = server
3880 # In case of AF_INET6 the client_address contains 4 items, ignore the last 2
3881 self.__ip, self.__port = client_address[:2]
3882 self.__request = request
3883 self.__connId = threading.current_thread().name
3884 self.__timeOut = 60 * 5
3885 self.__select_poll = select_poll
3886 # self.__connId = os.getpid()
3887 socketserver.BaseRequestHandler.__init__(self, request, client_address, server)
3889 def handle(self):
3890 self.__SMB.log("Incoming connection (%s,%d)" % (self.__ip, self.__port))
3891 self.__SMB.addConnection(self.__connId, self.__ip, self.__port)
3892 while True:
3893 try:
3894 # First of all let's get the NETBIOS packet
3895 session = nmb.NetBIOSTCPSession(self.__SMB.getServerName(), 'HOST', self.__ip, sess_port=self.__port,
3896 sock=self.__request, select_poll=self.__select_poll)
3897 try:
3898 p = session.recv_packet(self.__timeOut)
3899 except nmb.NetBIOSTimeout:
3900 raise
3901 except nmb.NetBIOSError:
3902 break
3904 if p.get_type() == nmb.NETBIOS_SESSION_REQUEST:
3905 # Someone is requesting a session, we're gonna accept them all :)
3906 _, rn, my = p.get_trailer().split(b' ')
3907 remote_name = nmb.decode_name(b'\x20' + rn)
3908 myname = nmb.decode_name(b'\x20' + my)
3909 self.__SMB.log(
3910 "NetBIOS Session request (%s,%s,%s)" % (self.__ip, remote_name[1].strip(), myname[1]))
3911 r = nmb.NetBIOSSessionPacket()
3912 r.set_type(nmb.NETBIOS_SESSION_POSITIVE_RESPONSE)
3913 r.set_trailer(p.get_trailer())
3914 self.__request.send(r.rawData())
3915 else:
3916 resp = self.__SMB.processRequest(self.__connId, p.get_trailer())
3917 # Send all the packets received. Except for big transactions this should be
3918 # a single packet
3919 for i in resp:
3920 if hasattr(i, 'getData'):
3921 session.send_packet(i.getData())
3922 else:
3923 session.send_packet(i)
3924 except Exception as e:
3925 self.__SMB.log("Handle: %s" % e)
3926 # import traceback
3927 # traceback.print_exc()
3928 break
3930 def finish(self):
3931 # Thread/process is dying, we should tell the main SMB thread to remove all this thread data
3932 self.__SMB.log("Closing down connection (%s,%d)" % (self.__ip, self.__port))
3933 self.__SMB.removeConnection(self.__connId)
3934 return socketserver.BaseRequestHandler.finish(self)
3937class SMBSERVER(socketserver.ThreadingMixIn, socketserver.TCPServer):
3938 # class SMBSERVER(socketserver.ForkingMixIn, socketserver.TCPServer):
3939 def __init__(self, server_address, handler_class=SMBSERVERHandler, config_parser=None):
3940 socketserver.TCPServer.allow_reuse_address = True
3941 socketserver.TCPServer.__init__(self, server_address, handler_class)
3943 # Server name and OS to be presented whenever is necessary
3944 self.__serverName = ''
3945 self.__serverOS = ''
3946 self.__serverDomain = ''
3947 self.__challenge = ''
3948 self.__log = None
3950 # Our ConfigParser data
3951 self.__serverConfig = config_parser
3953 # Our credentials to be used during the server's lifetime
3954 self.__credentials = {}
3956 # Our log file
3957 self.__logFile = ''
3959 # Registered Named Pipes, format is PipeName,Socket
3960 self.__registeredNamedPipes = {}
3962 # JTR dump path
3963 self.__jtr_dump_path = ''
3965 # SMB2 Support flag = default not active
3966 self.__SMB2Support = False
3968 # Allow anonymous logon
3969 self.__anonymousLogon = True
3971 # Our list of commands we will answer, by default the NOT IMPLEMENTED one
3972 self.__smbCommandsHandler = SMBCommands()
3973 self.__smbTrans2Handler = TRANS2Commands()
3974 self.__smbTransHandler = TRANSCommands()
3975 self.__smbNTTransHandler = NTTRANSCommands()
3976 self.__smb2CommandsHandler = SMB2Commands()
3977 self.__IoctlHandler = Ioctls()
3979 self.__smbNTTransCommands = {
3980 # NT IOCTL, can't find doc for this
3981 0xff: self.__smbNTTransHandler.default
3982 }
3984 self.__smbTransCommands = {
3985 '\\PIPE\\LANMAN': self.__smbTransHandler.lanMan,
3986 smb.SMB.TRANS_TRANSACT_NMPIPE: self.__smbTransHandler.transactNamedPipe,
3987 }
3988 self.__smbTrans2Commands = {
3989 smb.SMB.TRANS2_FIND_FIRST2: self.__smbTrans2Handler.findFirst2,
3990 smb.SMB.TRANS2_FIND_NEXT2: self.__smbTrans2Handler.findNext2,
3991 smb.SMB.TRANS2_QUERY_FS_INFORMATION: self.__smbTrans2Handler.queryFsInformation,
3992 smb.SMB.TRANS2_QUERY_PATH_INFORMATION: self.__smbTrans2Handler.queryPathInformation,
3993 smb.SMB.TRANS2_QUERY_FILE_INFORMATION: self.__smbTrans2Handler.queryFileInformation,
3994 smb.SMB.TRANS2_SET_FILE_INFORMATION: self.__smbTrans2Handler.setFileInformation,
3995 smb.SMB.TRANS2_SET_PATH_INFORMATION: self.__smbTrans2Handler.setPathInformation
3996 }
3998 self.__smbCommands = {
3999 smb.SMB.SMB_COM_FLUSH: self.__smbCommandsHandler.smbComFlush,
4000 smb.SMB.SMB_COM_CREATE_DIRECTORY: self.__smbCommandsHandler.smbComCreateDirectory,
4001 smb.SMB.SMB_COM_DELETE_DIRECTORY: self.__smbCommandsHandler.smbComDeleteDirectory,
4002 smb.SMB.SMB_COM_RENAME: self.__smbCommandsHandler.smbComRename,
4003 smb.SMB.SMB_COM_DELETE: self.__smbCommandsHandler.smbComDelete,
4004 smb.SMB.SMB_COM_NEGOTIATE: self.__smbCommandsHandler.smbComNegotiate,
4005 smb.SMB.SMB_COM_SESSION_SETUP_ANDX: self.__smbCommandsHandler.smbComSessionSetupAndX,
4006 smb.SMB.SMB_COM_LOGOFF_ANDX: self.__smbCommandsHandler.smbComLogOffAndX,
4007 smb.SMB.SMB_COM_TREE_CONNECT_ANDX: self.__smbCommandsHandler.smbComTreeConnectAndX,
4008 smb.SMB.SMB_COM_TREE_DISCONNECT: self.__smbCommandsHandler.smbComTreeDisconnect,
4009 smb.SMB.SMB_COM_ECHO: self.__smbCommandsHandler.smbComEcho,
4010 smb.SMB.SMB_COM_QUERY_INFORMATION: self.__smbCommandsHandler.smbQueryInformation,
4011 smb.SMB.SMB_COM_TRANSACTION2: self.__smbCommandsHandler.smbTransaction2,
4012 smb.SMB.SMB_COM_TRANSACTION: self.__smbCommandsHandler.smbTransaction,
4013 # Not needed for now
4014 smb.SMB.SMB_COM_NT_TRANSACT: self.__smbCommandsHandler.smbNTTransact,
4015 smb.SMB.SMB_COM_QUERY_INFORMATION_DISK: self.__smbCommandsHandler.smbQueryInformationDisk,
4016 smb.SMB.SMB_COM_OPEN_ANDX: self.__smbCommandsHandler.smbComOpenAndX,
4017 smb.SMB.SMB_COM_QUERY_INFORMATION2: self.__smbCommandsHandler.smbComQueryInformation2,
4018 smb.SMB.SMB_COM_READ_ANDX: self.__smbCommandsHandler.smbComReadAndX,
4019 smb.SMB.SMB_COM_READ: self.__smbCommandsHandler.smbComRead,
4020 smb.SMB.SMB_COM_WRITE_ANDX: self.__smbCommandsHandler.smbComWriteAndX,
4021 smb.SMB.SMB_COM_WRITE: self.__smbCommandsHandler.smbComWrite,
4022 smb.SMB.SMB_COM_CLOSE: self.__smbCommandsHandler.smbComClose,
4023 smb.SMB.SMB_COM_LOCKING_ANDX: self.__smbCommandsHandler.smbComLockingAndX,
4024 smb.SMB.SMB_COM_NT_CREATE_ANDX: self.__smbCommandsHandler.smbComNtCreateAndX,
4025 0xFF: self.__smbCommandsHandler.default
4026 }
4028 self.__smb2Ioctls = {
4029 smb2.FSCTL_DFS_GET_REFERRALS: self.__IoctlHandler.fsctlDfsGetReferrals,
4030 # smb2.FSCTL_PIPE_PEEK: self.__IoctlHandler.fsctlPipePeek,
4031 # smb2.FSCTL_PIPE_WAIT: self.__IoctlHandler.fsctlPipeWait,
4032 smb2.FSCTL_PIPE_TRANSCEIVE: self.__IoctlHandler.fsctlPipeTransceive,
4033 # smb2.FSCTL_SRV_COPYCHUNK: self.__IoctlHandler.fsctlSrvCopyChunk,
4034 # smb2.FSCTL_SRV_ENUMERATE_SNAPSHOTS: self.__IoctlHandler.fsctlSrvEnumerateSnapshots,
4035 # smb2.FSCTL_SRV_REQUEST_RESUME_KEY: self.__IoctlHandler.fsctlSrvRequestResumeKey,
4036 # smb2.FSCTL_SRV_READ_HASH: self.__IoctlHandler.fsctlSrvReadHash,
4037 # smb2.FSCTL_SRV_COPYCHUNK_WRITE: self.__IoctlHandler.fsctlSrvCopyChunkWrite,
4038 # smb2.FSCTL_LMR_REQUEST_RESILIENCY: self.__IoctlHandler.fsctlLmrRequestResiliency,
4039 # smb2.FSCTL_QUERY_NETWORK_INTERFACE_INFO: self.__IoctlHandler.fsctlQueryNetworkInterfaceInfo,
4040 # smb2.FSCTL_SET_REPARSE_POINT: self.__IoctlHandler.fsctlSetReparsePoint,
4041 # smb2.FSCTL_DFS_GET_REFERRALS_EX: self.__IoctlHandler.fsctlDfsGetReferralsEx,
4042 # smb2.FSCTL_FILE_LEVEL_TRIM: self.__IoctlHandler.fsctlFileLevelTrim,
4043 smb2.FSCTL_VALIDATE_NEGOTIATE_INFO: self.__IoctlHandler.fsctlValidateNegotiateInfo,
4044 }
4046 self.__smb2Commands = {
4047 smb2.SMB2_NEGOTIATE: self.__smb2CommandsHandler.smb2Negotiate,
4048 smb2.SMB2_SESSION_SETUP: self.__smb2CommandsHandler.smb2SessionSetup,
4049 smb2.SMB2_LOGOFF: self.__smb2CommandsHandler.smb2Logoff,
4050 smb2.SMB2_TREE_CONNECT: self.__smb2CommandsHandler.smb2TreeConnect,
4051 smb2.SMB2_TREE_DISCONNECT: self.__smb2CommandsHandler.smb2TreeDisconnect,
4052 smb2.SMB2_CREATE: self.__smb2CommandsHandler.smb2Create,
4053 smb2.SMB2_CLOSE: self.__smb2CommandsHandler.smb2Close,
4054 smb2.SMB2_FLUSH: self.__smb2CommandsHandler.smb2Flush,
4055 smb2.SMB2_READ: self.__smb2CommandsHandler.smb2Read,
4056 smb2.SMB2_WRITE: self.__smb2CommandsHandler.smb2Write,
4057 smb2.SMB2_LOCK: self.__smb2CommandsHandler.smb2Lock,
4058 smb2.SMB2_IOCTL: self.__smb2CommandsHandler.smb2Ioctl,
4059 smb2.SMB2_CANCEL: self.__smb2CommandsHandler.smb2Cancel,
4060 smb2.SMB2_ECHO: self.__smb2CommandsHandler.smb2Echo,
4061 smb2.SMB2_QUERY_DIRECTORY: self.__smb2CommandsHandler.smb2QueryDirectory,
4062 smb2.SMB2_CHANGE_NOTIFY: self.__smb2CommandsHandler.smb2ChangeNotify,
4063 smb2.SMB2_QUERY_INFO: self.__smb2CommandsHandler.smb2QueryInfo,
4064 smb2.SMB2_SET_INFO: self.__smb2CommandsHandler.smb2SetInfo,
4065 # smb2.SMB2_OPLOCK_BREAK: self.__smb2CommandsHandler.smb2SessionSetup,
4066 0xFF: self.__smb2CommandsHandler.default
4067 }
4069 # List of active connections
4070 self.__activeConnections = {}
4072 def getIoctls(self):
4073 return self.__smb2Ioctls
4075 def getCredentials(self):
4076 return self.__credentials
4078 def removeConnection(self, name):
4079 try:
4080 del (self.__activeConnections[name])
4081 except:
4082 pass
4083 self.log("Remaining connections %s" % list(self.__activeConnections.keys()))
4085 def addConnection(self, name, ip, port):
4086 self.__activeConnections[name] = {}
4087 # Let's init with some know stuff we will need to have
4088 # TODO: Document what's in there
4089 # print "Current Connections", self.__activeConnections.keys()
4090 self.__activeConnections[name]['PacketNum'] = 0
4091 self.__activeConnections[name]['ClientIP'] = ip
4092 self.__activeConnections[name]['ClientPort'] = port
4093 self.__activeConnections[name]['Uid'] = 0
4094 self.__activeConnections[name]['ConnectedShares'] = {}
4095 self.__activeConnections[name]['OpenedFiles'] = {}
4096 # SID results for findfirst2
4097 self.__activeConnections[name]['SIDs'] = {}
4098 self.__activeConnections[name]['LastRequest'] = {}
4099 self.__activeConnections[name]['SignatureEnabled'] = False
4100 self.__activeConnections[name]['SigningChallengeResponse'] = ''
4101 self.__activeConnections[name]['SigningSessionKey'] = b''
4102 self.__activeConnections[name]['Authenticated'] = False
4104 def getActiveConnections(self):
4105 return self.__activeConnections
4107 def setConnectionData(self, connId, data):
4108 self.__activeConnections[connId] = data
4109 # print "setConnectionData"
4110 # print self.__activeConnections
4112 def getConnectionData(self, connId, checkStatus=True):
4113 conn = self.__activeConnections[connId]
4114 if checkStatus is True:
4115 if ('Authenticated' in conn) is not True:
4116 # Can't keep going further
4117 raise Exception("User not Authenticated!")
4118 return conn
4120 def getRegisteredNamedPipes(self):
4121 return self.__registeredNamedPipes
4123 def registerNamedPipe(self, pipeName, address):
4124 self.__registeredNamedPipes[str(pipeName)] = address
4125 return True
4127 def unregisterNamedPipe(self, pipeName):
4128 if pipeName in self.__registeredNamedPipes:
4129 del (self.__registeredNamedPipes[str(pipeName)])
4130 return True
4131 return False
4133 def unregisterTransaction(self, transCommand):
4134 if transCommand in self.__smbTransCommands:
4135 del (self.__smbTransCommands[transCommand])
4137 def hookTransaction(self, transCommand, callback):
4138 # If you call this function, callback will replace
4139 # the current Transaction sub command.
4140 # (don't get confused with the Transaction smbCommand)
4141 # If the transaction sub command doesn't not exist, it is added
4142 # If the transaction sub command exists, it returns the original function # replaced
4143 #
4144 # callback MUST be declared as:
4145 # callback(connId, smbServer, recvPacket, parameters, data, maxDataCount=0)
4146 #
4147 # WHERE:
4148 #
4149 # connId : the connection Id, used to grab/update information about
4150 # the current connection
4151 # smbServer : the SMBServer instance available for you to ask
4152 # configuration data
4153 # recvPacket : the full SMBPacket that triggered this command
4154 # parameters : the transaction parameters
4155 # data : the transaction data
4156 # maxDataCount: the max amount of data that can be transferred agreed
4157 # with the client
4158 #
4159 # and MUST return:
4160 # respSetup, respParameters, respData, errorCode
4161 #
4162 # WHERE:
4163 #
4164 # respSetup: the setup response of the transaction
4165 # respParameters: the parameters response of the transaction
4166 # respData: the data response of the transaction
4167 # errorCode: the NT error code
4169 if transCommand in self.__smbTransCommands:
4170 originalCommand = self.__smbTransCommands[transCommand]
4171 else:
4172 originalCommand = None
4174 self.__smbTransCommands[transCommand] = callback
4175 return originalCommand
4177 def unregisterTransaction2(self, transCommand):
4178 if transCommand in self.__smbTrans2Commands:
4179 del (self.__smbTrans2Commands[transCommand])
4181 def hookTransaction2(self, transCommand, callback):
4182 # Here we should add to __smbTrans2Commands
4183 # Same description as Transaction
4184 if transCommand in self.__smbTrans2Commands:
4185 originalCommand = self.__smbTrans2Commands[transCommand]
4186 else:
4187 originalCommand = None
4189 self.__smbTrans2Commands[transCommand] = callback
4190 return originalCommand
4192 def unregisterNTTransaction(self, transCommand):
4193 if transCommand in self.__smbNTTransCommands:
4194 del (self.__smbNTTransCommands[transCommand])
4196 def hookNTTransaction(self, transCommand, callback):
4197 # Here we should add to __smbNTTransCommands
4198 # Same description as Transaction
4199 if transCommand in self.__smbNTTransCommands:
4200 originalCommand = self.__smbNTTransCommands[transCommand]
4201 else:
4202 originalCommand = None
4204 self.__smbNTTransCommands[transCommand] = callback
4205 return originalCommand
4207 def unregisterSmbCommand(self, smbCommand):
4208 if smbCommand in self.__smbCommands:
4209 del (self.__smbCommands[smbCommand])
4211 def hookSmbCommand(self, smbCommand, callback):
4212 # Here we should add to self.__smbCommands
4213 # If you call this function, callback will replace
4214 # the current smbCommand.
4215 # If smbCommand doesn't not exist, it is added
4216 # If SMB command exists, it returns the original function replaced
4217 #
4218 # callback MUST be declared as:
4219 # callback(connId, smbServer, SMBCommand, recvPacket)
4220 #
4221 # WHERE:
4222 #
4223 # connId : the connection Id, used to grab/update information about
4224 # the current connection
4225 # smbServer : the SMBServer instance available for you to ask
4226 # configuration data
4227 # SMBCommand: the SMBCommand itself, with its data and parameters.
4228 # Check smb.py:SMBCommand() for a reference
4229 # recvPacket: the full SMBPacket that triggered this command
4230 #
4231 # and MUST return:
4232 # <list of respSMBCommands>, <list of packets>, errorCode
4233 # <list of packets> has higher preference over commands, in case you
4234 # want to change the whole packet
4235 # errorCode: the NT error code
4236 #
4237 # For SMB_COM_TRANSACTION2, SMB_COM_TRANSACTION and SMB_COM_NT_TRANSACT
4238 # the callback function is slightly different:
4239 #
4240 # callback(connId, smbServer, SMBCommand, recvPacket, transCommands)
4241 #
4242 # WHERE:
4243 #
4244 # transCommands: a list of transaction subcommands already registered
4245 #
4247 if smbCommand in self.__smbCommands:
4248 originalCommand = self.__smbCommands[smbCommand]
4249 else:
4250 originalCommand = None
4252 self.__smbCommands[smbCommand] = callback
4253 return originalCommand
4255 def unregisterSmb2Command(self, smb2Command):
4256 if smb2Command in self.__smb2Commands:
4257 del (self.__smb2Commands[smb2Command])
4259 def hookSmb2Command(self, smb2Command, callback):
4260 if smb2Command in self.__smb2Commands:
4261 originalCommand = self.__smb2Commands[smb2Command]
4262 else:
4263 originalCommand = None
4265 self.__smb2Commands[smb2Command] = callback
4266 return originalCommand
4268 def log(self, msg, level=logging.INFO):
4269 self.__log.log(level, msg)
4271 def getServerName(self):
4272 return self.__serverName
4274 def getServerOS(self):
4275 return self.__serverOS
4277 def getServerDomain(self):
4278 return self.__serverDomain
4280 def getSMBChallenge(self):
4281 return self.__challenge
4283 def getServerConfig(self):
4284 return self.__serverConfig
4286 def setServerConfig(self, config):
4287 self.__serverConfig = config
4289 def getJTRdumpPath(self):
4290 return self.__jtr_dump_path
4292 def verify_request(self, request, client_address):
4293 # TODO: Control here the max amount of processes we want to launch
4294 # returning False, closes the connection
4295 return True
4297 def signSMBv1(self, connData, packet, signingSessionKey, signingChallengeResponse):
4298 # This logic MUST be applied for messages sent in response to any of the higher-layer actions and in
4299 # compliance with the message sequencing rules.
4300 # * The client or server that sends the message MUST provide the 32-bit sequence number for this
4301 # message, as specified in sections 3.2.4.1 and 3.3.4.1.
4302 # * The SMB_FLAGS2_SMB_SECURITY_SIGNATURE flag in the header MUST be set.
4303 # * To generate the signature, a 32-bit sequence number is copied into the
4304 # least significant 32 bits of the SecuritySignature field and the remaining
4305 # 4 bytes are set to 0x00.
4306 # * The MD5 algorithm, as specified in [RFC1321], MUST be used to generate a hash of the SMB
4307 # message from the start of the SMB Header, which is defined as follows.
4308 # CALL MD5Init( md5context )
4309 # CALL MD5Update( md5context, Connection.SigningSessionKey )
4310 # CALL MD5Update( md5context, Connection.SigningChallengeResponse )
4311 # CALL MD5Update( md5context, SMB message )
4312 # CALL MD5Final( digest, md5context )
4313 # SET signature TO the first 8 bytes of the digest
4314 # The resulting 8-byte signature MUST be copied into the SecuritySignature field of the SMB Header,
4315 # after which the message can be transmitted.
4317 # print "seq(%d) signingSessionKey %r, signingChallengeResponse %r" % (connData['SignSequenceNumber'], signingSessionKey, signingChallengeResponse)
4318 packet['SecurityFeatures'] = struct.pack('<q', connData['SignSequenceNumber'])
4319 # Sign with the sequence
4320 m = hashlib.md5()
4321 m.update(signingSessionKey)
4322 m.update(signingChallengeResponse)
4323 if hasattr(packet, 'getData'):
4324 m.update(packet.getData())
4325 else:
4326 m.update(packet)
4327 # Replace sequence with acual hash
4328 packet['SecurityFeatures'] = m.digest()[:8]
4329 connData['SignSequenceNumber'] += 2
4331 def signSMBv2(self, packet, signingSessionKey):
4332 packet['Signature'] = b'\x00' * 16
4333 packet['Flags'] |= smb2.SMB2_FLAGS_SIGNED
4334 signature = hmac.new(signingSessionKey, packet.getData(), hashlib.sha256).digest()
4335 packet['Signature'] = signature[:16]
4336 # print "%s" % packet['Signature'].encode('hex')
4338 def processRequest(self, connId, data):
4340 # TODO: Process batched commands.
4341 isSMB2 = False
4342 SMBCommand = None
4343 try:
4344 packet = smb.NewSMBPacket(data=data)
4345 SMBCommand = smb.SMBCommand(packet['Data'][0])
4346 except:
4347 # Maybe a SMB2 packet?
4348 packet = smb2.SMB2Packet(data=data)
4349 connData = self.getConnectionData(connId, False)
4350 self.signSMBv2(packet, connData['SigningSessionKey'])
4351 isSMB2 = True
4353 connData = self.getConnectionData(connId, False)
4355 # We might have compound requests
4356 compoundedPacketsResponse = []
4357 compoundedPackets = []
4358 try:
4359 # Search out list of implemented commands
4360 # We provide them with:
4361 # connId : representing the data for this specific connection
4362 # self : the SMBSERVER if they want to ask data to it
4363 # SMBCommand : the SMBCommand they are expecting to process
4364 # packet : the received packet itself, in case they need more data than the actual command
4365 # Only for Transactions
4366 # transCommand: a list of transaction subcommands
4367 # We expect to get:
4368 # respCommands: a list of answers for the commands processed
4369 # respPacket : if the commands chose to directly craft packet/s, we use this and not the previous
4370 # this MUST be a list
4371 # errorCode : self explanatory
4372 if isSMB2 is False:
4373 # Is the client authenticated already?
4374 if connData['Authenticated'] is False and packet['Command'] not in (
4375 smb.SMB.SMB_COM_NEGOTIATE, smb.SMB.SMB_COM_SESSION_SETUP_ANDX):
4376 # Nope.. in that case he should only ask for a few commands, if not throw him out.
4377 errorCode = STATUS_ACCESS_DENIED
4378 respPackets = None
4379 respCommands = [smb.SMBCommand(packet['Command'])]
4380 else:
4381 if packet['Command'] == smb.SMB.SMB_COM_TRANSACTION2:
4382 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4383 connId,
4384 self,
4385 SMBCommand,
4386 packet,
4387 self.__smbTrans2Commands)
4388 elif packet['Command'] == smb.SMB.SMB_COM_NT_TRANSACT:
4389 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4390 connId,
4391 self,
4392 SMBCommand,
4393 packet,
4394 self.__smbNTTransCommands)
4395 elif packet['Command'] == smb.SMB.SMB_COM_TRANSACTION:
4396 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4397 connId,
4398 self,
4399 SMBCommand,
4400 packet,
4401 self.__smbTransCommands)
4402 else:
4403 if packet['Command'] in self.__smbCommands:
4404 if self.__SMB2Support is True:
4405 if packet['Command'] == smb.SMB.SMB_COM_NEGOTIATE:
4406 try:
4407 respCommands, respPackets, errorCode = self.__smb2Commands[smb2.SMB2_NEGOTIATE](
4408 connId, self, packet, True)
4409 isSMB2 = True
4410 except Exception as e:
4411 import traceback
4412 traceback.print_exc()
4413 self.log('SMB2_NEGOTIATE: %s' % e, logging.ERROR)
4414 # If something went wrong, let's fallback to SMB1
4415 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4416 connId,
4417 self,
4418 SMBCommand,
4419 packet)
4420 # self.__SMB2Support = False
4421 pass
4422 else:
4423 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4424 connId,
4425 self,
4426 SMBCommand,
4427 packet)
4428 else:
4429 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4430 connId,
4431 self,
4432 SMBCommand,
4433 packet)
4434 else:
4435 respCommands, respPackets, errorCode = self.__smbCommands[255](connId, self, SMBCommand,
4436 packet)
4438 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))
4439 compoundedPackets.append(packet)
4441 else:
4442 # Is the client authenticated already?
4443 if connData['Authenticated'] is False and packet['Command'] not in (
4444 smb2.SMB2_NEGOTIATE, smb2.SMB2_SESSION_SETUP):
4445 # Nope.. in that case he should only ask for a few commands, if not throw him out.
4446 errorCode = STATUS_ACCESS_DENIED
4447 respPackets = None
4448 respCommands = ['']
4449 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))
4450 compoundedPackets.append(packet)
4451 else:
4452 done = False
4453 while not done:
4454 if packet['Command'] in self.__smb2Commands:
4455 if self.__SMB2Support is True:
4456 respCommands, respPackets, errorCode = self.__smb2Commands[packet['Command']](
4457 connId,
4458 self,
4459 packet)
4460 else:
4461 respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet)
4462 else:
4463 respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet)
4464 # Let's store the result for this compounded packet
4465 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))
4466 compoundedPackets.append(packet)
4467 if packet['NextCommand'] != 0:
4468 data = data[packet['NextCommand']:]
4469 packet = smb2.SMB2Packet(data=data)
4470 else:
4471 done = True
4473 except Exception as e:
4474 # import traceback
4475 # traceback.print_exc()
4476 # Something wen't wrong, defaulting to Bad user ID
4477 self.log('processRequest (0x%x,%s)' % (packet['Command'], e), logging.ERROR)
4478 raise
4480 # We prepare the response packet to commands don't need to bother about that.
4481 connData = self.getConnectionData(connId, False)
4483 # Force reconnection loop.. This is just a test.. client will send me back credentials :)
4484 # connData['PacketNum'] += 1
4485 # if connData['PacketNum'] == 15:
4486 # connData['PacketNum'] = 0
4487 # # Something wen't wrong, defaulting to Bad user ID
4488 # self.log('Sending BAD USER ID!', logging.ERROR)
4489 # #raise
4490 # packet['Flags1'] |= smb.SMB.FLAGS1_REPLY
4491 # packet['Flags2'] = 0
4492 # errorCode = STATUS_SMB_BAD_UID
4493 # packet['ErrorCode'] = errorCode >> 16
4494 # packet['ErrorClass'] = errorCode & 0xff
4495 # return [packet]
4497 self.setConnectionData(connId, connData)
4499 packetsToSend = []
4500 for packetNum in range(len(compoundedPacketsResponse)):
4501 respCommands, respPackets, errorCode = compoundedPacketsResponse[packetNum]
4502 packet = compoundedPackets[packetNum]
4503 if respPackets is None:
4504 for respCommand in respCommands:
4505 if isSMB2 is False:
4506 respPacket = smb.NewSMBPacket()
4507 respPacket['Flags1'] = smb.SMB.FLAGS1_REPLY
4509 # TODO this should come from a per session configuration
4510 respPacket[
4511 'Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \
4512 packet['Flags2'] & smb.SMB.FLAGS2_UNICODE
4513 # respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES
4514 # respPacket['Flags1'] = 0x98
4515 # respPacket['Flags2'] = 0xc807
4517 respPacket['Tid'] = packet['Tid']
4518 respPacket['Mid'] = packet['Mid']
4519 respPacket['Pid'] = packet['Pid']
4520 respPacket['Uid'] = connData['Uid']
4522 respPacket['ErrorCode'] = errorCode >> 16
4523 respPacket['_reserved'] = errorCode >> 8 & 0xff
4524 respPacket['ErrorClass'] = errorCode & 0xff
4525 respPacket.addCommand(respCommand)
4527 if connData['SignatureEnabled']:
4528 respPacket['Flags2'] |= smb.SMB.FLAGS2_SMB_SECURITY_SIGNATURE
4529 self.signSMBv1(connData, respPacket, connData['SigningSessionKey'],
4530 connData['SigningChallengeResponse'])
4532 packetsToSend.append(respPacket)
4533 else:
4534 respPacket = smb2.SMB2Packet()
4535 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR
4536 if packetNum > 0:
4537 respPacket['Flags'] |= smb2.SMB2_FLAGS_RELATED_OPERATIONS
4538 respPacket['Status'] = errorCode
4539 respPacket['CreditRequestResponse'] = packet['CreditRequestResponse']
4540 respPacket['Command'] = packet['Command']
4541 respPacket['CreditCharge'] = packet['CreditCharge']
4542 # respPacket['CreditCharge'] = 0
4543 respPacket['Reserved'] = packet['Reserved']
4544 respPacket['SessionID'] = connData['Uid']
4545 respPacket['MessageID'] = packet['MessageID']
4546 respPacket['TreeID'] = packet['TreeID']
4547 if hasattr(respCommand, 'getData'):
4548 respPacket['Data'] = respCommand.getData()
4549 else:
4550 respPacket['Data'] = str(respCommand)
4552 if connData['SignatureEnabled']:
4553 self.signSMBv2(respPacket, connData['SigningSessionKey'])
4555 packetsToSend.append(respPacket)
4556 else:
4557 # The SMBCommand took care of building the packet
4558 packetsToSend = respPackets
4560 if isSMB2 is True:
4561 # Let's build a compound answer
4562 finalData = b''
4563 i = 0
4564 for i in range(len(packetsToSend) - 1):
4565 packet = packetsToSend[i]
4566 # Align to 8-bytes
4567 padLen = (8 - (len(packet) % 8)) % 8
4568 packet['NextCommand'] = len(packet) + padLen
4569 if hasattr(packet, 'getData'):
4570 finalData += packet.getData() + padLen * b'\x00'
4571 else:
4572 finalData += packet + padLen * b'\x00'
4574 # Last one
4575 if hasattr(packetsToSend[len(packetsToSend) - 1], 'getData'):
4576 finalData += packetsToSend[len(packetsToSend) - 1].getData()
4577 else:
4578 finalData += packetsToSend[len(packetsToSend) - 1]
4579 packetsToSend = [finalData]
4581 # We clear the compound requests
4582 connData['LastRequest'] = {}
4584 return packetsToSend
4586 def processConfigFile(self, configFile=None):
4587 # TODO: Do a real config parser
4588 if self.__serverConfig is None:
4589 if configFile is None:
4590 configFile = 'smb.conf'
4591 self.__serverConfig = configparser.ConfigParser()
4592 self.__serverConfig.read(configFile)
4594 self.__serverName = self.__serverConfig.get('global', 'server_name')
4595 self.__serverOS = self.__serverConfig.get('global', 'server_os')
4596 self.__serverDomain = self.__serverConfig.get('global', 'server_domain')
4597 self.__logFile = self.__serverConfig.get('global', 'log_file')
4598 if self.__serverConfig.has_option('global', 'challenge'):
4599 self.__challenge = unhexlify(self.__serverConfig.get('global', 'challenge'))
4600 else:
4601 self.__challenge = b'A' * 16
4603 if self.__serverConfig.has_option("global", "jtr_dump_path"):
4604 self.__jtr_dump_path = self.__serverConfig.get("global", "jtr_dump_path")
4606 if self.__serverConfig.has_option("global", "SMB2Support"):
4607 self.__SMB2Support = self.__serverConfig.getboolean("global", "SMB2Support")
4608 else:
4609 self.__SMB2Support = False
4612 if self.__serverConfig.has_option("global", "anonymous_logon"):
4613 self.__anonymousLogon = self.__serverConfig.getboolean("global", "anonymous_logon")
4614 else:
4615 self.__anonymousLogon = True
4617 if self.__logFile != 'None':
4618 logging.basicConfig(filename=self.__logFile,
4619 level=logging.DEBUG,
4620 format="%(asctime)s: %(levelname)s: %(message)s",
4621 datefmt='%m/%d/%Y %I:%M:%S %p')
4622 self.__log = LOG
4624 # Process the credentials
4625 credentials_fname = self.__serverConfig.get('global', 'credentials_file')
4626 if credentials_fname != "":
4627 cred = open(credentials_fname)
4628 line = cred.readline()
4629 while line:
4630 name, uid, lmhash, nthash = line.split(':')
4631 self.__credentials[name.lower()] = (uid, lmhash, nthash.strip('\r\n'))
4632 line = cred.readline()
4633 cred.close()
4634 self.log('Config file parsed')
4636 def addCredential(self, name, uid, lmhash, nthash):
4637 # If we have hashes, normalize them
4638 if lmhash != '' or nthash != '':
4639 if len(lmhash) % 2:
4640 lmhash = '0%s' % lmhash
4641 if len(nthash) % 2:
4642 nthash = '0%s' % nthash
4643 try: # just in case they were converted already
4644 lmhash = a2b_hex(lmhash)
4645 nthash = a2b_hex(nthash)
4646 except:
4647 pass
4648 self.__credentials[name.lower()] = (uid, lmhash, nthash)
4651# For windows platforms, opening a directory is not an option, so we set a void FD
4652VOID_FILE_DESCRIPTOR = -1
4653PIPE_FILE_DESCRIPTOR = -2
4655######################################################################
4656# HELPER CLASSES
4657######################################################################
4659from impacket.dcerpc.v5.rpcrt import DCERPCServer
4660from impacket.dcerpc.v5.dtypes import NULL
4661from impacket.dcerpc.v5.srvs import NetrShareEnum, NetrShareEnumResponse, SHARE_INFO_1, NetrServerGetInfo, \
4662 NetrServerGetInfoResponse, NetrShareGetInfo, NetrShareGetInfoResponse
4663from impacket.dcerpc.v5.wkst import NetrWkstaGetInfo, NetrWkstaGetInfoResponse
4664from impacket.system_errors import ERROR_INVALID_LEVEL
4667class WKSTServer(DCERPCServer):
4668 def __init__(self):
4669 DCERPCServer.__init__(self)
4670 self.wkssvcCallBacks = {
4671 0: self.NetrWkstaGetInfo,
4672 }
4673 self.addCallbacks(('6BFFD098-A112-3610-9833-46C3F87E345A', '1.0'), '\\PIPE\\wkssvc', self.wkssvcCallBacks)
4675 def NetrWkstaGetInfo(self, data):
4676 request = NetrWkstaGetInfo(data)
4677 self.log("NetrWkstaGetInfo Level: %d" % request['Level'])
4679 answer = NetrWkstaGetInfoResponse()
4681 if request['Level'] not in (100, 101):
4682 answer['ErrorCode'] = ERROR_INVALID_LEVEL
4683 return answer
4685 answer['WkstaInfo']['tag'] = request['Level']
4687 if request['Level'] == 100:
4688 # Windows. Decimal value 500.
4689 answer['WkstaInfo']['WkstaInfo100']['wki100_platform_id'] = 0x000001F4
4690 answer['WkstaInfo']['WkstaInfo100']['wki100_computername'] = NULL
4691 answer['WkstaInfo']['WkstaInfo100']['wki100_langroup'] = NULL
4692 answer['WkstaInfo']['WkstaInfo100']['wki100_ver_major'] = 5
4693 answer['WkstaInfo']['WkstaInfo100']['wki100_ver_minor'] = 0
4694 else:
4695 # Windows. Decimal value 500.
4696 answer['WkstaInfo']['WkstaInfo101']['wki101_platform_id'] = 0x000001F4
4697 answer['WkstaInfo']['WkstaInfo101']['wki101_computername'] = NULL
4698 answer['WkstaInfo']['WkstaInfo101']['wki101_langroup'] = NULL
4699 answer['WkstaInfo']['WkstaInfo101']['wki101_ver_major'] = 5
4700 answer['WkstaInfo']['WkstaInfo101']['wki101_ver_minor'] = 0
4701 answer['WkstaInfo']['WkstaInfo101']['wki101_lanroot'] = NULL
4703 return answer
4706class SRVSServer(DCERPCServer):
4707 def __init__(self):
4708 DCERPCServer.__init__(self)
4710 self._shares = {}
4711 self.__serverConfig = None
4712 self.__logFile = None
4714 self.srvsvcCallBacks = {
4715 15: self.NetrShareEnum,
4716 16: self.NetrShareGetInfo,
4717 21: self.NetrServerGetInfo,
4718 }
4720 self.addCallbacks(('4B324FC8-1670-01D3-1278-5A47BF6EE188', '3.0'), '\\PIPE\\srvsvc', self.srvsvcCallBacks)
4722 def setServerConfig(self, config):
4723 self.__serverConfig = config
4725 def processConfigFile(self, configFile=None):
4726 if configFile is not None:
4727 self.__serverConfig = configparser.ConfigParser()
4728 self.__serverConfig.read(configFile)
4729 sections = self.__serverConfig.sections()
4730 # Let's check the log file
4731 self.__logFile = self.__serverConfig.get('global', 'log_file')
4732 if self.__logFile != 'None':
4733 logging.basicConfig(filename=self.__logFile,
4734 level=logging.DEBUG,
4735 format="%(asctime)s: %(levelname)s: %(message)s",
4736 datefmt='%m/%d/%Y %I:%M:%S %p')
4738 # Remove the global one
4739 del (sections[sections.index('global')])
4740 self._shares = {}
4741 for i in sections:
4742 self._shares[i] = dict(self.__serverConfig.items(i))
4744 def NetrShareGetInfo(self, data):
4745 request = NetrShareGetInfo(data)
4746 self.log("NetrGetShareInfo Level: %d" % request['Level'])
4748 s = request['NetName'][:-1].upper()
4749 answer = NetrShareGetInfoResponse()
4750 if s in self._shares:
4751 share = self._shares[s]
4753 answer['InfoStruct']['tag'] = 1
4754 answer['InfoStruct']['ShareInfo1']['shi1_netname'] = s + '\x00'
4755 answer['InfoStruct']['ShareInfo1']['shi1_type'] = share['share type']
4756 answer['InfoStruct']['ShareInfo1']['shi1_remark'] = share['comment'] + '\x00'
4757 answer['ErrorCode'] = 0
4758 else:
4759 answer['InfoStruct']['tag'] = 1
4760 answer['InfoStruct']['ShareInfo1'] = NULL
4761 answer['ErrorCode'] = 0x0906 # WERR_NET_NAME_NOT_FOUND
4763 return answer
4765 def NetrServerGetInfo(self, data):
4766 request = NetrServerGetInfo(data)
4767 self.log("NetrServerGetInfo Level: %d" % request['Level'])
4768 answer = NetrServerGetInfoResponse()
4769 answer['InfoStruct']['tag'] = 101
4770 # PLATFORM_ID_NT = 500
4771 answer['InfoStruct']['ServerInfo101']['sv101_platform_id'] = 500
4772 answer['InfoStruct']['ServerInfo101']['sv101_name'] = request['ServerName']
4773 # Windows 7 = 6.1
4774 answer['InfoStruct']['ServerInfo101']['sv101_version_major'] = 6
4775 answer['InfoStruct']['ServerInfo101']['sv101_version_minor'] = 1
4776 # Workstation = 1
4777 answer['InfoStruct']['ServerInfo101']['sv101_type'] = 1
4778 answer['InfoStruct']['ServerInfo101']['sv101_comment'] = NULL
4779 answer['ErrorCode'] = 0
4780 return answer
4782 def NetrShareEnum(self, data):
4783 request = NetrShareEnum(data)
4784 self.log("NetrShareEnum Level: %d" % request['InfoStruct']['Level'])
4785 shareEnum = NetrShareEnumResponse()
4786 shareEnum['InfoStruct']['Level'] = 1
4787 shareEnum['InfoStruct']['ShareInfo']['tag'] = 1
4788 shareEnum['TotalEntries'] = len(self._shares)
4789 shareEnum['InfoStruct']['ShareInfo']['Level1']['EntriesRead'] = len(self._shares)
4790 shareEnum['ErrorCode'] = 0
4792 for i in self._shares:
4793 shareInfo = SHARE_INFO_1()
4794 shareInfo['shi1_netname'] = i + '\x00'
4795 shareInfo['shi1_type'] = self._shares[i]['share type']
4796 shareInfo['shi1_remark'] = self._shares[i]['comment'] + '\x00'
4797 shareEnum['InfoStruct']['ShareInfo']['Level1']['Buffer'].append(shareInfo)
4799 return shareEnum
4802class SimpleSMBServer:
4803 """
4804 SimpleSMBServer class - Implements a simple, customizable SMB Server
4806 :param string listenAddress: the address you want the server to listen on
4807 :param integer listenPort: the port number you want the server to listen on
4808 :param string configFile: a file with all the servers' configuration. If no file specified, this class will create the basic parameters needed to run. You will need to add your shares manually tho. See addShare() method
4809 """
4811 def __init__(self, listenAddress='0.0.0.0', listenPort=445, configFile=''):
4812 if configFile != '':
4813 self.__server = SMBSERVER((listenAddress, listenPort))
4814 self.__server.processConfigFile(configFile)
4815 self.__smbConfig = None
4816 else:
4817 # Here we write a mini config for the server
4818 self.__smbConfig = configparser.ConfigParser()
4819 self.__smbConfig.add_section('global')
4820 self.__smbConfig.set('global', 'server_name',
4821 ''.join([random.choice(string.ascii_letters) for _ in range(8)]))
4822 self.__smbConfig.set('global', 'server_os', ''.join([random.choice(string.ascii_letters) for _ in range(8)])
4823 )
4824 self.__smbConfig.set('global', 'server_domain',
4825 ''.join([random.choice(string.ascii_letters) for _ in range(8)])
4826 )
4827 self.__smbConfig.set('global', 'log_file', 'None')
4828 self.__smbConfig.set('global', 'rpc_apis', 'yes')
4829 self.__smbConfig.set('global', 'credentials_file', '')
4830 self.__smbConfig.set('global', 'challenge', "A" * 16)
4832 # IPC always needed
4833 self.__smbConfig.add_section('IPC$')
4834 self.__smbConfig.set('IPC$', 'comment', '')
4835 self.__smbConfig.set('IPC$', 'read only', 'yes')
4836 self.__smbConfig.set('IPC$', 'share type', '3')
4837 self.__smbConfig.set('IPC$', 'path', '')
4838 self.__server = SMBSERVER((listenAddress, listenPort), config_parser=self.__smbConfig)
4839 self.__server.processConfigFile()
4841 # Now we have to register the MS-SRVS server. This specially important for
4842 # Windows 7+ and Mavericks clients since they WON'T (specially OSX)
4843 # ask for shares using MS-RAP.
4845 self.__srvsServer = SRVSServer()
4846 self.__srvsServer.daemon = True
4847 self.__wkstServer = WKSTServer()
4848 self.__wkstServer.daemon = True
4849 self.__server.registerNamedPipe('srvsvc', ('127.0.0.1', self.__srvsServer.getListenPort()))
4850 self.__server.registerNamedPipe('wkssvc', ('127.0.0.1', self.__wkstServer.getListenPort()))
4852 def start(self):
4853 self.__srvsServer.start()
4854 self.__wkstServer.start()
4855 self.__server.serve_forever()
4857 def stop(self):
4858 self.__server.server_close()
4860 def registerNamedPipe(self, pipeName, address):
4861 return self.__server.registerNamedPipe(pipeName, address)
4863 def unregisterNamedPipe(self, pipeName):
4864 return self.__server.unregisterNamedPipe(pipeName)
4866 def getRegisteredNamedPipes(self):
4867 return self.__server.getRegisteredNamedPipes()
4869 def addShare(self, shareName, sharePath, shareComment='', shareType='0', readOnly='no'):
4870 share = shareName.upper()
4871 self.__smbConfig.add_section(share)
4872 self.__smbConfig.set(share, 'comment', shareComment)
4873 self.__smbConfig.set(share, 'read only', readOnly)
4874 self.__smbConfig.set(share, 'share type', shareType)
4875 self.__smbConfig.set(share, 'path', sharePath)
4876 self.__server.setServerConfig(self.__smbConfig)
4877 self.__srvsServer.setServerConfig(self.__smbConfig)
4878 self.__server.processConfigFile()
4879 self.__srvsServer.processConfigFile()
4881 def removeShare(self, shareName):
4882 self.__smbConfig.remove_section(shareName.upper())
4883 self.__server.setServerConfig(self.__smbConfig)
4884 self.__srvsServer.setServerConfig(self.__smbConfig)
4885 self.__server.processConfigFile()
4886 self.__srvsServer.processConfigFile()
4888 def setSMBChallenge(self, challenge):
4889 if challenge != '':
4890 self.__smbConfig.set('global', 'challenge', challenge)
4891 self.__server.setServerConfig(self.__smbConfig)
4892 self.__server.processConfigFile()
4894 def setLogFile(self, logFile):
4895 self.__smbConfig.set('global', 'log_file', logFile)
4896 self.__server.setServerConfig(self.__smbConfig)
4897 self.__server.processConfigFile()
4899 def setCredentialsFile(self, logFile):
4900 self.__smbConfig.set('global', 'credentials_file', logFile)
4901 self.__server.setServerConfig(self.__smbConfig)
4902 self.__server.processConfigFile()
4904 def addCredential(self, name, uid, lmhash, nthash):
4905 self.__server.addCredential(name, uid, lmhash, nthash)
4907 def setSMB2Support(self, value):
4908 if value is True:
4909 self.__smbConfig.set("global", "SMB2Support", "True")
4910 else:
4911 self.__smbConfig.set("global", "SMB2Support", "False")
4912 self.__server.setServerConfig(self.__smbConfig)
4913 self.__server.processConfigFile()