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

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# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
2#
3# This software is provided under under a slightly modified version
4# of the Apache Software License. See the accompanying LICENSE file
5# for more information.
6#
7# Author: Alberto Solino (@agsolino)
8#
9# TODO:
10# [-] Functions should return NT error codes
11# [-] Handling errors in all situations, right now it's just raising exceptions.
12# [*] Standard authentication support
13# [ ] Organize the connectionData stuff
14# [*] Add capability to send a bad user ID if the user is not authenticated,
15# right now you can ask for any command without actually being authenticated
16# [ ] PATH TRAVERSALS EVERYWHERE.. BE WARNED!
17# [ ] Check error situation (now many places assume the right data is coming)
18# [ ] Implement IPC to the main process so the connectionData is on a single place
19# [ ] Hence.. implement locking
20# estamos en la B
23import calendar
24import socket
25import time
26import datetime
27import struct
28import threading
29import logging
30import logging.config
31import ntpath
32import os
33import fnmatch
34import errno
35import sys
36import random
37import shutil
38import string
39import hashlib
40import hmac
42from binascii import unhexlify, hexlify, a2b_hex
43from six import PY2, b, text_type
44from six.moves import configparser, socketserver
46# For signing
47from impacket import smb, nmb, ntlm, uuid
48from impacket import smb3structs as smb2
49from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, MechTypes, SPNEGO_NegTokenResp, ASN1_AID, \
50 ASN1_SUPPORTED_MECH
51from impacket.nt_errors import STATUS_NO_MORE_FILES, STATUS_NETWORK_NAME_DELETED, STATUS_INVALID_PARAMETER, \
52 STATUS_FILE_CLOSED, STATUS_MORE_PROCESSING_REQUIRED, STATUS_OBJECT_PATH_NOT_FOUND, STATUS_DIRECTORY_NOT_EMPTY, \
53 STATUS_FILE_IS_A_DIRECTORY, STATUS_NOT_IMPLEMENTED, STATUS_INVALID_HANDLE, STATUS_OBJECT_NAME_COLLISION, \
54 STATUS_NO_SUCH_FILE, STATUS_CANCELLED, STATUS_OBJECT_NAME_NOT_FOUND, STATUS_SUCCESS, STATUS_ACCESS_DENIED, \
55 STATUS_NOT_SUPPORTED, STATUS_INVALID_DEVICE_REQUEST, STATUS_FS_DRIVER_REQUIRED, STATUS_INVALID_INFO_CLASS, \
56 STATUS_LOGON_FAILURE, STATUS_OBJECT_PATH_SYNTAX_BAD
58# Setting LOG to current's module name
59LOG = logging.getLogger(__name__)
61# These ones not defined in nt_errors
62STATUS_SMB_BAD_UID = 0x005B0002
63STATUS_SMB_BAD_TID = 0x00050002
66# Utility functions
67# and general functions.
68# There are some common functions that can be accessed from more than one SMB
69# command (or either TRANSACTION). That's why I'm putting them here
70# TODO: Return NT ERROR Codes
72def computeNTLMv2(identity, lmhash, nthash, serverChallenge, authenticateMessage, ntlmChallenge, type1):
73 # Let's calculate the NTLMv2 Response
75 responseKeyNT = ntlm.NTOWFv2(identity, '', authenticateMessage['domain_name'].decode('utf-16le'), nthash)
76 responseKeyLM = ntlm.LMOWFv2(identity, '', authenticateMessage['domain_name'].decode('utf-16le'), lmhash)
78 ntProofStr = authenticateMessage['ntlm'][:16]
79 temp = authenticateMessage['ntlm'][16:]
80 ntProofStr2 = ntlm.hmac_md5(responseKeyNT, serverChallenge + temp)
81 lmChallengeResponse = authenticateMessage['lanman']
82 sessionBaseKey = ntlm.hmac_md5(responseKeyNT, ntProofStr)
84 responseFlags = type1['flags']
86 # Let's check the return flags
87 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) == 0:
88 # No extended session security, taking it out
89 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
90 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_128) == 0:
91 # No support for 128 key len, taking it out
92 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_128
93 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH) == 0:
94 # No key exchange supported, taking it out
95 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
96 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_SEAL) == 0:
97 # No sign available, taking it out
98 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_SEAL
99 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_SIGN) == 0:
100 # No sign available, taking it out
101 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_SIGN
102 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN) == 0:
103 # No sign available, taking it out
104 responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN
106 keyExchangeKey = ntlm.KXKEY(ntlmChallenge['flags'], sessionBaseKey, lmChallengeResponse,
107 ntlmChallenge['challenge'], '',
108 lmhash, nthash, True)
110 # If we set up key exchange, let's fill the right variables
111 if ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
112 exportedSessionKey = authenticateMessage['session_key']
113 exportedSessionKey = ntlm.generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey)
114 else:
115 encryptedRandomSessionKey = None
116 # [MS-NLMP] page 46
117 exportedSessionKey = keyExchangeKey
119 # Do they match?
120 if ntProofStr == ntProofStr2:
121 # Yes!, process login
122 return STATUS_SUCCESS, exportedSessionKey
123 else:
124 return STATUS_LOGON_FAILURE, exportedSessionKey
127def outputToJohnFormat(challenge, username, domain, lmresponse, ntresponse):
128 # We don't want to add a possible failure here, since this is an
129 # extra bonus. We try, if it fails, returns nothing
130 # ToDo: Document the parameter's types (bytes / string) and check all the places where it's called
131 ret_value = ''
132 if type(challenge) is not bytes:
133 challenge = challenge.decode('latin-1')
135 try:
136 if len(ntresponse) > 24:
137 # Extended Security - NTLMv2
138 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (
139 username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(challenge).decode('latin-1'),
140 hexlify(ntresponse).decode('latin-1')[:32],
141 hexlify(ntresponse).decode()[32:]), 'hash_version': 'ntlmv2'}
142 else:
143 # NTLMv1
144 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (
145 username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(lmresponse).decode('latin-1'),
146 hexlify(ntresponse).decode('latin-1'),
147 hexlify(challenge).decode()), 'hash_version': 'ntlm'}
148 except:
149 # Let's try w/o decoding Unicode
150 try:
151 if len(ntresponse) > 24:
152 # Extended Security - NTLMv2
153 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (
154 username.decode('latin-1'), domain.decode('latin-1'), hexlify(challenge).decode('latin-1'),
155 hexlify(ntresponse)[:32].decode('latin-1'), hexlify(ntresponse)[32:].decode('latin-1')),
156 'hash_version': 'ntlmv2'}
157 else:
158 # NTLMv1
159 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % (
160 username, domain, hexlify(lmresponse).decode('latin-1'), hexlify(ntresponse).decode('latin-1'),
161 hexlify(challenge).decode('latin-1')), 'hash_version': 'ntlm'}
162 except Exception as e:
163 import traceback
164 traceback.print_exc()
165 LOG.error("outputToJohnFormat: %s" % e)
166 pass
168 return ret_value
171def writeJohnOutputToFile(hash_string, hash_version, file_name):
172 fn_data = os.path.splitext(file_name)
173 if hash_version == "ntlmv2":
174 output_filename = fn_data[0] + "_ntlmv2" + fn_data[1]
175 else:
176 output_filename = fn_data[0] + "_ntlm" + fn_data[1]
178 with open(output_filename, "a") as f:
179 f.write(hash_string)
180 f.write('\n')
183def decodeSMBString(flags, text):
184 if flags & smb.SMB.FLAGS2_UNICODE:
185 return text.decode('utf-16le')
186 else:
187 return text
190def encodeSMBString(flags, text):
191 if flags & smb.SMB.FLAGS2_UNICODE:
192 return (text).encode('utf-16le')
193 else:
194 return text.encode('ascii')
197def getFileTime(t):
198 t *= 10000000
199 t += 116444736000000000
200 return t
203def getUnixTime(t):
204 t -= 116444736000000000
205 t //= 10000000
206 return t
209def getSMBDate(t):
210 # TODO: Fix this :P
211 d = datetime.date.fromtimestamp(t)
212 year = d.year - 1980
213 ret = (year << 8) + (d.month << 4) + d.day
214 return ret
217def getSMBTime(t):
218 # TODO: Fix this :P
219 d = datetime.datetime.fromtimestamp(t)
220 return (d.hour << 8) + (d.minute << 4) + d.second
223def getShares(connId, smbServer):
224 config = smbServer.getServerConfig()
225 sections = config.sections()
226 # Remove the global one
227 del (sections[sections.index('global')])
228 shares = {}
229 for i in sections:
230 shares[i] = dict(config.items(i))
231 return shares
234def searchShare(connId, share, smbServer):
235 config = smbServer.getServerConfig()
236 if config.has_section(share):
237 return dict(config.items(share))
238 else:
239 return None
242def openFile(path, fileName, accessMode, fileAttributes, openMode):
243 fileName = os.path.normpath(fileName.replace('\\', '/'))
244 errorCode = 0
245 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
246 # strip leading '/'
247 fileName = fileName[1:]
248 pathName = os.path.join(path, fileName)
249 mode = 0
250 # Check the Open Mode
251 if openMode & 0x10:
252 # If the file does not exist, create it.
253 mode = os.O_CREAT
254 else:
255 # If file does not exist, return an error
256 if os.path.exists(pathName) is not True:
257 errorCode = STATUS_NO_SUCH_FILE
258 return 0, mode, pathName, errorCode
260 if os.path.isdir(pathName) and (fileAttributes & smb.ATTR_DIRECTORY) == 0:
261 # Request to open a normal file and this is actually a directory
262 errorCode = STATUS_FILE_IS_A_DIRECTORY
263 return 0, mode, pathName, errorCode
264 # Check the Access Mode
265 if accessMode & 0x7 == 1:
266 mode |= os.O_WRONLY
267 elif accessMode & 0x7 == 2:
268 mode |= os.O_RDWR
269 else:
270 mode = os.O_RDONLY
272 try:
273 if sys.platform == 'win32':
274 mode |= os.O_BINARY
275 fid = os.open(pathName, mode)
276 except Exception as e:
277 LOG.error("openFile: %s,%s" % (pathName, mode), e)
278 fid = 0
279 errorCode = STATUS_ACCESS_DENIED
281 return fid, mode, pathName, errorCode
284def queryFsInformation(path, filename, level=0, pktFlags=smb.SMB.FLAGS2_UNICODE):
285 if pktFlags & smb.SMB.FLAGS2_UNICODE:
286 encoding = 'utf-16le'
287 else:
288 encoding = 'ascii'
290 fileName = os.path.normpath(filename.replace('\\', '/'))
291 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
292 # strip leading '/'
293 fileName = fileName[1:]
294 pathName = os.path.join(path, fileName)
295 fileSize = os.path.getsize(pathName)
296 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)
297 if level == smb.SMB_QUERY_FS_ATTRIBUTE_INFO or level == smb2.SMB2_FILESYSTEM_ATTRIBUTE_INFO:
298 data = smb.SMBQueryFsAttributeInfo()
299 data['FileSystemAttributes'] = smb.FILE_CASE_SENSITIVE_SEARCH | smb.FILE_CASE_PRESERVED_NAMES
300 data['MaxFilenNameLengthInBytes'] = 255
301 data['LengthOfFileSystemName'] = len('XTFS') * 2
302 data['FileSystemName'] = 'XTFS'.encode('utf-16le')
303 return data.getData()
304 elif level == smb.SMB_INFO_VOLUME:
305 data = smb.SMBQueryFsInfoVolume(flags=pktFlags)
306 data['VolumeLabel'] = 'SHARE'.encode(encoding)
307 return data.getData()
308 elif level == smb.SMB_QUERY_FS_VOLUME_INFO or level == smb2.SMB2_FILESYSTEM_VOLUME_INFO:
309 data = smb.SMBQueryFsVolumeInfo()
310 data['VolumeLabel'] = ''
311 data['VolumeCreationTime'] = getFileTime(ctime)
312 return data.getData()
313 elif level == smb.SMB_QUERY_FS_SIZE_INFO:
314 data = smb.SMBQueryFsSizeInfo()
315 return data.getData()
316 elif level == smb.FILE_FS_FULL_SIZE_INFORMATION:
317 data = smb.SMBFileFsFullSizeInformation()
318 return data.getData()
319 elif level == smb.FILE_FS_SIZE_INFORMATION:
320 data = smb.FileFsSizeInformation()
321 return data.getData()
322 else:
323 lastWriteTime = mtime
324 attribs = 0
325 if os.path.isdir(pathName):
326 attribs |= smb.SMB_FILE_ATTRIBUTE_DIRECTORY
327 if os.path.isfile(pathName):
328 attribs |= smb.SMB_FILE_ATTRIBUTE_NORMAL
329 fileAttributes = attribs
330 return fileSize, lastWriteTime, fileAttributes
333def findFirst2(path, fileName, level, searchAttributes, pktFlags=smb.SMB.FLAGS2_UNICODE, isSMB2=False):
334 # TODO: Depending on the level, this could be done much simpler
336 # print "FindFirs2 path:%s, filename:%s" % (path, fileName)
337 fileName = os.path.normpath(fileName.replace('\\', '/'))
338 # Let's choose the right encoding depending on the request
339 if pktFlags & smb.SMB.FLAGS2_UNICODE:
340 encoding = 'utf-16le'
341 else:
342 encoding = 'ascii'
344 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
345 # strip leading '/'
346 fileName = fileName[1:]
348 if not isInFileJail(path, fileName):
349 LOG.error("Path not in current working directory")
350 return [], 0, STATUS_OBJECT_PATH_SYNTAX_BAD
352 pathName = os.path.join(path, fileName)
353 files = []
355 if pathName.find('*') == -1 and pathName.find('?') == -1:
356 # No search patterns
357 pattern = ''
358 else:
359 pattern = os.path.basename(pathName)
360 dirName = os.path.dirname(pathName)
362 # Always add . and .. Not that important for Windows, but Samba whines if
363 # not present (for * search only)
364 if pattern == '*':
365 files.append(os.path.join(dirName, '.'))
366 files.append(os.path.join(dirName, '..'))
368 if pattern != '':
369 for file in os.listdir(dirName):
370 if fnmatch.fnmatch(file.lower(), pattern.lower()):
371 entry = os.path.join(dirName, file)
372 if os.path.isdir(entry):
373 if searchAttributes & smb.ATTR_DIRECTORY:
374 files.append(entry)
375 else:
376 files.append(entry)
377 else:
378 if os.path.exists(pathName):
379 files.append(pathName)
381 searchResult = []
382 searchCount = len(files)
383 errorCode = STATUS_SUCCESS
385 for i in files:
386 if level == smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_BOTH_DIRECTORY_INFO:
387 item = smb.SMBFindFileBothDirectoryInfo(flags=pktFlags)
388 elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO or level == smb2.SMB2_FILE_DIRECTORY_INFO:
389 item = smb.SMBFindFileDirectoryInfo(flags=pktFlags)
390 elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO:
391 item = smb.SMBFindFileFullDirectoryInfo(flags=pktFlags)
392 elif level == smb.SMB_FIND_INFO_STANDARD:
393 item = smb.SMBFindInfoStandard(flags=pktFlags)
394 elif level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_FULL_DIRECTORY_INFO:
395 item = smb.SMBFindFileIdFullDirectoryInfo(flags=pktFlags)
396 elif level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO:
397 item = smb.SMBFindFileIdBothDirectoryInfo(flags=pktFlags)
398 elif level == smb.SMB_FIND_FILE_NAMES_INFO or level == smb2.SMB2_FILE_NAMES_INFO:
399 item = smb.SMBFindFileNamesInfo(flags=pktFlags)
400 else:
401 LOG.error("Wrong level %d!" % level)
402 return searchResult, searchCount, STATUS_NOT_SUPPORTED
404 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(i)
405 if os.path.isdir(i):
406 item['ExtFileAttributes'] = smb.ATTR_DIRECTORY
407 else:
408 item['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
410 item['FileName'] = os.path.basename(i).encode(encoding)
412 if level == smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO or level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_BOTH_DIRECTORY_INFO:
413 item['EaSize'] = 0
414 item['EndOfFile'] = size
415 item['AllocationSize'] = size
416 item['CreationTime'] = getFileTime(ctime)
417 item['LastAccessTime'] = getFileTime(atime)
418 item['LastWriteTime'] = getFileTime(mtime)
419 item['LastChangeTime'] = getFileTime(mtime)
420 item['ShortName'] = '\x00' * 24
421 item['FileName'] = os.path.basename(i).encode(encoding)
422 padLen = (8 - (len(item) % 8)) % 8
423 item['NextEntryOffset'] = len(item) + padLen
424 elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO:
425 item['EndOfFile'] = size
426 item['AllocationSize'] = size
427 item['CreationTime'] = getFileTime(ctime)
428 item['LastAccessTime'] = getFileTime(atime)
429 item['LastWriteTime'] = getFileTime(mtime)
430 item['LastChangeTime'] = getFileTime(mtime)
431 item['FileName'] = os.path.basename(i).encode(encoding)
432 padLen = (8 - (len(item) % 8)) % 8
433 item['NextEntryOffset'] = len(item) + padLen
434 elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO:
435 item['EaSize'] = 0
436 item['EndOfFile'] = size
437 item['AllocationSize'] = size
438 item['CreationTime'] = getFileTime(ctime)
439 item['LastAccessTime'] = getFileTime(atime)
440 item['LastWriteTime'] = getFileTime(mtime)
441 item['LastChangeTime'] = getFileTime(mtime)
442 padLen = (8 - (len(item) % 8)) % 8
443 item['NextEntryOffset'] = len(item) + padLen
444 elif level == smb.SMB_FIND_INFO_STANDARD:
445 item['EaSize'] = size
446 item['CreationDate'] = getSMBDate(ctime)
447 item['CreationTime'] = getSMBTime(ctime)
448 item['LastAccessDate'] = getSMBDate(atime)
449 item['LastAccessTime'] = getSMBTime(atime)
450 item['LastWriteDate'] = getSMBDate(mtime)
451 item['LastWriteTime'] = getSMBTime(mtime)
452 searchResult.append(item)
454 # No more files
455 if (level >= smb.SMB_FIND_FILE_DIRECTORY_INFO or isSMB2 is True) and searchCount > 0:
456 searchResult[-1]['NextEntryOffset'] = 0
458 return searchResult, searchCount, errorCode
461def queryFileInformation(path, filename, level):
462 # print "queryFileInfo path: %s, filename: %s, level:0x%x" % (path,filename,level)
463 return queryPathInformation(path, filename, level)
466def queryPathInformation(path, filename, level):
467 # TODO: Depending on the level, this could be done much simpler
468 # print("queryPathInfo path: %s, filename: %s, level:0x%x" % (path,filename,level))
469 try:
470 errorCode = 0
471 fileName = os.path.normpath(filename.replace('\\', '/'))
472 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '':
473 # strip leading '/'
474 fileName = fileName[1:]
475 pathName = os.path.join(path, fileName)
476 if os.path.exists(pathName):
477 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)
478 if level == smb.SMB_QUERY_FILE_BASIC_INFO:
479 infoRecord = smb.SMBQueryFileBasicInfo()
480 infoRecord['CreationTime'] = getFileTime(ctime)
481 infoRecord['LastAccessTime'] = getFileTime(atime)
482 infoRecord['LastWriteTime'] = getFileTime(mtime)
483 infoRecord['LastChangeTime'] = getFileTime(mtime)
484 if os.path.isdir(pathName):
485 infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY
486 else:
487 infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
488 elif level == smb.SMB_QUERY_FILE_STANDARD_INFO:
489 infoRecord = smb.SMBQueryFileStandardInfo()
490 infoRecord['AllocationSize'] = size
491 infoRecord['EndOfFile'] = size
492 if os.path.isdir(pathName):
493 infoRecord['Directory'] = 1
494 else:
495 infoRecord['Directory'] = 0
496 elif level == smb.SMB_QUERY_FILE_ALL_INFO or level == smb2.SMB2_FILE_ALL_INFO:
497 infoRecord = smb.SMBQueryFileAllInfo()
498 infoRecord['CreationTime'] = getFileTime(ctime)
499 infoRecord['LastAccessTime'] = getFileTime(atime)
500 infoRecord['LastWriteTime'] = getFileTime(mtime)
501 infoRecord['LastChangeTime'] = getFileTime(mtime)
502 if os.path.isdir(pathName):
503 infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY
504 else:
505 infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
506 infoRecord['AllocationSize'] = size
507 infoRecord['EndOfFile'] = size
508 if os.path.isdir(pathName):
509 infoRecord['Directory'] = 1
510 else:
511 infoRecord['Directory'] = 0
512 infoRecord['FileName'] = filename.encode('utf-16le')
513 elif level == smb2.SMB2_FILE_NETWORK_OPEN_INFO:
514 infoRecord = smb.SMBFileNetworkOpenInfo()
515 infoRecord['CreationTime'] = getFileTime(ctime)
516 infoRecord['LastAccessTime'] = getFileTime(atime)
517 infoRecord['LastWriteTime'] = getFileTime(mtime)
518 infoRecord['ChangeTime'] = getFileTime(mtime)
519 infoRecord['AllocationSize'] = size
520 infoRecord['EndOfFile'] = size
521 if os.path.isdir(pathName):
522 infoRecord['FileAttributes'] = smb.ATTR_DIRECTORY
523 else:
524 infoRecord['FileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
525 elif level == smb.SMB_QUERY_FILE_EA_INFO or level == smb2.SMB2_FILE_EA_INFO:
526 infoRecord = smb.SMBQueryFileEaInfo()
527 elif level == smb2.SMB2_FILE_STREAM_INFO:
528 infoRecord = smb.SMBFileStreamInformation()
529 else:
530 LOG.error('Unknown level for query path info! 0x%x' % level)
531 # UNSUPPORTED
532 return None, STATUS_NOT_SUPPORTED
534 return infoRecord, errorCode
535 else:
536 # NOT FOUND
537 return None, STATUS_OBJECT_NAME_NOT_FOUND
538 except Exception as e:
539 LOG.error('queryPathInfo: %s' % e)
540 raise
543def queryDiskInformation(path):
544 # TODO: Do something useful here :)
545 # For now we just return fake values
546 totalUnits = 65535
547 freeUnits = 65535
548 return totalUnits, freeUnits
551def isInFileJail(path, fileName):
552 pathName = os.path.join(path, fileName)
553 share_real_path = os.path.realpath(path)
554 return os.path.commonprefix((os.path.realpath(pathName), share_real_path)) == share_real_path
557# Here we implement the NT transaction handlers
558class NTTRANSCommands:
559 def default(self, connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
560 pass
563# Here we implement the NT transaction handlers
564class TRANSCommands:
565 @staticmethod
566 def lanMan(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
567 # Minimal [MS-RAP] implementation, just to return the shares
568 connData = smbServer.getConnectionData(connId)
570 respSetup = b''
571 respParameters = b''
572 respData = b''
573 errorCode = STATUS_SUCCESS
574 if struct.unpack('<H', parameters[:2])[0] == 0:
575 # NetShareEnum Request
576 netShareEnum = smb.SMBNetShareEnum(parameters)
577 if netShareEnum['InfoLevel'] == 1:
578 shares = getShares(connId, smbServer)
579 respParameters = smb.SMBNetShareEnumResponse()
580 respParameters['EntriesReturned'] = len(shares)
581 respParameters['EntriesAvailable'] = len(shares)
582 tailData = ''
583 for i in shares:
584 # NetShareInfo1 len == 20
585 entry = smb.NetShareInfo1()
586 entry['NetworkName'] = i + '\x00' * (13 - len(i))
587 entry['Type'] = int(shares[i]['share type'])
588 # (beto) If offset == 0 it crashes explorer.exe on windows 7
589 entry['RemarkOffsetLow'] = 20 * len(shares) + len(tailData)
590 respData += entry.getData()
591 if 'comment' in shares[i]:
592 tailData += shares[i]['comment'] + '\x00'
593 else:
594 tailData += '\x00'
595 respData += tailData
596 else:
597 # We don't support other info levels
598 errorCode = STATUS_NOT_SUPPORTED
599 elif struct.unpack('<H', parameters[:2])[0] == 13:
600 # NetrServerGetInfo Request
601 respParameters = smb.SMBNetServerGetInfoResponse()
602 netServerInfo = smb.SMBNetServerInfo1()
603 netServerInfo['ServerName'] = smbServer.getServerName()
604 respData = netServerInfo.getData()
605 respParameters['TotalBytesAvailable'] = len(respData)
606 elif struct.unpack('<H', parameters[:2])[0] == 1:
607 # NetrShareGetInfo Request
608 request = smb.SMBNetShareGetInfo(parameters)
609 respParameters = smb.SMBNetShareGetInfoResponse()
610 shares = getShares(connId, smbServer)
611 share = shares[request['ShareName'].upper()]
612 shareInfo = smb.NetShareInfo1()
613 shareInfo['NetworkName'] = request['ShareName'].upper() + '\x00'
614 shareInfo['Type'] = int(share['share type'])
615 respData = shareInfo.getData()
616 if 'comment' in share:
617 shareInfo['RemarkOffsetLow'] = len(respData)
618 respData += share['comment'] + '\x00'
619 respParameters['TotalBytesAvailable'] = len(respData)
621 else:
622 # We don't know how to handle anything else
623 errorCode = STATUS_NOT_SUPPORTED
625 smbServer.setConnectionData(connId, connData)
627 return respSetup, respParameters, respData, errorCode
629 @staticmethod
630 def transactNamedPipe(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
631 connData = smbServer.getConnectionData(connId)
633 respSetup = b''
634 respParameters = b''
635 respData = b''
636 errorCode = STATUS_SUCCESS
637 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
638 transParameters = smb.SMBTransaction_Parameters(SMBCommand['Parameters'])
640 # Extract the FID
641 fid = struct.unpack('<H', transParameters['Setup'][2:])[0]
643 if fid in connData['OpenedFiles']:
644 fileHandle = connData['OpenedFiles'][fid]['FileHandle']
645 if fileHandle != PIPE_FILE_DESCRIPTOR:
646 os.write(fileHandle, data)
647 respData = os.read(fileHandle, data)
648 else:
649 sock = connData['OpenedFiles'][fid]['Socket']
650 sock.send(data)
651 respData = sock.recv(maxDataCount)
652 else:
653 errorCode = STATUS_INVALID_HANDLE
655 smbServer.setConnectionData(connId, connData)
657 return respSetup, respParameters, respData, errorCode
660# Here we implement the transaction2 handlers
661class TRANS2Commands:
662 # All these commands return setup, parameters, data, errorCode
663 @staticmethod
664 def setPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
665 connData = smbServer.getConnectionData(connId)
667 respSetup = b''
668 respParameters = b''
669 respData = b''
670 errorCode = STATUS_SUCCESS
671 setPathInfoParameters = smb.SMBSetPathInformation_Parameters(flags=recvPacket['Flags2'], data=parameters)
672 if recvPacket['Tid'] in connData['ConnectedShares']:
673 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
674 fileName = decodeSMBString(recvPacket['Flags2'], setPathInfoParameters['FileName'])
675 fileName = os.path.normpath(fileName.replace('\\', '/'))
676 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '':
677 # strip leading '/'
678 fileName = fileName[1:]
679 pathName = os.path.join(path, fileName)
680 if os.path.exists(pathName):
681 informationLevel = setPathInfoParameters['InformationLevel']
682 if informationLevel == smb.SMB_SET_FILE_BASIC_INFO:
683 infoRecord = smb.SMBSetFileBasicInfo(data)
684 # Creation time won't be set, the other ones we play with.
685 atime = infoRecord['LastAccessTime']
686 if atime == 0:
687 atime = -1
688 else:
689 atime = getUnixTime(atime)
690 mtime = infoRecord['LastWriteTime']
691 if mtime == 0:
692 mtime = -1
693 else:
694 mtime = getUnixTime(mtime)
695 if mtime != -1 or atime != -1:
696 os.utime(pathName, (atime, mtime))
697 else:
698 smbServer.log('Unknown level for set path info! 0x%x' % setPathInfoParameters['InformationLevel'],
699 logging.ERROR)
700 # UNSUPPORTED
701 errorCode = STATUS_NOT_SUPPORTED
702 else:
703 errorCode = STATUS_OBJECT_NAME_NOT_FOUND
705 if errorCode == STATUS_SUCCESS:
706 respParameters = smb.SMBSetPathInformationResponse_Parameters()
708 else:
709 errorCode = STATUS_SMB_BAD_TID
711 smbServer.setConnectionData(connId, connData)
713 return respSetup, respParameters, respData, errorCode
715 @staticmethod
716 def setFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
717 connData = smbServer.getConnectionData(connId)
719 respSetup = b''
720 respParameters = b''
721 respData = b''
722 errorCode = STATUS_SUCCESS
723 setFileInfoParameters = smb.SMBSetFileInformation_Parameters(parameters)
725 if recvPacket['Tid'] in connData['ConnectedShares']:
726 if setFileInfoParameters['FID'] in connData['OpenedFiles']:
727 fileName = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileName']
728 informationLevel = setFileInfoParameters['InformationLevel']
729 if informationLevel == smb.SMB_SET_FILE_DISPOSITION_INFO:
730 infoRecord = smb.SMBSetFileDispositionInfo(parameters)
731 if infoRecord['DeletePending'] > 0:
732 # Mark this file for removal after closed
733 connData['OpenedFiles'][setFileInfoParameters['FID']]['DeleteOnClose'] = True
734 respParameters = smb.SMBSetFileInformationResponse_Parameters()
735 elif informationLevel == smb.SMB_SET_FILE_BASIC_INFO:
736 infoRecord = smb.SMBSetFileBasicInfo(data)
737 # Creation time won't be set, the other ones we play with.
738 atime = infoRecord['LastAccessTime']
739 if atime == 0:
740 atime = -1
741 else:
742 atime = getUnixTime(atime)
743 mtime = infoRecord['LastWriteTime']
744 if mtime == 0:
745 mtime = -1
746 else:
747 mtime = getUnixTime(mtime)
748 os.utime(fileName, (atime, mtime))
749 elif informationLevel == smb.SMB_SET_FILE_END_OF_FILE_INFO:
750 fileHandle = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileHandle']
751 infoRecord = smb.SMBSetFileEndOfFileInfo(data)
752 if infoRecord['EndOfFile'] > 0:
753 os.lseek(fileHandle, infoRecord['EndOfFile'] - 1, 0)
754 os.write(fileHandle, b'\x00')
755 else:
756 smbServer.log('Unknown level for set file info! 0x%x' % setFileInfoParameters['InformationLevel'],
757 logging.ERROR)
758 # UNSUPPORTED
759 errorCode = STATUS_NOT_SUPPORTED
760 else:
761 errorCode = STATUS_NO_SUCH_FILE
763 if errorCode == STATUS_SUCCESS:
764 respParameters = smb.SMBSetFileInformationResponse_Parameters()
765 else:
766 errorCode = STATUS_SMB_BAD_TID
768 smbServer.setConnectionData(connId, connData)
770 return respSetup, respParameters, respData, errorCode
772 @staticmethod
773 def queryFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
774 connData = smbServer.getConnectionData(connId)
776 respSetup = b''
777 respParameters = b''
778 respData = b''
780 queryFileInfoParameters = smb.SMBQueryFileInformation_Parameters(parameters)
782 if recvPacket['Tid'] in connData['ConnectedShares']:
783 if queryFileInfoParameters['FID'] in connData['OpenedFiles']:
784 fileName = connData['OpenedFiles'][queryFileInfoParameters['FID']]['FileName']
786 infoRecord, errorCode = queryFileInformation('', fileName, queryFileInfoParameters['InformationLevel'])
788 if infoRecord is not None:
789 respParameters = smb.SMBQueryFileInformationResponse_Parameters()
790 respData = infoRecord
791 else:
792 errorCode = STATUS_INVALID_HANDLE
793 else:
794 errorCode = STATUS_SMB_BAD_TID
796 smbServer.setConnectionData(connId, connData)
798 return respSetup, respParameters, respData, errorCode
800 @staticmethod
801 def queryPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
802 connData = smbServer.getConnectionData(connId)
804 respSetup = b''
805 respParameters = b''
806 respData = b''
807 errorCode = 0
809 queryPathInfoParameters = smb.SMBQueryPathInformation_Parameters(flags=recvPacket['Flags2'], data=parameters)
811 if recvPacket['Tid'] in connData['ConnectedShares']:
812 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
813 try:
814 infoRecord, errorCode = queryPathInformation(path, decodeSMBString(recvPacket['Flags2'],
815 queryPathInfoParameters['FileName']),
816 queryPathInfoParameters['InformationLevel'])
817 except Exception as e:
818 smbServer.log("queryPathInformation: %s" % e, logging.ERROR)
820 if infoRecord is not None:
821 respParameters = smb.SMBQueryPathInformationResponse_Parameters()
822 respData = infoRecord
823 else:
824 errorCode = STATUS_SMB_BAD_TID
826 smbServer.setConnectionData(connId, connData)
828 return respSetup, respParameters, respData, errorCode
830 @staticmethod
831 def queryFsInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount=0):
832 connData = smbServer.getConnectionData(connId)
833 errorCode = 0
834 # Get the Tid associated
835 if recvPacket['Tid'] in connData['ConnectedShares']:
836 data = queryFsInformation(connData['ConnectedShares'][recvPacket['Tid']]['path'], '',
837 struct.unpack('<H', parameters)[0], pktFlags=recvPacket['Flags2'])
839 smbServer.setConnectionData(connId, connData)
841 return b'', b'', data, errorCode
843 @staticmethod
844 def findNext2(connId, smbServer, recvPacket, parameters, data, maxDataCount):
845 connData = smbServer.getConnectionData(connId)
847 respSetup = b''
848 respParameters = b''
849 respData = b''
850 errorCode = STATUS_SUCCESS
851 findNext2Parameters = smb.SMBFindNext2_Parameters(flags=recvPacket['Flags2'], data=parameters)
853 sid = findNext2Parameters['SID']
854 if recvPacket['Tid'] in connData['ConnectedShares']:
855 if sid in connData['SIDs']:
856 searchResult = connData['SIDs'][sid]
857 respParameters = smb.SMBFindNext2Response_Parameters()
858 endOfSearch = 1
859 searchCount = 1
860 totalData = 0
861 for i in enumerate(searchResult):
862 data = i[1].getData()
863 lenData = len(data)
864 if (totalData + lenData) >= maxDataCount or (i[0] + 1) >= findNext2Parameters['SearchCount']:
865 # We gotta stop here and continue on a find_next2
866 endOfSearch = 0
867 connData['SIDs'][sid] = searchResult[i[0]:]
868 respParameters['LastNameOffset'] = totalData
869 break
870 else:
871 searchCount += 1
872 respData += data
873 totalData += lenData
875 # Have we reached the end of the search or still stuff to send?
876 if endOfSearch > 0:
877 # Let's remove the SID from our ConnData
878 del (connData['SIDs'][sid])
880 respParameters['EndOfSearch'] = endOfSearch
881 respParameters['SearchCount'] = searchCount
882 else:
883 errorCode = STATUS_INVALID_HANDLE
884 else:
885 errorCode = STATUS_SMB_BAD_TID
887 smbServer.setConnectionData(connId, connData)
889 return respSetup, respParameters, respData, errorCode
891 @staticmethod
892 def findFirst2(connId, smbServer, recvPacket, parameters, data, maxDataCount):
893 connData = smbServer.getConnectionData(connId)
895 respSetup = b''
896 respParameters = b''
897 respData = b''
898 findFirst2Parameters = smb.SMBFindFirst2_Parameters(recvPacket['Flags2'], data=parameters)
900 if recvPacket['Tid'] in connData['ConnectedShares']:
901 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
903 searchResult, searchCount, errorCode = findFirst2(path,
904 decodeSMBString(recvPacket['Flags2'],
905 findFirst2Parameters['FileName']),
906 findFirst2Parameters['InformationLevel'],
907 findFirst2Parameters['SearchAttributes'],
908 pktFlags=recvPacket['Flags2'])
910 respParameters = smb.SMBFindFirst2Response_Parameters()
911 endOfSearch = 1
912 sid = 0x80 # default SID
913 searchCount = 0
914 totalData = 0
915 for i in enumerate(searchResult):
916 # i[1].dump()
917 data = i[1].getData()
918 lenData = len(data)
919 if (totalData + lenData) >= maxDataCount or (i[0] + 1) > findFirst2Parameters['SearchCount']:
920 # We gotta stop here and continue on a find_next2
921 endOfSearch = 0
922 # Simple way to generate a fid
923 if len(connData['SIDs']) == 0:
924 sid = 1
925 else:
926 sid = list(connData['SIDs'].keys())[-1] + 1
927 # Store the remaining search results in the ConnData SID
928 connData['SIDs'][sid] = searchResult[i[0]:]
929 respParameters['LastNameOffset'] = totalData
930 break
931 else:
932 searchCount += 1
933 respData += data
935 padLen = (8 - (lenData % 8)) % 8
936 respData += b'\xaa' * padLen
937 totalData += lenData + padLen
939 respParameters['SID'] = sid
940 respParameters['EndOfSearch'] = endOfSearch
941 respParameters['SearchCount'] = searchCount
942 else:
943 errorCode = STATUS_SMB_BAD_TID
945 smbServer.setConnectionData(connId, connData)
947 return respSetup, respParameters, respData, errorCode
950# Here we implement the commands handlers
951class SMBCommands:
953 @staticmethod
954 def smbTransaction(connId, smbServer, SMBCommand, recvPacket, transCommands):
955 connData = smbServer.getConnectionData(connId)
957 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
959 transParameters = smb.SMBTransaction_Parameters(SMBCommand['Parameters'])
961 # Do the stuff
962 if transParameters['ParameterCount'] != transParameters['TotalParameterCount']:
963 # TODO: Handle partial parameters
964 raise Exception("Unsupported partial parameters in TRANSACT2!")
965 else:
966 transData = smb.SMBTransaction_SData(flags=recvPacket['Flags2'])
967 # Standard says servers shouldn't trust Parameters and Data comes
968 # in order, so we have to parse the offsets, ugly
970 paramCount = transParameters['ParameterCount']
971 transData['Trans_ParametersLength'] = paramCount
972 dataCount = transParameters['DataCount']
973 transData['Trans_DataLength'] = dataCount
974 transData.fromString(SMBCommand['Data'])
975 if transParameters['ParameterOffset'] > 0:
976 paramOffset = transParameters['ParameterOffset'] - 63 - transParameters['SetupLength']
977 transData['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset + paramCount]
978 else:
979 transData['Trans_Parameters'] = b''
981 if transParameters['DataOffset'] > 0:
982 dataOffset = transParameters['DataOffset'] - 63 - transParameters['SetupLength']
983 transData['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
984 else:
985 transData['Trans_Data'] = b''
987 # Call the handler for this TRANSACTION
988 if transParameters['SetupCount'] == 0:
989 # No subcommand, let's play with the Name
990 command = decodeSMBString(recvPacket['Flags2'], transData['Name'])
991 else:
992 command = struct.unpack('<H', transParameters['Setup'][:2])[0]
994 if command in transCommands:
995 # Call the TRANS subcommand
996 setup = b''
997 parameters = b''
998 data = b''
999 try:
1000 setup, parameters, data, errorCode = transCommands[command](connId,
1001 smbServer,
1002 recvPacket,
1003 transData['Trans_Parameters'],
1004 transData['Trans_Data'],
1005 transParameters['MaxDataCount'])
1006 except Exception as e:
1007 # print 'Transaction: %s' % e,e
1008 smbServer.log('Transaction: (%r,%s)' % (command, e), logging.ERROR)
1009 errorCode = STATUS_ACCESS_DENIED
1010 # raise
1012 if setup == b'' and parameters == b'' and data == b'':
1013 # Something wen't wrong
1014 respParameters = b''
1015 respData = b''
1016 else:
1017 # Build the answer
1018 if hasattr(data, 'getData'):
1019 data = data.getData()
1020 remainingData = len(data)
1021 if hasattr(parameters, 'getData'):
1022 parameters = parameters.getData()
1023 remainingParameters = len(parameters)
1024 commands = []
1025 dataDisplacement = 0
1026 while remainingData > 0 or remainingParameters > 0:
1027 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
1028 respParameters = smb.SMBTransactionResponse_Parameters()
1029 respData = smb.SMBTransaction2Response_Data()
1031 respParameters['TotalParameterCount'] = len(parameters)
1032 respParameters['ParameterCount'] = len(parameters)
1033 respData['Trans_ParametersLength'] = len(parameters)
1034 respParameters['TotalDataCount'] = len(data)
1035 respParameters['DataDisplacement'] = dataDisplacement
1037 # TODO: Do the same for parameters
1038 if len(data) > transParameters['MaxDataCount']:
1039 # Answer doesn't fit in this packet
1040 LOG.debug("Lowering answer from %d to %d" % (len(data), transParameters['MaxDataCount']))
1041 respParameters['DataCount'] = transParameters['MaxDataCount']
1042 else:
1043 respParameters['DataCount'] = len(data)
1045 respData['Trans_DataLength'] = respParameters['DataCount']
1046 respParameters['SetupCount'] = len(setup)
1047 respParameters['Setup'] = setup
1048 # TODO: Make sure we're calculating the pad right
1049 if len(parameters) > 0:
1050 # padLen = 4 - (55 + len(setup)) % 4
1051 padLen = (4 - (55 + len(setup)) % 4) % 4
1052 padBytes = b'\xFF' * padLen
1053 respData['Pad1'] = padBytes
1054 respParameters['ParameterOffset'] = 55 + len(setup) + padLen
1055 else:
1056 padLen = 0
1057 respParameters['ParameterOffset'] = 0
1058 respData['Pad1'] = b''
1060 if len(data) > 0:
1061 # pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4
1062 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4
1063 respData['Pad2'] = b'\xFF' * pad2Len
1064 respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len
1065 else:
1066 respParameters['DataOffset'] = 0
1067 respData['Pad2'] = b''
1069 respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']]
1070 respData['Trans_Data'] = data[:respParameters['DataCount']]
1071 respSMBCommand['Parameters'] = respParameters
1072 respSMBCommand['Data'] = respData
1074 data = data[respParameters['DataCount']:]
1075 remainingData -= respParameters['DataCount']
1076 dataDisplacement += respParameters['DataCount'] + 1
1078 parameters = parameters[respParameters['ParameterCount']:]
1079 remainingParameters -= respParameters['ParameterCount']
1080 commands.append(respSMBCommand)
1082 smbServer.setConnectionData(connId, connData)
1083 return commands, None, errorCode
1085 else:
1086 smbServer.log("Unsupported Transact command %r" % command, logging.ERROR)
1087 respParameters = b''
1088 respData = b''
1089 errorCode = STATUS_NOT_IMPLEMENTED
1091 respSMBCommand['Parameters'] = respParameters
1092 respSMBCommand['Data'] = respData
1093 smbServer.setConnectionData(connId, connData)
1095 return [respSMBCommand], None, errorCode
1097 @staticmethod
1098 def smbNTTransact(connId, smbServer, SMBCommand, recvPacket, transCommands):
1099 connData = smbServer.getConnectionData(connId)
1101 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
1103 NTTransParameters = smb.SMBNTTransaction_Parameters(SMBCommand['Parameters'])
1104 # Do the stuff
1105 if NTTransParameters['ParameterCount'] != NTTransParameters['TotalParameterCount']:
1106 # TODO: Handle partial parameters
1107 raise Exception("Unsupported partial parameters in NTTrans!")
1108 else:
1109 NTTransData = smb.SMBNTTransaction_Data()
1110 # Standard says servers shouldn't trust Parameters and Data comes
1111 # in order, so we have to parse the offsets, ugly
1113 paramCount = NTTransParameters['ParameterCount']
1114 NTTransData['NT_Trans_ParametersLength'] = paramCount
1115 dataCount = NTTransParameters['DataCount']
1116 NTTransData['NT_Trans_DataLength'] = dataCount
1118 if NTTransParameters['ParameterOffset'] > 0:
1119 paramOffset = NTTransParameters['ParameterOffset'] - 73 - NTTransParameters['SetupLength']
1120 NTTransData['NT_Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset + paramCount]
1121 else:
1122 NTTransData['NT_Trans_Parameters'] = b''
1124 if NTTransParameters['DataOffset'] > 0:
1125 dataOffset = NTTransParameters['DataOffset'] - 73 - NTTransParameters['SetupLength']
1126 NTTransData['NT_Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
1127 else:
1128 NTTransData['NT_Trans_Data'] = b''
1130 # Call the handler for this TRANSACTION
1131 command = NTTransParameters['Function']
1132 if command in transCommands:
1133 # Call the NT TRANS subcommand
1134 setup = b''
1135 parameters = b''
1136 data = b''
1137 try:
1138 setup, parameters, data, errorCode = transCommands[command](connId,
1139 smbServer,
1140 recvPacket,
1141 NTTransData['NT_Trans_Parameters'],
1142 NTTransData['NT_Trans_Data'],
1143 NTTransParameters['MaxDataCount'])
1144 except Exception as e:
1145 smbServer.log('NTTransaction: (0x%x,%s)' % (command, e), logging.ERROR)
1146 errorCode = STATUS_ACCESS_DENIED
1147 # raise
1149 if setup == b'' and parameters == b'' and data == b'':
1150 # Something wen't wrong
1151 respParameters = b''
1152 respData = b''
1153 if errorCode == STATUS_SUCCESS:
1154 errorCode = STATUS_ACCESS_DENIED
1155 else:
1156 # Build the answer
1157 if hasattr(data, 'getData'):
1158 data = data.getData()
1159 remainingData = len(data)
1160 if hasattr(parameters, 'getData'):
1161 parameters = parameters.getData()
1162 remainingParameters = len(parameters)
1163 commands = []
1164 dataDisplacement = 0
1165 while remainingData > 0 or remainingParameters > 0:
1166 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
1167 respParameters = smb.SMBNTTransactionResponse_Parameters()
1168 respData = smb.SMBNTTransactionResponse_Data()
1170 respParameters['TotalParameterCount'] = len(parameters)
1171 respParameters['ParameterCount'] = len(parameters)
1172 respData['Trans_ParametersLength'] = len(parameters)
1173 respParameters['TotalDataCount'] = len(data)
1174 respParameters['DataDisplacement'] = dataDisplacement
1175 # TODO: Do the same for parameters
1176 if len(data) > NTTransParameters['MaxDataCount']:
1177 # Answer doesn't fit in this packet
1178 LOG.debug("Lowering answer from %d to %d" % (len(data), NTTransParameters['MaxDataCount']))
1179 respParameters['DataCount'] = NTTransParameters['MaxDataCount']
1180 else:
1181 respParameters['DataCount'] = len(data)
1183 respData['NT_Trans_DataLength'] = respParameters['DataCount']
1184 respParameters['SetupCount'] = len(setup)
1185 respParameters['Setup'] = setup
1186 # TODO: Make sure we're calculating the pad right
1187 if len(parameters) > 0:
1188 # padLen = 4 - (71 + len(setup)) % 4
1189 padLen = (4 - (73 + len(setup)) % 4) % 4
1190 padBytes = b'\xFF' * padLen
1191 respData['Pad1'] = padBytes
1192 respParameters['ParameterOffset'] = 73 + len(setup) + padLen
1193 else:
1194 padLen = 0
1195 respParameters['ParameterOffset'] = 0
1196 respData['Pad1'] = b''
1198 if len(data) > 0:
1199 # pad2Len = 4 - (71 + len(setup) + padLen + len(parameters)) % 4
1200 pad2Len = (4 - (73 + len(setup) + padLen + len(parameters)) % 4) % 4
1201 respData['Pad2'] = b'\xFF' * pad2Len
1202 respParameters['DataOffset'] = 73 + len(setup) + padLen + len(parameters) + pad2Len
1203 else:
1204 respParameters['DataOffset'] = 0
1205 respData['Pad2'] = b''
1207 respData['NT_Trans_Parameters'] = parameters[:respParameters['ParameterCount']]
1208 respData['NT_Trans_Data'] = data[:respParameters['DataCount']]
1209 respSMBCommand['Parameters'] = respParameters
1210 respSMBCommand['Data'] = respData
1212 data = data[respParameters['DataCount']:]
1213 remainingData -= respParameters['DataCount']
1214 dataDisplacement += respParameters['DataCount'] + 1
1216 parameters = parameters[respParameters['ParameterCount']:]
1217 remainingParameters -= respParameters['ParameterCount']
1218 commands.append(respSMBCommand)
1220 smbServer.setConnectionData(connId, connData)
1221 return commands, None, errorCode
1223 else:
1224 # smbServer.log("Unsupported NTTransact command 0x%x" % command, logging.ERROR)
1225 respParameters = b''
1226 respData = b''
1227 errorCode = STATUS_NOT_IMPLEMENTED
1229 respSMBCommand['Parameters'] = respParameters
1230 respSMBCommand['Data'] = respData
1232 smbServer.setConnectionData(connId, connData)
1233 return [respSMBCommand], None, errorCode
1235 @staticmethod
1236 def smbTransaction2(connId, smbServer, SMBCommand, recvPacket, transCommands):
1237 connData = smbServer.getConnectionData(connId)
1239 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
1241 trans2Parameters = smb.SMBTransaction2_Parameters(SMBCommand['Parameters'])
1243 # Do the stuff
1244 if trans2Parameters['ParameterCount'] != trans2Parameters['TotalParameterCount']:
1245 # TODO: Handle partial parameters
1246 # print "Unsupported partial parameters in TRANSACT2!"
1247 raise Exception("Unsupported partial parameters in TRANSACT2!")
1248 else:
1249 trans2Data = smb.SMBTransaction2_Data()
1250 # Standard says servers shouldn't trust Parameters and Data comes
1251 # in order, so we have to parse the offsets, ugly
1253 paramCount = trans2Parameters['ParameterCount']
1254 trans2Data['Trans_ParametersLength'] = paramCount
1255 dataCount = trans2Parameters['DataCount']
1256 trans2Data['Trans_DataLength'] = dataCount
1258 if trans2Parameters['ParameterOffset'] > 0:
1259 paramOffset = trans2Parameters['ParameterOffset'] - 63 - trans2Parameters['SetupLength']
1260 trans2Data['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset + paramCount]
1261 else:
1262 trans2Data['Trans_Parameters'] = b''
1264 if trans2Parameters['DataOffset'] > 0:
1265 dataOffset = trans2Parameters['DataOffset'] - 63 - trans2Parameters['SetupLength']
1266 trans2Data['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
1267 else:
1268 trans2Data['Trans_Data'] = b''
1270 # Call the handler for this TRANSACTION
1271 command = struct.unpack('<H', trans2Parameters['Setup'])[0]
1272 if command in transCommands:
1273 # Call the TRANS2 subcommand
1274 try:
1275 setup, parameters, data, errorCode = transCommands[command](connId,
1276 smbServer,
1277 recvPacket,
1278 trans2Data['Trans_Parameters'],
1279 trans2Data['Trans_Data'],
1280 trans2Parameters['MaxDataCount'])
1281 except Exception as e:
1282 smbServer.log('Transaction2: (0x%x,%s)' % (command, e), logging.ERROR)
1283 # import traceback
1284 # traceback.print_exc()
1285 raise
1287 if setup == b'' and parameters == b'' and data == b'':
1288 # Something wen't wrong
1289 respParameters = b''
1290 respData = b''
1291 else:
1292 # Build the answer
1293 if hasattr(data, 'getData'):
1294 data = data.getData()
1295 remainingData = len(data)
1296 if hasattr(parameters, 'getData'):
1297 parameters = parameters.getData()
1298 remainingParameters = len(parameters)
1299 commands = []
1300 dataDisplacement = 0
1301 while remainingData > 0 or remainingParameters > 0:
1302 respSMBCommand = smb.SMBCommand(recvPacket['Command'])
1303 respParameters = smb.SMBTransaction2Response_Parameters()
1304 respData = smb.SMBTransaction2Response_Data()
1306 respParameters['TotalParameterCount'] = len(parameters)
1307 respParameters['ParameterCount'] = len(parameters)
1308 respData['Trans_ParametersLength'] = len(parameters)
1309 respParameters['TotalDataCount'] = len(data)
1310 respParameters['DataDisplacement'] = dataDisplacement
1311 # TODO: Do the same for parameters
1312 if len(data) > trans2Parameters['MaxDataCount']:
1313 # Answer doesn't fit in this packet
1314 LOG.debug("Lowering answer from %d to %d" % (len(data), trans2Parameters['MaxDataCount']))
1315 respParameters['DataCount'] = trans2Parameters['MaxDataCount']
1316 else:
1317 respParameters['DataCount'] = len(data)
1319 respData['Trans_DataLength'] = respParameters['DataCount']
1320 respParameters['SetupCount'] = len(setup)
1321 respParameters['Setup'] = setup
1322 # TODO: Make sure we're calculating the pad right
1323 if len(parameters) > 0:
1324 # padLen = 4 - (55 + len(setup)) % 4
1325 padLen = (4 - (55 + len(setup)) % 4) % 4
1326 padBytes = b'\xFF' * padLen
1327 respData['Pad1'] = padBytes
1328 respParameters['ParameterOffset'] = 55 + len(setup) + padLen
1329 else:
1330 padLen = 0
1331 respParameters['ParameterOffset'] = 0
1332 respData['Pad1'] = b''
1334 if len(data) > 0:
1335 # pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4
1336 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4
1337 respData['Pad2'] = b'\xFF' * pad2Len
1338 respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len
1339 else:
1340 respParameters['DataOffset'] = 0
1341 respData['Pad2'] = b''
1343 respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']]
1344 respData['Trans_Data'] = data[:respParameters['DataCount']]
1345 respSMBCommand['Parameters'] = respParameters
1346 respSMBCommand['Data'] = respData
1348 data = data[respParameters['DataCount']:]
1349 remainingData -= respParameters['DataCount']
1350 dataDisplacement += respParameters['DataCount'] + 1
1352 parameters = parameters[respParameters['ParameterCount']:]
1353 remainingParameters -= respParameters['ParameterCount']
1354 commands.append(respSMBCommand)
1356 smbServer.setConnectionData(connId, connData)
1357 return commands, None, errorCode
1359 else:
1360 smbServer.log("Unsupported Transact/2 command 0x%x" % command, logging.ERROR)
1361 respParameters = b''
1362 respData = b''
1363 errorCode = STATUS_NOT_IMPLEMENTED
1365 respSMBCommand['Parameters'] = respParameters
1366 respSMBCommand['Data'] = respData
1368 smbServer.setConnectionData(connId, connData)
1369 return [respSMBCommand], None, errorCode
1371 @staticmethod
1372 def smbComLockingAndX(connId, smbServer, SMBCommand, recvPacket):
1373 connData = smbServer.getConnectionData(connId)
1375 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOCKING_ANDX)
1376 respParameters = b''
1377 respData = b''
1379 # I'm actually doing nothing.. just make MacOS happy ;)
1380 errorCode = STATUS_SUCCESS
1382 respSMBCommand['Parameters'] = respParameters
1383 respSMBCommand['Data'] = respData
1384 smbServer.setConnectionData(connId, connData)
1386 return [respSMBCommand], None, errorCode
1388 @staticmethod
1389 def smbComClose(connId, smbServer, SMBCommand, recvPacket):
1390 connData = smbServer.getConnectionData(connId)
1392 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CLOSE)
1393 respParameters = b''
1394 respData = b''
1396 comClose = smb.SMBClose_Parameters(SMBCommand['Parameters'])
1398 if comClose['FID'] in connData['OpenedFiles']:
1399 errorCode = STATUS_SUCCESS
1400 fileHandle = connData['OpenedFiles'][comClose['FID']]['FileHandle']
1401 try:
1402 if fileHandle == PIPE_FILE_DESCRIPTOR:
1403 connData['OpenedFiles'][comClose['FID']]['Socket'].close()
1404 elif fileHandle != VOID_FILE_DESCRIPTOR:
1405 os.close(fileHandle)
1406 except Exception as e:
1407 smbServer.log("comClose %s" % e, logging.ERROR)
1408 errorCode = STATUS_ACCESS_DENIED
1409 else:
1410 # Check if the file was marked for removal
1411 if connData['OpenedFiles'][comClose['FID']]['DeleteOnClose'] is True:
1412 try:
1413 os.remove(connData['OpenedFiles'][comClose['FID']]['FileName'])
1414 except Exception as e:
1415 smbServer.log("comClose %s" % e, logging.ERROR)
1416 errorCode = STATUS_ACCESS_DENIED
1417 del (connData['OpenedFiles'][comClose['FID']])
1418 else:
1419 errorCode = STATUS_INVALID_HANDLE
1421 if errorCode > 0:
1422 respParameters = b''
1423 respData = b''
1425 respSMBCommand['Parameters'] = respParameters
1426 respSMBCommand['Data'] = respData
1427 smbServer.setConnectionData(connId, connData)
1429 return [respSMBCommand], None, errorCode
1431 @staticmethod
1432 def smbComWrite(connId, smbServer, SMBCommand, recvPacket):
1433 connData = smbServer.getConnectionData(connId)
1435 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE)
1436 respParameters = smb.SMBWriteResponse_Parameters()
1437 respData = b''
1439 comWriteParameters = smb.SMBWrite_Parameters(SMBCommand['Parameters'])
1440 comWriteData = smb.SMBWrite_Data(SMBCommand['Data'])
1442 if comWriteParameters['Fid'] in connData['OpenedFiles']:
1443 fileHandle = connData['OpenedFiles'][comWriteParameters['Fid']]['FileHandle']
1444 errorCode = STATUS_SUCCESS
1445 try:
1446 if fileHandle != PIPE_FILE_DESCRIPTOR:
1447 # TODO: Handle big size files
1448 # If we're trying to write past the file end we just skip the write call (Vista does this)
1449 if os.lseek(fileHandle, 0, 2) >= comWriteParameters['Offset']:
1450 os.lseek(fileHandle, comWriteParameters['Offset'], 0)
1451 os.write(fileHandle, comWriteData['Data'])
1452 else:
1453 sock = connData['OpenedFiles'][comWriteParameters['Fid']]['Socket']
1454 sock.send(comWriteData['Data'])
1455 respParameters['Count'] = comWriteParameters['Count']
1456 except Exception as e:
1457 smbServer.log('smbComWrite: %s' % e, logging.ERROR)
1458 errorCode = STATUS_ACCESS_DENIED
1459 else:
1460 errorCode = STATUS_INVALID_HANDLE
1462 if errorCode > 0:
1463 respParameters = b''
1464 respData = b''
1466 respSMBCommand['Parameters'] = respParameters
1467 respSMBCommand['Data'] = respData
1468 smbServer.setConnectionData(connId, connData)
1470 return [respSMBCommand], None, errorCode
1472 @staticmethod
1473 def smbComFlush(connId, smbServer, SMBCommand, recvPacket):
1474 connData = smbServer.getConnectionData(connId)
1476 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_FLUSH)
1477 respParameters = b''
1478 respData = b''
1480 comFlush = smb.SMBFlush_Parameters(SMBCommand['Parameters'])
1482 if comFlush['FID'] in connData['OpenedFiles']:
1483 errorCode = STATUS_SUCCESS
1484 fileHandle = connData['OpenedFiles'][comFlush['FID']]['FileHandle']
1485 try:
1486 os.fsync(fileHandle)
1487 except Exception as e:
1488 smbServer.log("comFlush %s" % e, logging.ERROR)
1489 errorCode = STATUS_ACCESS_DENIED
1490 else:
1491 errorCode = STATUS_INVALID_HANDLE
1493 if errorCode > 0:
1494 respParameters = b''
1495 respData = b''
1497 respSMBCommand['Parameters'] = respParameters
1498 respSMBCommand['Data'] = respData
1499 smbServer.setConnectionData(connId, connData)
1501 return [respSMBCommand], None, errorCode
1503 @staticmethod
1504 def smbComCreateDirectory(connId, smbServer, SMBCommand, recvPacket):
1505 connData = smbServer.getConnectionData(connId)
1507 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY)
1508 respParameters = b''
1509 respData = b''
1511 comCreateDirectoryData = smb.SMBCreateDirectory_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
1513 # Get the Tid associated
1514 if recvPacket['Tid'] in connData['ConnectedShares']:
1515 errorCode = STATUS_SUCCESS
1516 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
1517 fileName = os.path.normpath(
1518 decodeSMBString(recvPacket['Flags2'], comCreateDirectoryData['DirectoryName']).replace('\\', '/'))
1519 if len(fileName) > 0:
1520 if fileName[0] == '/' or fileName[0] == '\\':
1521 # strip leading '/'
1522 fileName = fileName[1:]
1523 pathName = os.path.join(path, fileName)
1524 if os.path.exists(pathName):
1525 errorCode = STATUS_OBJECT_NAME_COLLISION
1527 # TODO: More checks here in the future.. Specially when we support
1528 # user access
1529 else:
1530 try:
1531 os.mkdir(pathName)
1532 except Exception as e:
1533 smbServer.log("smbComCreateDirectory: %s" % e, logging.ERROR)
1534 errorCode = STATUS_ACCESS_DENIED
1535 else:
1536 errorCode = STATUS_SMB_BAD_TID
1538 if errorCode > 0:
1539 respParameters = b''
1540 respData = b''
1542 respSMBCommand['Parameters'] = respParameters
1543 respSMBCommand['Data'] = respData
1544 smbServer.setConnectionData(connId, connData)
1546 return [respSMBCommand], None, errorCode
1548 @staticmethod
1549 def smbComRename(connId, smbServer, SMBCommand, recvPacket):
1550 connData = smbServer.getConnectionData(connId)
1552 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_RENAME)
1553 respParameters = b''
1554 respData = b''
1556 comRenameData = smb.SMBRename_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
1557 # Get the Tid associated
1558 if recvPacket['Tid'] in connData['ConnectedShares']:
1559 errorCode = STATUS_SUCCESS
1560 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
1561 oldFileName = os.path.normpath(
1562 decodeSMBString(recvPacket['Flags2'], comRenameData['OldFileName']).replace('\\', '/'))
1563 newFileName = os.path.normpath(
1564 decodeSMBString(recvPacket['Flags2'], comRenameData['NewFileName']).replace('\\', '/'))
1565 if len(oldFileName) > 0 and (oldFileName[0] == '/' or oldFileName[0] == '\\'):
1566 # strip leading '/'
1567 oldFileName = oldFileName[1:]
1568 oldPathName = os.path.join(path, oldFileName)
1569 if len(newFileName) > 0 and (newFileName[0] == '/' or newFileName[0] == '\\'):
1570 # strip leading '/'
1571 newFileName = newFileName[1:]
1572 newPathName = os.path.join(path, newFileName)
1574 if os.path.exists(oldPathName) is not True:
1575 errorCode = STATUS_NO_SUCH_FILE
1577 # TODO: More checks here in the future.. Specially when we support
1578 # user access
1579 else:
1580 try:
1581 os.rename(oldPathName, newPathName)
1582 except OSError as e:
1583 smbServer.log("smbComRename: %s" % e, logging.ERROR)
1584 errorCode = STATUS_ACCESS_DENIED
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 smbComDelete(connId, smbServer, SMBCommand, recvPacket):
1600 connData = smbServer.getConnectionData(connId)
1602 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE)
1603 respParameters = b''
1604 respData = b''
1606 comDeleteData = smb.SMBDelete_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 = os.path.normpath(
1613 decodeSMBString(recvPacket['Flags2'], comDeleteData['FileName']).replace('\\', '/'))
1614 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
1615 # strip leading '/'
1616 fileName = fileName[1:]
1617 pathName = os.path.join(path, fileName)
1618 if os.path.exists(pathName) is not True:
1619 errorCode = STATUS_NO_SUCH_FILE
1621 # TODO: More checks here in the future.. Specially when we support
1622 # user access
1623 else:
1624 try:
1625 os.remove(pathName)
1626 except OSError as e:
1627 smbServer.log("smbComDelete: %s" % e, logging.ERROR)
1628 errorCode = STATUS_ACCESS_DENIED
1629 else:
1630 errorCode = STATUS_SMB_BAD_TID
1632 if errorCode > 0:
1633 respParameters = b''
1634 respData = b''
1636 respSMBCommand['Parameters'] = respParameters
1637 respSMBCommand['Data'] = respData
1638 smbServer.setConnectionData(connId, connData)
1640 return [respSMBCommand], None, errorCode
1642 @staticmethod
1643 def smbComDeleteDirectory(connId, smbServer, SMBCommand, recvPacket):
1644 connData = smbServer.getConnectionData(connId)
1646 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY)
1647 respParameters = b''
1648 respData = b''
1650 comDeleteDirectoryData = smb.SMBDeleteDirectory_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
1652 # Get the Tid associated
1653 if recvPacket['Tid'] in connData['ConnectedShares']:
1654 errorCode = STATUS_SUCCESS
1655 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
1656 fileName = os.path.normpath(
1657 decodeSMBString(recvPacket['Flags2'], comDeleteDirectoryData['DirectoryName']).replace('\\', '/'))
1658 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
1659 # strip leading '/'
1660 fileName = fileName[1:]
1661 pathName = os.path.join(path, fileName)
1662 if os.path.exists(pathName) is not True:
1663 errorCode = STATUS_NO_SUCH_FILE
1665 # TODO: More checks here in the future.. Specially when we support
1666 # user access
1667 else:
1668 try:
1669 os.rmdir(pathName)
1670 except OSError as e:
1671 smbServer.log("smbComDeleteDirectory: %s" % e, logging.ERROR)
1672 if e.errno == errno.ENOTEMPTY:
1673 errorCode = STATUS_DIRECTORY_NOT_EMPTY
1674 else:
1675 errorCode = STATUS_ACCESS_DENIED
1676 else:
1677 errorCode = STATUS_SMB_BAD_TID
1679 if errorCode > 0:
1680 respParameters = b''
1681 respData = b''
1683 respSMBCommand['Parameters'] = respParameters
1684 respSMBCommand['Data'] = respData
1685 smbServer.setConnectionData(connId, connData)
1687 return [respSMBCommand], None, errorCode
1689 @staticmethod
1690 def smbComWriteAndX(connId, smbServer, SMBCommand, recvPacket):
1691 connData = smbServer.getConnectionData(connId)
1693 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE_ANDX)
1694 respParameters = smb.SMBWriteAndXResponse_Parameters()
1695 respData = b''
1697 if SMBCommand['WordCount'] == 0x0C:
1698 writeAndX = smb.SMBWriteAndX_Parameters_Short(SMBCommand['Parameters'])
1699 writeAndXData = smb.SMBWriteAndX_Data_Short()
1700 else:
1701 writeAndX = smb.SMBWriteAndX_Parameters(SMBCommand['Parameters'])
1702 writeAndXData = smb.SMBWriteAndX_Data()
1703 writeAndXData['DataLength'] = writeAndX['DataLength']
1704 writeAndXData['DataOffset'] = writeAndX['DataOffset']
1705 writeAndXData.fromString(SMBCommand['Data'])
1707 if writeAndX['Fid'] in connData['OpenedFiles']:
1708 fileHandle = connData['OpenedFiles'][writeAndX['Fid']]['FileHandle']
1709 errorCode = STATUS_SUCCESS
1710 try:
1711 if fileHandle != PIPE_FILE_DESCRIPTOR:
1712 offset = writeAndX['Offset']
1713 if 'HighOffset' in writeAndX.fields:
1714 offset += (writeAndX['HighOffset'] << 32)
1715 # If we're trying to write past the file end we just skip the write call (Vista does this)
1716 if os.lseek(fileHandle, 0, 2) >= offset:
1717 os.lseek(fileHandle, offset, 0)
1718 os.write(fileHandle, writeAndXData['Data'])
1719 else:
1720 sock = connData['OpenedFiles'][writeAndX['Fid']]['Socket']
1721 sock.send(writeAndXData['Data'])
1723 respParameters['Count'] = writeAndX['DataLength']
1724 respParameters['Available'] = 0xff
1725 except Exception as e:
1726 smbServer.log('smbComWriteAndx: %s' % e, logging.ERROR)
1727 errorCode = STATUS_ACCESS_DENIED
1728 else:
1729 errorCode = STATUS_INVALID_HANDLE
1731 if errorCode > 0:
1732 respParameters = b''
1733 respData = b''
1735 respSMBCommand['Parameters'] = respParameters
1736 respSMBCommand['Data'] = respData
1737 smbServer.setConnectionData(connId, connData)
1739 return [respSMBCommand], None, errorCode
1741 @staticmethod
1742 def smbComRead(connId, smbServer, SMBCommand, recvPacket):
1743 connData = smbServer.getConnectionData(connId)
1745 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ)
1746 respParameters = smb.SMBReadResponse_Parameters()
1747 respData = smb.SMBReadResponse_Data()
1749 comReadParameters = smb.SMBRead_Parameters(SMBCommand['Parameters'])
1751 if comReadParameters['Fid'] in connData['OpenedFiles']:
1752 fileHandle = connData['OpenedFiles'][comReadParameters['Fid']]['FileHandle']
1753 errorCode = STATUS_SUCCESS
1754 try:
1755 if fileHandle != PIPE_FILE_DESCRIPTOR:
1756 # TODO: Handle big size files
1757 os.lseek(fileHandle, comReadParameters['Offset'], 0)
1758 content = os.read(fileHandle, comReadParameters['Count'])
1759 else:
1760 sock = connData['OpenedFiles'][comReadParameters['Fid']]['Socket']
1761 content = sock.recv(comReadParameters['Count'])
1762 respParameters['Count'] = len(content)
1763 respData['DataLength'] = len(content)
1764 respData['Data'] = content
1765 except Exception as e:
1766 smbServer.log('smbComRead: %s ' % e, logging.ERROR)
1767 errorCode = STATUS_ACCESS_DENIED
1768 else:
1769 errorCode = STATUS_INVALID_HANDLE
1771 if errorCode > 0:
1772 respParameters = b''
1773 respData = b''
1775 respSMBCommand['Parameters'] = respParameters
1776 respSMBCommand['Data'] = respData
1777 smbServer.setConnectionData(connId, connData)
1779 return [respSMBCommand], None, errorCode
1781 @staticmethod
1782 def smbComReadAndX(connId, smbServer, SMBCommand, recvPacket):
1783 connData = smbServer.getConnectionData(connId)
1785 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ_ANDX)
1786 respParameters = smb.SMBReadAndXResponse_Parameters()
1787 respData = b''
1789 if SMBCommand['WordCount'] == 0x0A:
1790 readAndX = smb.SMBReadAndX_Parameters2(SMBCommand['Parameters'])
1791 else:
1792 readAndX = smb.SMBReadAndX_Parameters(SMBCommand['Parameters'])
1794 if readAndX['Fid'] in connData['OpenedFiles']:
1795 fileHandle = connData['OpenedFiles'][readAndX['Fid']]['FileHandle']
1796 errorCode = 0
1797 try:
1798 if fileHandle != PIPE_FILE_DESCRIPTOR:
1799 offset = readAndX['Offset']
1800 if 'HighOffset' in readAndX.fields:
1801 offset += (readAndX['HighOffset'] << 32)
1802 os.lseek(fileHandle, offset, 0)
1803 content = os.read(fileHandle, readAndX['MaxCount'])
1804 else:
1805 sock = connData['OpenedFiles'][readAndX['Fid']]['Socket']
1806 content = sock.recv(readAndX['MaxCount'])
1807 respParameters['Remaining'] = 0xffff
1808 respParameters['DataCount'] = len(content)
1809 respParameters['DataOffset'] = 59
1810 respParameters['DataCount_Hi'] = 0
1811 respData = content
1812 except Exception as e:
1813 smbServer.log('smbComReadAndX: %s ' % e, logging.ERROR)
1814 errorCode = STATUS_ACCESS_DENIED
1815 else:
1816 errorCode = STATUS_INVALID_HANDLE
1818 if errorCode > 0:
1819 respParameters = b''
1820 respData = b''
1822 respSMBCommand['Parameters'] = respParameters
1823 respSMBCommand['Data'] = respData
1824 smbServer.setConnectionData(connId, connData)
1826 return [respSMBCommand], None, errorCode
1828 @staticmethod
1829 def smbQueryInformation(connId, smbServer, SMBCommand, recvPacket):
1830 connData = smbServer.getConnectionData(connId)
1832 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION)
1833 respParameters = smb.SMBQueryInformationResponse_Parameters()
1834 respData = b''
1836 queryInformation = smb.SMBQueryInformation_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
1838 # Get the Tid associated
1839 if recvPacket['Tid'] in connData['ConnectedShares']:
1840 fileSize, lastWriteTime, fileAttributes = queryFsInformation(
1841 connData['ConnectedShares'][recvPacket['Tid']]['path'],
1842 decodeSMBString(recvPacket['Flags2'], queryInformation['FileName']), pktFlags=recvPacket['Flags2'])
1844 respParameters['FileSize'] = fileSize
1845 respParameters['LastWriteTime'] = lastWriteTime
1846 respParameters['FileAttributes'] = fileAttributes
1847 errorCode = STATUS_SUCCESS
1848 else:
1849 # STATUS_SMB_BAD_TID
1850 errorCode = STATUS_SMB_BAD_TID
1851 respParameters = b''
1852 respData = b''
1854 respSMBCommand['Parameters'] = respParameters
1855 respSMBCommand['Data'] = respData
1857 smbServer.setConnectionData(connId, connData)
1858 return [respSMBCommand], None, errorCode
1860 @staticmethod
1861 def smbQueryInformationDisk(connId, smbServer, SMBCommand, recvPacket):
1862 connData = smbServer.getConnectionData(connId)
1864 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION_DISK)
1865 respParameters = smb.SMBQueryInformationDiskResponse_Parameters()
1866 respData = b''
1868 # Get the Tid associated
1869 if recvPacket['Tid'] in connData['ConnectedShares']:
1870 totalUnits, freeUnits = queryDiskInformation(
1871 connData['ConnectedShares'][recvPacket['Tid']]['path'])
1873 respParameters['TotalUnits'] = totalUnits
1874 respParameters['BlocksPerUnit'] = 1
1875 respParameters['BlockSize'] = 1
1876 respParameters['FreeUnits'] = freeUnits
1877 errorCode = STATUS_SUCCESS
1878 else:
1879 # STATUS_SMB_BAD_TID
1880 respData = b''
1881 respParameters = b''
1882 errorCode = STATUS_SMB_BAD_TID
1884 respSMBCommand['Parameters'] = respParameters
1885 respSMBCommand['Data'] = respData
1887 smbServer.setConnectionData(connId, connData)
1888 return [respSMBCommand], None, errorCode
1890 @staticmethod
1891 def smbComEcho(connId, smbServer, SMBCommand, recvPacket):
1892 connData = smbServer.getConnectionData(connId)
1894 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO)
1895 respParameters = smb.SMBEchoResponse_Parameters()
1896 respData = smb.SMBEchoResponse_Data()
1898 echoData = smb.SMBEcho_Data(SMBCommand['Data'])
1900 respParameters['SequenceNumber'] = 1
1901 respData['Data'] = echoData['Data']
1903 respSMBCommand['Parameters'] = respParameters
1904 respSMBCommand['Data'] = respData
1906 errorCode = STATUS_SUCCESS
1907 smbServer.setConnectionData(connId, connData)
1908 return [respSMBCommand], None, errorCode
1910 @staticmethod
1911 def smbComTreeDisconnect(connId, smbServer, SMBCommand, recvPacket):
1912 connData = smbServer.getConnectionData(connId)
1914 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_DISCONNECT)
1916 # Check if the Tid matches the Tid trying to disconnect
1917 respParameters = b''
1918 respData = b''
1920 if recvPacket['Tid'] in connData['ConnectedShares']:
1921 smbServer.log("Disconnecting Share(%d:%s)" % (
1922 recvPacket['Tid'], connData['ConnectedShares'][recvPacket['Tid']]['shareName']))
1923 del (connData['ConnectedShares'][recvPacket['Tid']])
1924 errorCode = STATUS_SUCCESS
1925 else:
1926 # STATUS_SMB_BAD_TID
1927 errorCode = STATUS_SMB_BAD_TID
1929 respSMBCommand['Parameters'] = respParameters
1930 respSMBCommand['Data'] = respData
1932 smbServer.setConnectionData(connId, connData)
1933 return [respSMBCommand], None, errorCode
1935 @staticmethod
1936 def smbComLogOffAndX(connId, smbServer, SMBCommand, recvPacket):
1937 connData = smbServer.getConnectionData(connId)
1939 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOGOFF_ANDX)
1941 # Check if the Uid matches the user trying to logoff
1942 respParameters = b''
1943 respData = b''
1944 if recvPacket['Uid'] != connData['Uid']:
1945 # STATUS_SMB_BAD_UID
1946 errorCode = STATUS_SMB_BAD_UID
1947 else:
1948 errorCode = STATUS_SUCCESS
1950 respSMBCommand['Parameters'] = respParameters
1951 respSMBCommand['Data'] = respData
1952 connData['Uid'] = 0
1953 connData['Authenticated'] = False
1955 smbServer.setConnectionData(connId, connData)
1957 return [respSMBCommand], None, errorCode
1959 @staticmethod
1960 def smbComQueryInformation2(connId, smbServer, SMBCommand, recvPacket):
1961 connData = smbServer.getConnectionData(connId)
1963 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION2)
1964 respParameters = smb.SMBQueryInformation2Response_Parameters()
1965 respData = b''
1967 queryInformation2 = smb.SMBQueryInformation2_Parameters(SMBCommand['Parameters'])
1968 errorCode = 0xFF
1969 if queryInformation2['Fid'] in connData['OpenedFiles']:
1970 errorCode = STATUS_SUCCESS
1971 pathName = connData['OpenedFiles'][queryInformation2['Fid']]['FileName']
1972 try:
1973 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)
1974 respParameters['CreateDate'] = getSMBDate(ctime)
1975 respParameters['CreationTime'] = getSMBTime(ctime)
1976 respParameters['LastAccessDate'] = getSMBDate(atime)
1977 respParameters['LastAccessTime'] = getSMBTime(atime)
1978 respParameters['LastWriteDate'] = getSMBDate(mtime)
1979 respParameters['LastWriteTime'] = getSMBTime(mtime)
1980 respParameters['FileDataSize'] = size
1981 respParameters['FileAllocationSize'] = size
1982 attribs = 0
1983 if os.path.isdir(pathName):
1984 attribs = smb.SMB_FILE_ATTRIBUTE_DIRECTORY
1985 if os.path.isfile(pathName):
1986 attribs = smb.SMB_FILE_ATTRIBUTE_NORMAL
1987 respParameters['FileAttributes'] = attribs
1988 except Exception as e:
1989 smbServer.log('smbComQueryInformation2 %s' % e, logging.ERROR)
1990 errorCode = STATUS_ACCESS_DENIED
1992 if errorCode > 0:
1993 respParameters = b''
1994 respData = b''
1996 respSMBCommand['Parameters'] = respParameters
1997 respSMBCommand['Data'] = respData
1998 smbServer.setConnectionData(connId, connData)
2000 return [respSMBCommand], None, errorCode
2002 @staticmethod
2003 def smbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket):
2004 # TODO: Fully implement this
2005 connData = smbServer.getConnectionData(connId)
2007 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX)
2008 respParameters = smb.SMBNtCreateAndXResponse_Parameters()
2009 respData = b''
2011 ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters'])
2012 ntCreateAndXData = smb.SMBNtCreateAndX_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
2014 # if ntCreateAndXParameters['CreateFlags'] & 0x10: # NT_CREATE_REQUEST_EXTENDED_RESPONSE
2015 # respParameters = smb.SMBNtCreateAndXExtendedResponse_Parameters()
2016 # respParameters['VolumeGUID'] = '\x00'
2018 # Get the Tid associated
2019 if recvPacket['Tid'] in connData['ConnectedShares']:
2020 # If we have a rootFid, the path is relative to that fid
2021 errorCode = STATUS_SUCCESS
2022 if ntCreateAndXParameters['RootFid'] > 0:
2023 path = connData['OpenedFiles'][ntCreateAndXParameters['RootFid']]['FileName']
2024 LOG.debug("RootFid present %s!" % path)
2025 else:
2026 if 'path' in connData['ConnectedShares'][recvPacket['Tid']]:
2027 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
2028 else:
2029 path = 'NONE'
2030 errorCode = STATUS_ACCESS_DENIED
2032 deleteOnClose = False
2034 fileName = os.path.normpath(
2035 decodeSMBString(recvPacket['Flags2'], ntCreateAndXData['FileName']).replace('\\', '/'))
2036 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
2037 # strip leading '/'
2038 fileName = fileName[1:]
2040 if not isInFileJail(path, fileName):
2041 LOG.error("Path not in current working directory")
2042 respSMBCommand['Parameters'] = b''
2043 respSMBCommand['Data'] = b''
2044 return [respSMBCommand], None, STATUS_OBJECT_PATH_SYNTAX_BAD
2046 pathName = os.path.join(path, fileName)
2047 createDisposition = ntCreateAndXParameters['Disposition']
2048 mode = 0
2050 if createDisposition == smb.FILE_SUPERSEDE:
2051 mode |= os.O_TRUNC | os.O_CREAT
2052 elif createDisposition & smb.FILE_OVERWRITE_IF == smb.FILE_OVERWRITE_IF:
2053 mode |= os.O_TRUNC | os.O_CREAT
2054 elif createDisposition & smb.FILE_OVERWRITE == smb.FILE_OVERWRITE:
2055 if os.path.exists(pathName) is True:
2056 mode |= os.O_TRUNC
2057 else:
2058 errorCode = STATUS_NO_SUCH_FILE
2059 elif createDisposition & smb.FILE_OPEN_IF == smb.FILE_OPEN_IF:
2060 if os.path.exists(pathName) is True:
2061 mode |= os.O_TRUNC
2062 else:
2063 mode |= os.O_TRUNC | os.O_CREAT
2064 elif createDisposition & smb.FILE_CREATE == smb.FILE_CREATE:
2065 if os.path.exists(pathName) is True:
2066 errorCode = STATUS_OBJECT_NAME_COLLISION
2067 else:
2068 mode |= os.O_CREAT
2069 elif createDisposition & smb.FILE_OPEN == smb.FILE_OPEN:
2070 if os.path.exists(pathName) is not True and (
2071 str(pathName) in smbServer.getRegisteredNamedPipes()) is not True:
2072 errorCode = STATUS_NO_SUCH_FILE
2074 if errorCode == STATUS_SUCCESS:
2075 desiredAccess = ntCreateAndXParameters['AccessMask']
2076 if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ):
2077 mode |= os.O_RDONLY
2078 if (desiredAccess & smb.FILE_WRITE_DATA) or (desiredAccess & smb.GENERIC_WRITE):
2079 if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ):
2080 mode |= os.O_RDWR # | os.O_APPEND
2081 else:
2082 mode |= os.O_WRONLY # | os.O_APPEND
2083 if desiredAccess & smb.GENERIC_ALL:
2084 mode |= os.O_RDWR # | os.O_APPEND
2086 createOptions = ntCreateAndXParameters['CreateOptions']
2087 if mode & os.O_CREAT == os.O_CREAT:
2088 if createOptions & smb.FILE_DIRECTORY_FILE == smb.FILE_DIRECTORY_FILE:
2089 try:
2090 # Let's create the directory
2091 os.mkdir(pathName)
2092 mode = os.O_RDONLY
2093 except Exception as e:
2094 smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName, mode, e), logging.ERROR)
2095 errorCode = STATUS_ACCESS_DENIED
2096 if createOptions & smb.FILE_NON_DIRECTORY_FILE == smb.FILE_NON_DIRECTORY_FILE:
2097 # If the file being opened is a directory, the server MUST fail the request with
2098 # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server
2099 # response.
2100 if os.path.isdir(pathName) is True:
2101 errorCode = STATUS_FILE_IS_A_DIRECTORY
2103 if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE:
2104 deleteOnClose = True
2106 if errorCode == STATUS_SUCCESS:
2107 try:
2108 if os.path.isdir(pathName) and sys.platform == 'win32':
2109 fid = VOID_FILE_DESCRIPTOR
2110 else:
2111 if sys.platform == 'win32':
2112 mode |= os.O_BINARY
2113 if str(pathName) in smbServer.getRegisteredNamedPipes():
2114 fid = PIPE_FILE_DESCRIPTOR
2115 sock = socket.socket()
2116 sock.connect(smbServer.getRegisteredNamedPipes()[str(pathName)])
2117 else:
2118 fid = os.open(pathName, mode)
2119 except Exception as e:
2120 smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName, mode, e), logging.ERROR)
2121 # print e
2122 fid = 0
2123 errorCode = STATUS_ACCESS_DENIED
2124 else:
2125 errorCode = STATUS_SMB_BAD_TID
2127 if errorCode == STATUS_SUCCESS:
2128 # Simple way to generate a fid
2129 if len(connData['OpenedFiles']) == 0:
2130 fakefid = 1
2131 else:
2132 fakefid = list(connData['OpenedFiles'].keys())[-1] + 1
2133 respParameters['Fid'] = fakefid
2134 respParameters['CreateAction'] = createDisposition
2135 if fid == PIPE_FILE_DESCRIPTOR:
2136 respParameters['FileAttributes'] = 0x80
2137 respParameters['IsDirectory'] = 0
2138 respParameters['CreateTime'] = 0
2139 respParameters['LastAccessTime'] = 0
2140 respParameters['LastWriteTime'] = 0
2141 respParameters['LastChangeTime'] = 0
2142 respParameters['AllocationSize'] = 4096
2143 respParameters['EndOfFile'] = 0
2144 respParameters['FileType'] = 2
2145 respParameters['IPCState'] = 0x5ff
2146 else:
2147 if os.path.isdir(pathName):
2148 respParameters['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY
2149 respParameters['IsDirectory'] = 1
2150 else:
2151 respParameters['IsDirectory'] = 0
2152 respParameters['FileAttributes'] = ntCreateAndXParameters['FileAttributes']
2153 # Let's get this file's information
2154 respInfo, errorCode = queryPathInformation('', pathName, level=smb.SMB_QUERY_FILE_ALL_INFO)
2155 if errorCode == STATUS_SUCCESS:
2156 respParameters['CreateTime'] = respInfo['CreationTime']
2157 respParameters['LastAccessTime'] = respInfo['LastAccessTime']
2158 respParameters['LastWriteTime'] = respInfo['LastWriteTime']
2159 respParameters['LastChangeTime'] = respInfo['LastChangeTime']
2160 respParameters['FileAttributes'] = respInfo['ExtFileAttributes']
2161 respParameters['AllocationSize'] = respInfo['AllocationSize']
2162 respParameters['EndOfFile'] = respInfo['EndOfFile']
2163 else:
2164 respParameters = b''
2165 respData = b''
2167 if errorCode == STATUS_SUCCESS:
2168 # Let's store the fid for the connection
2169 # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode))
2170 connData['OpenedFiles'][fakefid] = {}
2171 connData['OpenedFiles'][fakefid]['FileHandle'] = fid
2172 connData['OpenedFiles'][fakefid]['FileName'] = pathName
2173 connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose
2174 if fid == PIPE_FILE_DESCRIPTOR:
2175 connData['OpenedFiles'][fakefid]['Socket'] = sock
2176 else:
2177 respParameters = b''
2178 respData = b''
2180 respSMBCommand['Parameters'] = respParameters
2181 respSMBCommand['Data'] = respData
2182 smbServer.setConnectionData(connId, connData)
2184 return [respSMBCommand], None, errorCode
2186 @staticmethod
2187 def smbComOpenAndX(connId, smbServer, SMBCommand, recvPacket):
2188 connData = smbServer.getConnectionData(connId)
2190 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_OPEN_ANDX)
2191 respParameters = smb.SMBOpenAndXResponse_Parameters()
2192 respData = b''
2194 openAndXParameters = smb.SMBOpenAndX_Parameters(SMBCommand['Parameters'])
2195 openAndXData = smb.SMBOpenAndX_Data(flags=recvPacket['Flags2'], data=SMBCommand['Data'])
2197 # Get the Tid associated
2198 if recvPacket['Tid'] in connData['ConnectedShares']:
2199 path = connData['ConnectedShares'][recvPacket['Tid']]['path']
2200 openedFile, mode, pathName, errorCode = openFile(path,
2201 decodeSMBString(recvPacket['Flags2'],
2202 openAndXData['FileName']),
2203 openAndXParameters['DesiredAccess'],
2204 openAndXParameters['FileAttributes'],
2205 openAndXParameters['OpenMode'])
2206 else:
2207 errorCode = STATUS_SMB_BAD_TID
2209 if errorCode == STATUS_SUCCESS:
2210 # Simple way to generate a fid
2211 fid = len(connData['OpenedFiles']) + 1
2212 if len(connData['OpenedFiles']) == 0:
2213 fid = 1
2214 else:
2215 fid = list(connData['OpenedFiles'].keys())[-1] + 1
2216 respParameters['Fid'] = fid
2217 if mode & os.O_CREAT:
2218 # File did not exist and was created
2219 respParameters['Action'] = 0x2
2220 elif mode & os.O_RDONLY:
2221 # File existed and was opened
2222 respParameters['Action'] = 0x1
2223 elif mode & os.O_APPEND:
2224 # File existed and was opened
2225 respParameters['Action'] = 0x1
2226 else:
2227 # File existed and was truncated
2228 respParameters['Action'] = 0x3
2230 # Let's store the fid for the connection
2231 # smbServer.log('Opening file %s' % pathName)
2232 connData['OpenedFiles'][fid] = {}
2233 connData['OpenedFiles'][fid]['FileHandle'] = openedFile
2234 connData['OpenedFiles'][fid]['FileName'] = pathName
2235 connData['OpenedFiles'][fid]['DeleteOnClose'] = False
2236 else:
2237 respParameters = b''
2238 respData = b''
2240 respSMBCommand['Parameters'] = respParameters
2241 respSMBCommand['Data'] = respData
2242 smbServer.setConnectionData(connId, connData)
2244 return [respSMBCommand], None, errorCode
2246 @staticmethod
2247 def smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket):
2248 connData = smbServer.getConnectionData(connId)
2250 resp = smb.NewSMBPacket()
2251 resp['Flags1'] = smb.SMB.FLAGS1_REPLY
2252 resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \
2253 recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE
2255 resp['Tid'] = recvPacket['Tid']
2256 resp['Mid'] = recvPacket['Mid']
2257 resp['Pid'] = connData['Pid']
2259 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX)
2260 respParameters = smb.SMBTreeConnectAndXResponse_Parameters()
2261 respData = smb.SMBTreeConnectAndXResponse_Data()
2263 treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters'])
2265 if treeConnectAndXParameters['Flags'] & 0x8:
2266 respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters()
2268 treeConnectAndXData = smb.SMBTreeConnectAndX_Data(flags=recvPacket['Flags2'])
2269 treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength']
2270 treeConnectAndXData.fromString(SMBCommand['Data'])
2272 errorCode = STATUS_SUCCESS
2274 ## Process here the request, does the share exist?
2275 UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])
2277 # Is this a UNC?
2278 if ntpath.ismount(UNCOrShare):
2279 path = UNCOrShare.split('\\')[3]
2280 else:
2281 path = ntpath.basename(UNCOrShare)
2283 share = searchShare(connId, path, smbServer)
2284 if share is not None:
2285 # Simple way to generate a Tid
2286 if len(connData['ConnectedShares']) == 0:
2287 tid = 1
2288 else:
2289 tid = list(connData['ConnectedShares'].keys())[-1] + 1
2290 connData['ConnectedShares'][tid] = share
2291 connData['ConnectedShares'][tid]['shareName'] = path
2292 resp['Tid'] = tid
2293 # smbServer.log("Connecting Share(%d:%s)" % (tid,path))
2294 else:
2295 smbServer.log("TreeConnectAndX not found %s" % path, logging.ERROR)
2296 errorCode = STATUS_OBJECT_PATH_NOT_FOUND
2297 resp['ErrorCode'] = errorCode >> 16
2298 resp['ErrorClass'] = errorCode & 0xff
2299 ##
2300 respParameters['OptionalSupport'] = smb.SMB.SMB_SUPPORT_SEARCH_BITS
2302 if path == 'IPC$':
2303 respData['Service'] = 'IPC'
2304 else:
2305 respData['Service'] = path
2306 respData['PadLen'] = 0
2307 respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS').decode()
2309 respSMBCommand['Parameters'] = respParameters
2310 respSMBCommand['Data'] = respData
2312 resp['Uid'] = connData['Uid']
2313 resp.addCommand(respSMBCommand)
2315 # Sign the packet if needed
2316 if connData['SignatureEnabled']:
2317 smbServer.signSMBv1(connData, resp, connData['SigningSessionKey'], connData['SigningChallengeResponse'])
2318 smbServer.setConnectionData(connId, connData)
2320 return None, [resp], errorCode
2322 @staticmethod
2323 def smbComSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket):
2324 connData = smbServer.getConnectionData(connId, checkStatus=False)
2326 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)
2328 # From [MS-SMB]
2329 # When extended security is being used (see section 3.2.4.2.4), the
2330 # request MUST take the following form
2331 # [..]
2332 # WordCount (1 byte): The value of this field MUST be 0x0C.
2333 if SMBCommand['WordCount'] == 12:
2334 # Extended security. Here we deal with all SPNEGO stuff
2335 respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters()
2336 respData = smb.SMBSessionSetupAndX_Extended_Response_Data(flags=recvPacket['Flags2'])
2337 sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters'])
2338 sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data()
2339 sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength']
2340 sessionSetupData.fromString(SMBCommand['Data'])
2341 connData['Capabilities'] = sessionSetupParameters['Capabilities']
2343 rawNTLM = False
2344 if struct.unpack('B', sessionSetupData['SecurityBlob'][0:1])[0] == ASN1_AID:
2345 # NEGOTIATE packet
2346 blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob'])
2347 token = blob['MechToken']
2348 if len(blob['MechTypes'][0]) > 0:
2349 # Is this GSSAPI NTLM or something else we don't support?
2350 mechType = blob['MechTypes'][0]
2351 if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']:
2352 # Nope, do we know it?
2353 if mechType in MechTypes:
2354 mechStr = MechTypes[mechType]
2355 else:
2356 mechStr = hexlify(mechType)
2357 smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL)
2358 # We don't know the token, we answer back again saying
2359 # we just support NTLM.
2360 # ToDo: Build this into a SPNEGO_NegTokenResp()
2361 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'
2362 respParameters['SecurityBlobLength'] = len(respToken)
2363 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']
2364 respData['SecurityBlob'] = respToken
2365 respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
2366 respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
2367 respSMBCommand['Parameters'] = respParameters
2368 respSMBCommand['Data'] = respData
2369 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED
2371 elif struct.unpack('B', sessionSetupData['SecurityBlob'][0:1])[0] == ASN1_SUPPORTED_MECH:
2372 # AUTH packet
2373 blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob'])
2374 token = blob['ResponseToken']
2375 else:
2376 # No GSSAPI stuff, raw NTLMSSP
2377 rawNTLM = True
2378 token = sessionSetupData['SecurityBlob']
2380 # Here we only handle NTLMSSP, depending on what stage of the
2381 # authentication we are, we act on it
2382 messageType = struct.unpack('<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0]
2384 if messageType == 0x01:
2385 # NEGOTIATE_MESSAGE
2386 negotiateMessage = ntlm.NTLMAuthNegotiate()
2387 negotiateMessage.fromString(token)
2388 # Let's store it in the connection data
2389 connData['NEGOTIATE_MESSAGE'] = negotiateMessage
2390 # Let's build the answer flags
2391 # TODO: Parse all the flags. With this we're leaving some clients out
2393 ansFlags = 0
2395 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56:
2396 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56
2397 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128:
2398 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128
2399 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
2400 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
2401 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
2402 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
2403 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
2404 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE
2405 if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM:
2406 ansFlags |= ntlm.NTLM_NEGOTIATE_OEM
2408 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET
2410 # Generate the AV_PAIRS
2411 av_pairs = ntlm.AV_PAIRS()
2412 # TODO: Put the proper data from SMBSERVER config
2413 av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[
2414 ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le')
2415 av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[
2416 ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le')
2417 av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (
2418 116444736000000000 + calendar.timegm(time.gmtime()) * 10000000))
2420 challengeMessage = ntlm.NTLMAuthChallenge()
2421 challengeMessage['flags'] = ansFlags
2422 challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le'))
2423 challengeMessage['domain_max_len'] = challengeMessage['domain_len']
2424 challengeMessage['domain_offset'] = 40 + 16
2425 challengeMessage['challenge'] = smbServer.getSMBChallenge()
2426 challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le')
2427 challengeMessage['TargetInfoFields_len'] = len(av_pairs)
2428 challengeMessage['TargetInfoFields_max_len'] = len(av_pairs)
2429 challengeMessage['TargetInfoFields'] = av_pairs
2430 challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name'])
2431 challengeMessage['Version'] = b'\xff' * 8
2432 challengeMessage['VersionLen'] = 8
2434 if rawNTLM is False:
2435 respToken = SPNEGO_NegTokenResp()
2436 # accept-incomplete. We want more data
2437 respToken['NegState'] = b'\x01'
2438 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
2440 respToken['ResponseToken'] = challengeMessage.getData()
2441 else:
2442 respToken = challengeMessage
2444 # Setting the packet to STATUS_MORE_PROCESSING
2445 errorCode = STATUS_MORE_PROCESSING_REQUIRED
2446 # Let's set up an UID for this connection and store it
2447 # in the connection's data
2448 # Picking a fixed value
2449 # TODO: Manage more UIDs for the same session
2450 connData['Uid'] = 10
2451 # Let's store it in the connection data
2452 connData['CHALLENGE_MESSAGE'] = challengeMessage
2454 elif messageType == 0x02:
2455 # CHALLENGE_MESSAGE
2456 raise Exception('Challenge Message raise, not implemented!')
2457 elif messageType == 0x03:
2458 # AUTHENTICATE_MESSAGE, here we deal with authentication
2459 authenticateMessage = ntlm.NTLMAuthChallengeResponse()
2460 authenticateMessage.fromString(token)
2461 smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (
2462 authenticateMessage['domain_name'].decode('utf-16le'),
2463 authenticateMessage['user_name'].decode('utf-16le'),
2464 authenticateMessage['host_name'].decode('utf-16le')))
2465 # Do we have credentials to check?
2466 if len(smbServer.getCredentials()) > 0:
2467 identity = authenticateMessage['user_name'].decode('utf-16le').lower()
2468 # Do we have this user's credentials?
2469 if identity in smbServer.getCredentials():
2470 # Process data:
2471 # Let's parse some data and keep it to ourselves in case it is asked
2472 uid, lmhash, nthash = smbServer.getCredentials()[identity]
2474 errorCode, sessionKey = computeNTLMv2(identity, lmhash, nthash, smbServer.getSMBChallenge(),
2475 authenticateMessage, connData['CHALLENGE_MESSAGE'],
2476 connData['NEGOTIATE_MESSAGE'])
2478 if sessionKey is not None:
2479 connData['SignatureEnabled'] = False
2480 connData['SigningSessionKey'] = sessionKey
2481 connData['SignSequenceNumber'] = 1
2482 else:
2483 errorCode = STATUS_LOGON_FAILURE
2484 else:
2485 # No credentials provided, let's grant access
2486 errorCode = STATUS_SUCCESS
2488 if errorCode == STATUS_SUCCESS:
2489 connData['Authenticated'] = True
2490 respToken = SPNEGO_NegTokenResp()
2491 # accept-completed
2492 respToken['NegState'] = b'\x00'
2494 smbServer.log(
2495 'User %s\\%s authenticated successfully' % (authenticateMessage['host_name'].decode('utf-16le'),
2496 authenticateMessage['user_name'].decode(
2497 'utf-16le')))
2498 # Let's store it in the connection data
2499 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
2500 try:
2501 jtr_dump_path = smbServer.getJTRdumpPath()
2502 ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],
2503 authenticateMessage['user_name'],
2504 authenticateMessage['domain_name'],
2505 authenticateMessage['lanman'], authenticateMessage['ntlm'])
2506 smbServer.log(ntlm_hash_data['hash_string'])
2507 if jtr_dump_path != '':
2508 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
2509 jtr_dump_path)
2510 except:
2511 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
2512 else:
2513 respToken = SPNEGO_NegTokenResp()
2514 respToken['NegState'] = b'\x02'
2515 smbServer.log("Could not authenticate user!")
2516 else:
2517 raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
2519 respParameters['SecurityBlobLength'] = len(respToken)
2520 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']
2521 respData['SecurityBlob'] = respToken.getData()
2523 else:
2524 # Process Standard Security
2525 respParameters = smb.SMBSessionSetupAndXResponse_Parameters()
2526 respData = smb.SMBSessionSetupAndXResponse_Data()
2527 sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters'])
2528 sessionSetupData = smb.SMBSessionSetupAndX_Data()
2529 sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']
2530 sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']
2531 sessionSetupData.fromString(SMBCommand['Data'])
2532 connData['Capabilities'] = sessionSetupParameters['Capabilities']
2533 # Do the verification here, for just now we grant access
2534 # TODO: Manage more UIDs for the same session
2535 errorCode = STATUS_SUCCESS
2536 connData['Uid'] = 10
2537 connData['Authenticated'] = True
2538 respParameters['Action'] = 0
2539 smbServer.log('User %s\\%s authenticated successfully (basic)' % (
2540 sessionSetupData['PrimaryDomain'], sessionSetupData['Account']))
2541 try:
2542 jtr_dump_path = smbServer.getJTRdumpPath()
2543 ntlm_hash_data = outputToJohnFormat(b'', b(sessionSetupData['Account']),
2544 b(sessionSetupData['PrimaryDomain']), sessionSetupData['AnsiPwd'],
2545 sessionSetupData['UnicodePwd'])
2546 smbServer.log(ntlm_hash_data['hash_string'])
2547 if jtr_dump_path != '':
2548 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path)
2549 except:
2550 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
2552 respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
2553 respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
2554 respSMBCommand['Parameters'] = respParameters
2555 respSMBCommand['Data'] = respData
2557 # From now on, the client can ask for other commands
2558 connData['Authenticated'] = True
2559 # For now, just switching to nobody
2560 # os.setregid(65534,65534)
2561 # os.setreuid(65534,65534)
2562 smbServer.setConnectionData(connId, connData)
2564 return [respSMBCommand], None, errorCode
2566 @staticmethod
2567 def smbComNegotiate(connId, smbServer, SMBCommand, recvPacket):
2568 connData = smbServer.getConnectionData(connId, checkStatus=False)
2569 connData['Pid'] = recvPacket['Pid']
2571 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
2572 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NEGOTIATE)
2574 resp = smb.NewSMBPacket()
2575 resp['Flags1'] = smb.SMB.FLAGS1_REPLY
2576 resp['Pid'] = connData['Pid']
2577 resp['Tid'] = recvPacket['Tid']
2578 resp['Mid'] = recvPacket['Mid']
2580 # TODO: We support more dialects, and parse them accordingly
2581 dialects = SMBCommand['Data'].split(b'\x02')
2582 try:
2583 index = dialects.index(b'NT LM 0.12\x00') - 1
2584 # Let's fill the data for NTLM
2585 if recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY:
2586 resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE
2587 # resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS
2588 _dialects_data = smb.SMBExtended_Security_Data()
2589 _dialects_data['ServerGUID'] = b'A' * 16
2590 blob = SPNEGO_NegTokenInit()
2591 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
2592 _dialects_data['SecurityBlob'] = blob.getData()
2594 _dialects_parameters = smb.SMBExtended_Security_Parameters()
2595 _dialects_parameters[
2596 'Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY | smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS | smb.SMB.CAP_UNICODE
2597 _dialects_parameters['ChallengeLength'] = 0
2599 else:
2600 resp['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE
2601 _dialects_parameters = smb.SMBNTLMDialect_Parameters()
2602 _dialects_data = smb.SMBNTLMDialect_Data()
2603 _dialects_data['Payload'] = ''
2604 if 'EncryptionKey' in connData:
2605 _dialects_data['Challenge'] = connData['EncryptionKey']
2606 _dialects_parameters['ChallengeLength'] = len(_dialects_data.getData())
2607 else:
2608 # TODO: Handle random challenges, now one that can be used with rainbow tables
2609 _dialects_data['Challenge'] = b'\x11\x22\x33\x44\x55\x66\x77\x88'
2610 _dialects_parameters['ChallengeLength'] = 8
2611 _dialects_parameters['Capabilities'] = smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS
2613 # Let's see if we need to support RPC_REMOTE_APIS
2614 config = smbServer.getServerConfig()
2615 if config.has_option('global', 'rpc_apis'):
2616 if config.getboolean('global', 'rpc_apis') is True:
2617 _dialects_parameters['Capabilities'] |= smb.SMB.CAP_RPC_REMOTE_APIS
2619 _dialects_parameters['DialectIndex'] = index
2620 # _dialects_parameters['SecurityMode'] = smb.SMB.SECURITY_AUTH_ENCRYPTED | smb.SMB.SECURITY_SHARE_USER | smb.SMB.SECURITY_SIGNATURES_REQUIRED
2621 _dialects_parameters['SecurityMode'] = smb.SMB.SECURITY_AUTH_ENCRYPTED | smb.SMB.SECURITY_SHARE_USER
2622 _dialects_parameters['MaxMpxCount'] = 1
2623 _dialects_parameters['MaxNumberVcs'] = 1
2624 _dialects_parameters['MaxBufferSize'] = 64000
2625 _dialects_parameters['MaxRawSize'] = 65536
2626 _dialects_parameters['SessionKey'] = 0
2627 _dialects_parameters['LowDateTime'] = 0
2628 _dialects_parameters['HighDateTime'] = 0
2629 _dialects_parameters['ServerTimeZone'] = 0
2631 respSMBCommand['Data'] = _dialects_data
2632 respSMBCommand['Parameters'] = _dialects_parameters
2633 connData['_dialects_data'] = _dialects_data
2634 connData['_dialects_parameters'] = _dialects_parameters
2636 except Exception as e:
2637 # No NTLM throw an error
2638 smbServer.log('smbComNegotiate: %s' % e, logging.ERROR)
2639 respSMBCommand['Data'] = struct.pack('<H', 0xffff)
2641 smbServer.setConnectionData(connId, connData)
2643 resp.addCommand(respSMBCommand)
2645 return None, [resp], STATUS_SUCCESS
2647 @staticmethod
2648 def default(connId, smbServer, SMBCommand, recvPacket):
2649 # By default we return an SMB Packet with error not implemented
2650 smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'], logging.DEBUG)
2651 packet = smb.NewSMBPacket()
2652 packet['Flags1'] = smb.SMB.FLAGS1_REPLY
2653 packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS
2654 packet['Command'] = recvPacket['Command']
2655 packet['Pid'] = recvPacket['Pid']
2656 packet['Tid'] = recvPacket['Tid']
2657 packet['Mid'] = recvPacket['Mid']
2658 packet['Uid'] = recvPacket['Uid']
2659 packet['Data'] = b'\x00\x00\x00'
2660 errorCode = STATUS_NOT_IMPLEMENTED
2661 packet['ErrorCode'] = errorCode >> 16
2662 packet['ErrorClass'] = errorCode & 0xff
2664 return None, [packet], errorCode
2667class SMB2Commands:
2668 @staticmethod
2669 def smb2Negotiate(connId, smbServer, recvPacket, isSMB1=False):
2670 connData = smbServer.getConnectionData(connId, checkStatus=False)
2672 respPacket = smb2.SMB2Packet()
2673 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR
2674 respPacket['Status'] = STATUS_SUCCESS
2675 respPacket['CreditRequestResponse'] = 1
2676 respPacket['Command'] = smb2.SMB2_NEGOTIATE
2677 respPacket['SessionID'] = 0
2678 if isSMB1 is False:
2679 respPacket['MessageID'] = recvPacket['MessageID']
2680 else:
2681 respPacket['MessageID'] = 0
2682 respPacket['TreeID'] = 0
2684 respSMBCommand = smb2.SMB2Negotiate_Response()
2686 respSMBCommand['SecurityMode'] = 1
2687 if isSMB1 is True:
2688 # Let's first parse the packet to see if the client supports SMB2
2689 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
2691 dialects = SMBCommand['Data'].split(b'\x02')
2692 if b'SMB 2.002\x00' in dialects or b'SMB 2.???\x00' in dialects:
2693 respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002
2694 else:
2695 # Client does not support SMB2 fallbacking
2696 raise Exception('SMB2 not supported, fallbacking')
2697 else:
2698 respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002
2699 respSMBCommand['ServerGuid'] = b'A' * 16
2700 respSMBCommand['Capabilities'] = 0
2701 respSMBCommand['MaxTransactSize'] = 65536
2702 respSMBCommand['MaxReadSize'] = 65536
2703 respSMBCommand['MaxWriteSize'] = 65536
2704 respSMBCommand['SystemTime'] = getFileTime(calendar.timegm(time.gmtime()))
2705 respSMBCommand['ServerStartTime'] = getFileTime(calendar.timegm(time.gmtime()))
2706 respSMBCommand['SecurityBufferOffset'] = 0x80
2708 blob = SPNEGO_NegTokenInit()
2709 blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
2711 respSMBCommand['Buffer'] = blob.getData()
2712 respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer'])
2714 respPacket['Data'] = respSMBCommand
2716 smbServer.setConnectionData(connId, connData)
2718 return None, [respPacket], STATUS_SUCCESS
2720 @staticmethod
2721 def smb2SessionSetup(connId, smbServer, recvPacket):
2722 connData = smbServer.getConnectionData(connId, checkStatus=False)
2724 respSMBCommand = smb2.SMB2SessionSetup_Response()
2726 sessionSetupData = smb2.SMB2SessionSetup(recvPacket['Data'])
2728 connData['Capabilities'] = sessionSetupData['Capabilities']
2730 securityBlob = sessionSetupData['Buffer']
2732 rawNTLM = False
2733 if struct.unpack('B', securityBlob[0:1])[0] == ASN1_AID:
2734 # NEGOTIATE packet
2735 blob = SPNEGO_NegTokenInit(securityBlob)
2736 token = blob['MechToken']
2737 if len(blob['MechTypes'][0]) > 0:
2738 # Is this GSSAPI NTLM or something else we don't support?
2739 mechType = blob['MechTypes'][0]
2740 if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']:
2741 # Nope, do we know it?
2742 if mechType in MechTypes:
2743 mechStr = MechTypes[mechType]
2744 else:
2745 mechStr = hexlify(mechType)
2746 smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL)
2747 # We don't know the token, we answer back again saying
2748 # we just support NTLM.
2749 # ToDo: Build this into a SPNEGO_NegTokenResp()
2750 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'
2751 respSMBCommand['SecurityBufferOffset'] = 0x48
2752 respSMBCommand['SecurityBufferLength'] = len(respToken)
2753 respSMBCommand['Buffer'] = respToken
2755 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED
2756 elif struct.unpack('B', securityBlob[0:1])[0] == ASN1_SUPPORTED_MECH:
2757 # AUTH packet
2758 blob = SPNEGO_NegTokenResp(securityBlob)
2759 token = blob['ResponseToken']
2760 else:
2761 # No GSSAPI stuff, raw NTLMSSP
2762 rawNTLM = True
2763 token = securityBlob
2765 # Here we only handle NTLMSSP, depending on what stage of the
2766 # authentication we are, we act on it
2767 messageType = struct.unpack('<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0]
2769 if messageType == 0x01:
2770 # NEGOTIATE_MESSAGE
2771 negotiateMessage = ntlm.NTLMAuthNegotiate()
2772 negotiateMessage.fromString(token)
2773 # Let's store it in the connection data
2774 connData['NEGOTIATE_MESSAGE'] = negotiateMessage
2775 # Let's build the answer flags
2776 # TODO: Parse all the flags. With this we're leaving some clients out
2778 ansFlags = 0
2780 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56:
2781 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56
2782 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128:
2783 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128
2784 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
2785 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
2786 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
2787 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
2788 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
2789 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE
2790 if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM:
2791 ansFlags |= ntlm.NTLM_NEGOTIATE_OEM
2793 ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET
2795 # Generate the AV_PAIRS
2796 av_pairs = ntlm.AV_PAIRS()
2797 # TODO: Put the proper data from SMBSERVER config
2798 av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[
2799 ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le')
2800 av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[
2801 ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le')
2802 av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (
2803 116444736000000000 + calendar.timegm(time.gmtime()) * 10000000))
2805 challengeMessage = ntlm.NTLMAuthChallenge()
2806 challengeMessage['flags'] = ansFlags
2807 challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le'))
2808 challengeMessage['domain_max_len'] = challengeMessage['domain_len']
2809 challengeMessage['domain_offset'] = 40 + 16
2810 challengeMessage['challenge'] = smbServer.getSMBChallenge()
2811 challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le')
2812 challengeMessage['TargetInfoFields_len'] = len(av_pairs)
2813 challengeMessage['TargetInfoFields_max_len'] = len(av_pairs)
2814 challengeMessage['TargetInfoFields'] = av_pairs
2815 challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name'])
2816 challengeMessage['Version'] = b'\xff' * 8
2817 challengeMessage['VersionLen'] = 8
2819 if rawNTLM is False:
2820 respToken = SPNEGO_NegTokenResp()
2821 # accept-incomplete. We want more data
2822 respToken['NegState'] = b'\x01'
2823 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
2825 respToken['ResponseToken'] = challengeMessage.getData()
2826 else:
2827 respToken = challengeMessage
2829 # Setting the packet to STATUS_MORE_PROCESSING
2830 errorCode = STATUS_MORE_PROCESSING_REQUIRED
2831 # Let's set up an UID for this connection and store it
2832 # in the connection's data
2833 # Picking a fixed value
2834 # TODO: Manage more UIDs for the same session
2835 connData['Uid'] = random.randint(1, 0xffffffff)
2836 # Let's store it in the connection data
2837 connData['CHALLENGE_MESSAGE'] = challengeMessage
2839 elif messageType == 0x02:
2840 # CHALLENGE_MESSAGE
2841 raise Exception('Challenge Message raise, not implemented!')
2842 elif messageType == 0x03:
2843 # AUTHENTICATE_MESSAGE, here we deal with authentication
2844 authenticateMessage = ntlm.NTLMAuthChallengeResponse()
2845 authenticateMessage.fromString(token)
2846 smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (
2847 authenticateMessage['domain_name'].decode('utf-16le'),
2848 authenticateMessage['user_name'].decode('utf-16le'),
2849 authenticateMessage['host_name'].decode('utf-16le')))
2850 # TODO: Check the credentials! Now granting permissions
2851 # Do we have credentials to check?
2852 if len(smbServer.getCredentials()) > 0:
2853 isGuest = False
2854 identity = authenticateMessage['user_name'].decode('utf-16le').lower()
2855 # Do we have this user's credentials?
2856 if identity in smbServer.getCredentials():
2857 # Process data:
2858 # Let's parse some data and keep it to ourselves in case it is asked
2859 uid, lmhash, nthash = smbServer.getCredentials()[identity]
2861 errorCode, sessionKey = computeNTLMv2(identity, lmhash, nthash, smbServer.getSMBChallenge(),
2862 authenticateMessage, connData['CHALLENGE_MESSAGE'],
2863 connData['NEGOTIATE_MESSAGE'])
2865 if sessionKey is not None:
2866 connData['SignatureEnabled'] = True
2867 connData['SigningSessionKey'] = sessionKey
2868 connData['SignSequenceNumber'] = 1
2869 else:
2870 errorCode = STATUS_LOGON_FAILURE
2871 else:
2872 # No credentials provided, let's grant access
2873 isGuest = True
2874 errorCode = STATUS_SUCCESS
2876 if errorCode == STATUS_SUCCESS:
2877 connData['Authenticated'] = True
2878 respToken = SPNEGO_NegTokenResp()
2879 # accept-completed
2880 respToken['NegState'] = b'\x00'
2881 smbServer.log('User %s\\%s authenticated successfully' % (
2882 authenticateMessage['host_name'].decode('utf-16le'),
2883 authenticateMessage['user_name'].decode('utf-16le')))
2884 # Let's store it in the connection data
2885 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
2886 try:
2887 jtr_dump_path = smbServer.getJTRdumpPath()
2888 ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],
2889 authenticateMessage['user_name'],
2890 authenticateMessage['domain_name'],
2891 authenticateMessage['lanman'], authenticateMessage['ntlm'])
2892 smbServer.log(ntlm_hash_data['hash_string'])
2893 if jtr_dump_path != '':
2894 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
2895 jtr_dump_path)
2896 except:
2897 smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
2899 if isGuest:
2900 respSMBCommand['SessionFlags'] = 1
2902 else:
2903 respToken = SPNEGO_NegTokenResp()
2904 respToken['NegState'] = b'\x02'
2905 smbServer.log("Could not authenticate user!")
2906 else:
2907 raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
2909 respSMBCommand['SecurityBufferOffset'] = 0x48
2910 respSMBCommand['SecurityBufferLength'] = len(respToken)
2911 respSMBCommand['Buffer'] = respToken.getData()
2913 # From now on, the client can ask for other commands
2914 connData['Authenticated'] = True
2915 # For now, just switching to nobody
2916 # os.setregid(65534,65534)
2917 # os.setreuid(65534,65534)
2918 smbServer.setConnectionData(connId, connData)
2920 return [respSMBCommand], None, errorCode
2922 @staticmethod
2923 def smb2TreeConnect(connId, smbServer, recvPacket):
2924 connData = smbServer.getConnectionData(connId)
2926 respPacket = smb2.SMB2Packet()
2927 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR
2928 respPacket['Status'] = STATUS_SUCCESS
2929 respPacket['CreditRequestResponse'] = 1
2930 respPacket['Command'] = recvPacket['Command']
2931 respPacket['SessionID'] = connData['Uid']
2932 respPacket['Reserved'] = recvPacket['Reserved']
2933 respPacket['MessageID'] = recvPacket['MessageID']
2934 respPacket['TreeID'] = recvPacket['TreeID']
2936 respSMBCommand = smb2.SMB2TreeConnect_Response()
2938 treeConnectRequest = smb2.SMB2TreeConnect(recvPacket['Data'])
2940 errorCode = STATUS_SUCCESS
2942 ## Process here the request, does the share exist?
2943 path = recvPacket.getData()[treeConnectRequest['PathOffset']:][:treeConnectRequest['PathLength']]
2944 UNCOrShare = path.decode('utf-16le')
2946 # Is this a UNC?
2947 if ntpath.ismount(UNCOrShare):
2948 path = UNCOrShare.split('\\')[3]
2949 else:
2950 path = ntpath.basename(UNCOrShare)
2952 share = searchShare(connId, path.upper(), smbServer)
2953 if share is not None:
2954 # Simple way to generate a Tid
2955 if len(connData['ConnectedShares']) == 0:
2956 tid = 1
2957 else:
2958 tid = list(connData['ConnectedShares'].keys())[-1] + 1
2959 connData['ConnectedShares'][tid] = share
2960 connData['ConnectedShares'][tid]['shareName'] = path
2961 respPacket['TreeID'] = tid
2962 smbServer.log("Connecting Share(%d:%s)" % (tid, path))
2963 else:
2964 smbServer.log("SMB2_TREE_CONNECT not found %s" % path, logging.ERROR)
2965 errorCode = STATUS_OBJECT_PATH_NOT_FOUND
2966 respPacket['Status'] = errorCode
2967 ##
2969 if path.upper() == 'IPC$':
2970 respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_PIPE
2971 respSMBCommand['ShareFlags'] = 0x30
2972 else:
2973 respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_DISK
2974 respSMBCommand['ShareFlags'] = 0x0
2976 respSMBCommand['Capabilities'] = 0
2977 respSMBCommand['MaximalAccess'] = 0x000f01ff
2979 respPacket['Data'] = respSMBCommand
2981 # Sign the packet if needed
2982 if connData['SignatureEnabled']:
2983 smbServer.signSMBv2(respPacket, connData['SigningSessionKey'])
2984 smbServer.setConnectionData(connId, connData)
2986 return None, [respPacket], errorCode
2988 @staticmethod
2989 def smb2Create(connId, smbServer, recvPacket):
2990 connData = smbServer.getConnectionData(connId)
2992 respSMBCommand = smb2.SMB2Create_Response()
2994 ntCreateRequest = smb2.SMB2Create(recvPacket['Data'])
2996 respSMBCommand['Buffer'] = b'\x00'
2997 # Get the Tid associated
2998 if recvPacket['TreeID'] in connData['ConnectedShares']:
2999 # If we have a rootFid, the path is relative to that fid
3000 errorCode = STATUS_SUCCESS
3001 if 'path' in connData['ConnectedShares'][recvPacket['TreeID']]:
3002 path = connData['ConnectedShares'][recvPacket['TreeID']]['path']
3003 else:
3004 path = 'NONE'
3005 errorCode = STATUS_ACCESS_DENIED
3007 deleteOnClose = False
3009 fileName = os.path.normpath(
3010 ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le').replace('\\', '/'))
3011 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
3012 # strip leading '/'
3013 fileName = fileName[1:]
3015 if not isInFileJail(path, fileName):
3016 LOG.error("Path not in current working directory")
3017 return [smb2.SMB2Error()], None, STATUS_OBJECT_PATH_SYNTAX_BAD
3019 pathName = os.path.join(path, fileName)
3020 createDisposition = ntCreateRequest['CreateDisposition']
3021 mode = 0
3023 if createDisposition == smb2.FILE_SUPERSEDE:
3024 mode |= os.O_TRUNC | os.O_CREAT
3025 elif createDisposition & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF:
3026 mode |= os.O_TRUNC | os.O_CREAT
3027 elif createDisposition & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE:
3028 if os.path.exists(pathName) is True:
3029 mode |= os.O_TRUNC
3030 else:
3031 errorCode = STATUS_NO_SUCH_FILE
3032 elif createDisposition & smb2.FILE_OPEN_IF == smb2.FILE_OPEN_IF:
3033 if os.path.exists(pathName) is True:
3034 mode |= os.O_TRUNC
3035 else:
3036 mode |= os.O_TRUNC | os.O_CREAT
3037 elif createDisposition & smb2.FILE_CREATE == smb2.FILE_CREATE:
3038 if os.path.exists(pathName) is True:
3039 errorCode = STATUS_OBJECT_NAME_COLLISION
3040 else:
3041 mode |= os.O_CREAT
3042 elif createDisposition & smb2.FILE_OPEN == smb2.FILE_OPEN:
3043 if os.path.exists(pathName) is not True and (
3044 str(pathName) in smbServer.getRegisteredNamedPipes()) is not True:
3045 errorCode = STATUS_NO_SUCH_FILE
3047 if errorCode == STATUS_SUCCESS:
3048 desiredAccess = ntCreateRequest['DesiredAccess']
3049 if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):
3050 mode |= os.O_RDONLY
3051 if (desiredAccess & smb2.FILE_WRITE_DATA) or (desiredAccess & smb2.GENERIC_WRITE):
3052 if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):
3053 mode |= os.O_RDWR # | os.O_APPEND
3054 else:
3055 mode |= os.O_WRONLY # | os.O_APPEND
3056 if desiredAccess & smb2.GENERIC_ALL:
3057 mode |= os.O_RDWR # | os.O_APPEND
3059 createOptions = ntCreateRequest['CreateOptions']
3060 if mode & os.O_CREAT == os.O_CREAT:
3061 if createOptions & smb2.FILE_DIRECTORY_FILE == smb2.FILE_DIRECTORY_FILE:
3062 try:
3063 # Let's create the directory
3064 os.mkdir(pathName)
3065 mode = os.O_RDONLY
3066 except Exception as e:
3067 smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName, mode, e), logging.ERROR)
3068 errorCode = STATUS_ACCESS_DENIED
3069 if createOptions & smb2.FILE_NON_DIRECTORY_FILE == smb2.FILE_NON_DIRECTORY_FILE:
3070 # If the file being opened is a directory, the server MUST fail the request with
3071 # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server
3072 # response.
3073 if os.path.isdir(pathName) is True:
3074 errorCode = STATUS_FILE_IS_A_DIRECTORY
3076 if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE:
3077 deleteOnClose = True
3079 if errorCode == STATUS_SUCCESS:
3080 try:
3081 if os.path.isdir(pathName) and sys.platform == 'win32':
3082 fid = VOID_FILE_DESCRIPTOR
3083 else:
3084 if sys.platform == 'win32':
3085 mode |= os.O_BINARY
3086 if str(pathName) in smbServer.getRegisteredNamedPipes():
3087 fid = PIPE_FILE_DESCRIPTOR
3088 sock = socket.socket()
3089 sock.connect(smbServer.getRegisteredNamedPipes()[str(pathName)])
3090 else:
3091 fid = os.open(pathName, mode)
3092 except Exception as e:
3093 smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName, mode, e), logging.ERROR)
3094 # print e
3095 fid = 0
3096 errorCode = STATUS_ACCESS_DENIED
3097 else:
3098 errorCode = STATUS_SMB_BAD_TID
3100 if errorCode == STATUS_SUCCESS:
3101 # Simple way to generate a fid
3102 fakefid = uuid.generate()
3104 respSMBCommand['FileID'] = fakefid
3105 respSMBCommand['CreateAction'] = createDisposition
3107 if fid == PIPE_FILE_DESCRIPTOR:
3108 respSMBCommand['CreationTime'] = 0
3109 respSMBCommand['LastAccessTime'] = 0
3110 respSMBCommand['LastWriteTime'] = 0
3111 respSMBCommand['ChangeTime'] = 0
3112 respSMBCommand['AllocationSize'] = 4096
3113 respSMBCommand['EndOfFile'] = 0
3114 respSMBCommand['FileAttributes'] = 0x80
3116 else:
3117 if os.path.isdir(pathName):
3118 respSMBCommand['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY
3119 else:
3120 respSMBCommand['FileAttributes'] = ntCreateRequest['FileAttributes']
3121 # Let's get this file's information
3122 respInfo, errorCode = queryPathInformation('', pathName, level=smb.SMB_QUERY_FILE_ALL_INFO)
3123 if errorCode == STATUS_SUCCESS:
3124 respSMBCommand['CreationTime'] = respInfo['CreationTime']
3125 respSMBCommand['LastAccessTime'] = respInfo['LastAccessTime']
3126 respSMBCommand['LastWriteTime'] = respInfo['LastWriteTime']
3127 respSMBCommand['LastChangeTime'] = respInfo['LastChangeTime']
3128 respSMBCommand['FileAttributes'] = respInfo['ExtFileAttributes']
3129 respSMBCommand['AllocationSize'] = respInfo['AllocationSize']
3130 respSMBCommand['EndOfFile'] = respInfo['EndOfFile']
3132 if errorCode == STATUS_SUCCESS:
3133 # Let's store the fid for the connection
3134 # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode))
3135 connData['OpenedFiles'][fakefid] = {}
3136 connData['OpenedFiles'][fakefid]['FileHandle'] = fid
3137 connData['OpenedFiles'][fakefid]['FileName'] = pathName
3138 connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose
3139 connData['OpenedFiles'][fakefid]['Open'] = {}
3140 connData['OpenedFiles'][fakefid]['Open']['EnumerationLocation'] = 0
3141 connData['OpenedFiles'][fakefid]['Open']['EnumerationSearchPattern'] = ''
3142 if fid == PIPE_FILE_DESCRIPTOR:
3143 connData['OpenedFiles'][fakefid]['Socket'] = sock
3144 else:
3145 respSMBCommand = smb2.SMB2Error()
3147 if errorCode == STATUS_SUCCESS:
3148 connData['LastRequest']['SMB2_CREATE'] = respSMBCommand
3149 smbServer.setConnectionData(connId, connData)
3151 return [respSMBCommand], None, errorCode
3153 @staticmethod
3154 def smb2Close(connId, smbServer, recvPacket):
3155 connData = smbServer.getConnectionData(connId)
3157 respSMBCommand = smb2.SMB2Close_Response()
3159 closeRequest = smb2.SMB2Close(recvPacket['Data'])
3161 if closeRequest['FileID'].getData() == b'\xff' * 16:
3162 # Let's take the data from the lastRequest
3163 if 'SMB2_CREATE' in connData['LastRequest']:
3164 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3165 else:
3166 fileID = closeRequest['FileID'].getData()
3167 else:
3168 fileID = closeRequest['FileID'].getData()
3170 if fileID in connData['OpenedFiles']:
3171 errorCode = STATUS_SUCCESS
3172 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
3173 pathName = connData['OpenedFiles'][fileID]['FileName']
3174 infoRecord = None
3175 try:
3176 if fileHandle == PIPE_FILE_DESCRIPTOR:
3177 connData['OpenedFiles'][fileID]['Socket'].close()
3178 elif fileHandle != VOID_FILE_DESCRIPTOR:
3179 os.close(fileHandle)
3180 infoRecord, errorCode = queryFileInformation(os.path.dirname(pathName), os.path.basename(pathName),
3181 smb2.SMB2_FILE_NETWORK_OPEN_INFO)
3182 except Exception as e:
3183 smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR)
3184 errorCode = STATUS_INVALID_HANDLE
3185 else:
3186 # Check if the file was marked for removal
3187 if connData['OpenedFiles'][fileID]['DeleteOnClose'] is True:
3188 try:
3189 if os.path.isdir(pathName):
3190 shutil.rmtree(connData['OpenedFiles'][fileID]['FileName'])
3191 else:
3192 os.remove(connData['OpenedFiles'][fileID]['FileName'])
3193 except Exception as e:
3194 smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR)
3195 errorCode = STATUS_ACCESS_DENIED
3197 # Now fill out the response
3198 if infoRecord is not None:
3199 respSMBCommand['CreationTime'] = infoRecord['CreationTime']
3200 respSMBCommand['LastAccessTime'] = infoRecord['LastAccessTime']
3201 respSMBCommand['LastWriteTime'] = infoRecord['LastWriteTime']
3202 respSMBCommand['ChangeTime'] = infoRecord['ChangeTime']
3203 respSMBCommand['AllocationSize'] = infoRecord['AllocationSize']
3204 respSMBCommand['EndofFile'] = infoRecord['EndOfFile']
3205 respSMBCommand['FileAttributes'] = infoRecord['FileAttributes']
3206 if errorCode == STATUS_SUCCESS:
3207 del (connData['OpenedFiles'][fileID])
3208 else:
3209 errorCode = STATUS_INVALID_HANDLE
3211 smbServer.setConnectionData(connId, connData)
3212 return [respSMBCommand], None, errorCode
3214 @staticmethod
3215 def smb2QueryInfo(connId, smbServer, recvPacket):
3216 connData = smbServer.getConnectionData(connId)
3218 respSMBCommand = smb2.SMB2QueryInfo_Response()
3220 queryInfo = smb2.SMB2QueryInfo(recvPacket['Data'])
3222 errorCode = STATUS_SUCCESS
3224 respSMBCommand['OutputBufferOffset'] = 0x48
3225 respSMBCommand['Buffer'] = b'\x00'
3227 if queryInfo['FileID'].getData() == b'\xff' * 16:
3228 # Let's take the data from the lastRequest
3229 if 'SMB2_CREATE' in connData['LastRequest']:
3230 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3231 else:
3232 fileID = queryInfo['FileID'].getData()
3233 else:
3234 fileID = queryInfo['FileID'].getData()
3236 if recvPacket['TreeID'] in connData['ConnectedShares']:
3237 if fileID in connData['OpenedFiles']:
3238 fileName = connData['OpenedFiles'][fileID]['FileName']
3240 if queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILE:
3241 if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_INTERNAL_INFO:
3242 # No need to call queryFileInformation, we have the data here
3243 infoRecord = smb2.FileInternalInformation()
3244 infoRecord['IndexNumber'] = fileID
3245 else:
3246 infoRecord, errorCode = queryFileInformation(os.path.dirname(fileName),
3247 os.path.basename(fileName),
3248 queryInfo['FileInfoClass'])
3249 elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM:
3250 if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_EA_INFO:
3251 infoRecord = b'\x00' * 4
3252 else:
3253 infoRecord = queryFsInformation(os.path.dirname(fileName), os.path.basename(fileName),
3254 queryInfo['FileInfoClass'])
3255 elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY:
3256 # Failing for now, until we support it
3257 infoRecord = None
3258 errorCode = STATUS_ACCESS_DENIED
3259 else:
3260 smbServer.log("queryInfo not supported (%x)" % queryInfo['InfoType'], logging.ERROR)
3262 if infoRecord is not None:
3263 respSMBCommand['OutputBufferLength'] = len(infoRecord)
3264 respSMBCommand['Buffer'] = infoRecord
3265 else:
3266 errorCode = STATUS_INVALID_HANDLE
3267 else:
3268 errorCode = STATUS_SMB_BAD_TID
3270 smbServer.setConnectionData(connId, connData)
3271 return [respSMBCommand], None, errorCode
3273 @staticmethod
3274 def smb2SetInfo(connId, smbServer, recvPacket):
3275 connData = smbServer.getConnectionData(connId)
3277 respSMBCommand = smb2.SMB2SetInfo_Response()
3279 setInfo = smb2.SMB2SetInfo(recvPacket['Data'])
3281 errorCode = STATUS_SUCCESS
3283 if setInfo['FileID'].getData() == b'\xff' * 16:
3284 # Let's take the data from the lastRequest
3285 if 'SMB2_CREATE' in connData['LastRequest']:
3286 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3287 else:
3288 fileID = setInfo['FileID'].getData()
3289 else:
3290 fileID = setInfo['FileID'].getData()
3292 if recvPacket['TreeID'] in connData['ConnectedShares']:
3293 path = connData['ConnectedShares'][recvPacket['TreeID']]['path']
3294 if fileID in connData['OpenedFiles']:
3295 pathName = connData['OpenedFiles'][fileID]['FileName']
3297 if setInfo['InfoType'] == smb2.SMB2_0_INFO_FILE:
3298 # The file information is being set
3299 informationLevel = setInfo['FileInfoClass']
3300 if informationLevel == smb2.SMB2_FILE_DISPOSITION_INFO:
3301 infoRecord = smb.SMBSetFileDispositionInfo(setInfo['Buffer'])
3302 if infoRecord['DeletePending'] > 0:
3303 # Mark this file for removal after closed
3304 connData['OpenedFiles'][fileID]['DeleteOnClose'] = True
3305 elif informationLevel == smb2.SMB2_FILE_BASIC_INFO:
3306 infoRecord = smb.SMBSetFileBasicInfo(setInfo['Buffer'])
3307 # Creation time won't be set, the other ones we play with.
3308 atime = infoRecord['LastWriteTime']
3309 if atime == 0:
3310 atime = -1
3311 else:
3312 atime = getUnixTime(atime)
3313 mtime = infoRecord['ChangeTime']
3314 if mtime == 0:
3315 mtime = -1
3316 else:
3317 mtime = getUnixTime(mtime)
3318 if atime > 0 and mtime > 0:
3319 os.utime(pathName, (atime, mtime))
3320 elif informationLevel == smb2.SMB2_FILE_END_OF_FILE_INFO:
3321 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
3322 infoRecord = smb.SMBSetFileEndOfFileInfo(setInfo['Buffer'])
3323 if infoRecord['EndOfFile'] > 0:
3324 os.lseek(fileHandle, infoRecord['EndOfFile'] - 1, 0)
3325 os.write(fileHandle, b'\x00')
3326 elif informationLevel == smb2.SMB2_FILE_RENAME_INFO:
3327 renameInfo = smb2.FILE_RENAME_INFORMATION_TYPE_2(setInfo['Buffer'])
3328 newPathName = os.path.join(path, renameInfo['FileName'].decode('utf-16le').replace('\\', '/'))
3329 if renameInfo['ReplaceIfExists'] == 0 and os.path.exists(newPathName):
3330 return [smb2.SMB2Error()], None, STATUS_OBJECT_NAME_COLLISION
3331 try:
3332 os.rename(pathName, newPathName)
3333 connData['OpenedFiles'][fileID]['FileName'] = newPathName
3334 except Exception as e:
3335 smbServer.log("smb2SetInfo: %s" % e, logging.ERROR)
3336 errorCode = STATUS_ACCESS_DENIED
3337 else:
3338 smbServer.log('Unknown level for set file info! 0x%x' % informationLevel, logging.ERROR)
3339 # UNSUPPORTED
3340 errorCode = STATUS_NOT_SUPPORTED
3341 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM:
3342 # # The underlying object store information is being set.
3343 # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass'])
3344 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY:
3345 # # The security information is being set.
3346 # # Failing for now, until we support it
3347 # infoRecord = None
3348 # errorCode = STATUS_ACCESS_DENIED
3349 # elif setInfo['InfoType'] == smb2.SMB2_0_INFO_QUOTA:
3350 # # The underlying object store quota information is being set.
3351 # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass'])
3352 else:
3353 smbServer.log("setInfo not supported (%x)" % setInfo['InfoType'], logging.ERROR)
3355 else:
3356 errorCode = STATUS_INVALID_HANDLE
3357 else:
3358 errorCode = STATUS_SMB_BAD_TID
3360 smbServer.setConnectionData(connId, connData)
3361 return [respSMBCommand], None, errorCode
3363 @staticmethod
3364 def smb2Write(connId, smbServer, recvPacket):
3365 connData = smbServer.getConnectionData(connId)
3367 respSMBCommand = smb2.SMB2Write_Response()
3368 writeRequest = smb2.SMB2Write(recvPacket['Data'])
3370 respSMBCommand['Buffer'] = b'\x00'
3372 if writeRequest['FileID'].getData() == b'\xff' * 16:
3373 # Let's take the data from the lastRequest
3374 if 'SMB2_CREATE' in connData['LastRequest']:
3375 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3376 else:
3377 fileID = writeRequest['FileID'].getData()
3378 else:
3379 fileID = writeRequest['FileID'].getData()
3381 if fileID in connData['OpenedFiles']:
3382 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
3383 errorCode = STATUS_SUCCESS
3384 try:
3385 if fileHandle != PIPE_FILE_DESCRIPTOR:
3386 offset = writeRequest['Offset']
3387 # If we're trying to write past the file end we just skip the write call (Vista does this)
3388 if os.lseek(fileHandle, 0, 2) >= offset:
3389 os.lseek(fileHandle, offset, 0)
3390 os.write(fileHandle, writeRequest['Buffer'])
3391 else:
3392 sock = connData['OpenedFiles'][fileID]['Socket']
3393 sock.send(writeRequest['Buffer'])
3395 respSMBCommand['Count'] = writeRequest['Length']
3396 respSMBCommand['Remaining'] = 0xff
3397 except Exception as e:
3398 smbServer.log('SMB2_WRITE: %s' % e, logging.ERROR)
3399 errorCode = STATUS_ACCESS_DENIED
3400 else:
3401 errorCode = STATUS_INVALID_HANDLE
3403 smbServer.setConnectionData(connId, connData)
3404 return [respSMBCommand], None, errorCode
3406 @staticmethod
3407 def smb2Read(connId, smbServer, recvPacket):
3408 connData = smbServer.getConnectionData(connId)
3410 respSMBCommand = smb2.SMB2Read_Response()
3411 readRequest = smb2.SMB2Read(recvPacket['Data'])
3413 respSMBCommand['Buffer'] = b'\x00'
3415 if readRequest['FileID'].getData() == b'\xff' * 16:
3416 # Let's take the data from the lastRequest
3417 if 'SMB2_CREATE' in connData['LastRequest']:
3418 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3419 else:
3420 fileID = readRequest['FileID'].getData()
3421 else:
3422 fileID = readRequest['FileID'].getData()
3424 if fileID in connData['OpenedFiles']:
3425 fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
3426 errorCode = 0
3427 try:
3428 if fileHandle != PIPE_FILE_DESCRIPTOR:
3429 offset = readRequest['Offset']
3430 os.lseek(fileHandle, offset, 0)
3431 content = os.read(fileHandle, readRequest['Length'])
3432 else:
3433 sock = connData['OpenedFiles'][fileID]['Socket']
3434 content = sock.recv(readRequest['Length'])
3436 respSMBCommand['DataOffset'] = 0x50
3437 respSMBCommand['DataLength'] = len(content)
3438 respSMBCommand['DataRemaining'] = 0
3439 respSMBCommand['Buffer'] = content
3440 except Exception as e:
3441 smbServer.log('SMB2_READ: %s ' % e, logging.ERROR)
3442 errorCode = STATUS_ACCESS_DENIED
3443 else:
3444 errorCode = STATUS_INVALID_HANDLE
3446 smbServer.setConnectionData(connId, connData)
3447 return [respSMBCommand], None, errorCode
3449 @staticmethod
3450 def smb2Flush(connId, smbServer, recvPacket):
3451 connData = smbServer.getConnectionData(connId)
3453 respSMBCommand = smb2.SMB2Flush_Response()
3454 flushRequest = smb2.SMB2Flush(recvPacket['Data'])
3456 if flushRequest['FileID'].getData() in connData['OpenedFiles']:
3457 fileHandle = connData['OpenedFiles'][flushRequest['FileID'].getData()]['FileHandle']
3458 errorCode = STATUS_SUCCESS
3459 try:
3460 os.fsync(fileHandle)
3461 except Exception as e:
3462 smbServer.log("SMB2_FLUSH %s" % e, logging.ERROR)
3463 errorCode = STATUS_ACCESS_DENIED
3464 else:
3465 errorCode = STATUS_INVALID_HANDLE
3467 smbServer.setConnectionData(connId, connData)
3468 return [respSMBCommand], None, errorCode
3470 @staticmethod
3471 def smb2QueryDirectory(connId, smbServer, recvPacket):
3472 connData = smbServer.getConnectionData(connId)
3473 respSMBCommand = smb2.SMB2QueryDirectory_Response()
3474 queryDirectoryRequest = smb2.SMB2QueryDirectory(recvPacket['Data'])
3476 respSMBCommand['Buffer'] = b'\x00'
3478 # The server MUST locate the tree connection, as specified in section 3.3.5.2.11.
3479 if (recvPacket['TreeID'] in connData['ConnectedShares']) is False:
3480 return [smb2.SMB2Error()], None, STATUS_NETWORK_NAME_DELETED
3482 # Next, the server MUST locate the open for the directory to be queried
3483 # If no open is found, the server MUST fail the request with STATUS_FILE_CLOSED
3484 if queryDirectoryRequest['FileID'].getData() == b'\xff' * 16:
3485 # Let's take the data from the lastRequest
3486 if 'SMB2_CREATE' in connData['LastRequest']:
3487 fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
3488 else:
3489 fileID = queryDirectoryRequest['FileID'].getData()
3490 else:
3491 fileID = queryDirectoryRequest['FileID'].getData()
3493 if (fileID in connData['OpenedFiles']) is False:
3494 return [smb2.SMB2Error()], None, STATUS_FILE_CLOSED
3496 # If the open is not an open to a directory, the request MUST be failed
3497 # with STATUS_INVALID_PARAMETER.
3498 if os.path.isdir(connData['OpenedFiles'][fileID]['FileName']) is False:
3499 return [smb2.SMB2Error()], None, STATUS_INVALID_PARAMETER
3501 # If any other information class is specified in the FileInformationClass
3502 # field of the SMB2 QUERY_DIRECTORY Request, the server MUST fail the
3503 # operation with STATUS_INVALID_INFO_CLASS.
3504 if queryDirectoryRequest['FileInformationClass'] not in (
3505 smb2.FILE_DIRECTORY_INFORMATION, smb2.FILE_FULL_DIRECTORY_INFORMATION,
3506 smb2.FILEID_FULL_DIRECTORY_INFORMATION,
3507 smb2.FILE_BOTH_DIRECTORY_INFORMATION, smb2.FILEID_BOTH_DIRECTORY_INFORMATION,
3508 smb2.FILENAMES_INFORMATION):
3509 return [smb2.SMB2Error()], None, STATUS_INVALID_INFO_CLASS
3511 # If SMB2_REOPEN is set in the Flags field of the SMB2 QUERY_DIRECTORY
3512 # Request, the server SHOULD<326> set Open.EnumerationLocation to 0
3513 # and Open.EnumerationSearchPattern to an empty string.
3514 if queryDirectoryRequest['Flags'] & smb2.SMB2_REOPEN:
3515 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0
3516 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = ''
3518 # If SMB2_RESTART_SCANS is set in the Flags field of the SMB2
3519 # QUERY_DIRECTORY Request, the server MUST set
3520 # Open.EnumerationLocation to 0.
3521 if queryDirectoryRequest['Flags'] & smb2.SMB2_RESTART_SCANS:
3522 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0
3524 # If Open.EnumerationLocation is 0 and Open.EnumerationSearchPattern
3525 # is an empty string, then Open.EnumerationSearchPattern MUST be set
3526 # to the search pattern specified in the SMB2 QUERY_DIRECTORY by
3527 # FileNameOffset and FileNameLength. If FileNameLength is 0, the server
3528 # SHOULD<327> set Open.EnumerationSearchPattern as "*" to search all entries.
3530 pattern = queryDirectoryRequest['Buffer'].decode('utf-16le')
3531 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0 and \
3532 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] == '':
3533 if pattern == '':
3534 pattern = '*'
3535 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern
3537 # If SMB2_INDEX_SPECIFIED is set and FileNameLength is not zero,
3538 # the server MUST set Open.EnumerationSearchPattern to the search pattern
3539 # specified in the request by FileNameOffset and FileNameLength.
3540 if queryDirectoryRequest['Flags'] & smb2.SMB2_INDEX_SPECIFIED and \
3541 queryDirectoryRequest['FileNameLength'] > 0:
3542 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern
3544 pathName = os.path.join(os.path.normpath(connData['OpenedFiles'][fileID]['FileName']), pattern)
3545 searchResult, searchCount, errorCode = findFirst2(os.path.dirname(pathName),
3546 os.path.basename(pathName),
3547 queryDirectoryRequest['FileInformationClass'],
3548 smb.ATTR_DIRECTORY, isSMB2=True)
3550 if errorCode != STATUS_SUCCESS:
3551 return [smb2.SMB2Error()], None, errorCode
3553 if searchCount > 2 and pattern == '*':
3554 # strip . and ..
3555 searchCount -= 2
3556 searchResult = searchResult[2:]
3558 if searchCount == 0 and connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0:
3559 return [smb2.SMB2Error()], None, STATUS_NO_SUCH_FILE
3561 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] < 0:
3562 return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES
3564 totalData = 0
3565 respData = b''
3566 for nItem in range(connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'], searchCount):
3567 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] += 1
3568 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY:
3569 # If single entry is requested we must clear the NextEntryOffset
3570 searchResult[nItem]['NextEntryOffset'] = 0
3571 data = searchResult[nItem].getData()
3572 lenData = len(data)
3573 padLen = (8 - (lenData % 8)) % 8
3575 if (totalData + lenData) >= queryDirectoryRequest['OutputBufferLength']:
3576 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] -= 1
3577 break
3578 else:
3579 respData += data + b'\x00' * padLen
3580 totalData += lenData + padLen
3582 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY:
3583 break
3585 if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] >= searchCount:
3586 connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = -1
3588 respSMBCommand['OutputBufferOffset'] = 0x48
3589 respSMBCommand['OutputBufferLength'] = totalData
3590 respSMBCommand['Buffer'] = respData
3592 smbServer.setConnectionData(connId, connData)
3593 return [respSMBCommand], None, errorCode
3595 @staticmethod
3596 def smb2ChangeNotify(connId, smbServer, recvPacket):
3598 return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED
3600 @staticmethod
3601 def smb2Echo(connId, smbServer, recvPacket):
3603 respSMBCommand = smb2.SMB2Echo_Response()
3605 return [respSMBCommand], None, STATUS_SUCCESS
3607 @staticmethod
3608 def smb2TreeDisconnect(connId, smbServer, recvPacket):
3609 connData = smbServer.getConnectionData(connId)
3611 respSMBCommand = smb2.SMB2TreeDisconnect_Response()
3613 if recvPacket['TreeID'] in connData['ConnectedShares']:
3614 smbServer.log("Disconnecting Share(%d:%s)" % (
3615 recvPacket['TreeID'], connData['ConnectedShares'][recvPacket['TreeID']]['shareName']))
3616 del (connData['ConnectedShares'][recvPacket['TreeID']])
3617 errorCode = STATUS_SUCCESS
3618 else:
3619 # STATUS_SMB_BAD_TID
3620 errorCode = STATUS_SMB_BAD_TID
3622 smbServer.setConnectionData(connId, connData)
3623 return [respSMBCommand], None, errorCode
3625 @staticmethod
3626 def smb2Logoff(connId, smbServer, recvPacket):
3627 connData = smbServer.getConnectionData(connId)
3629 respSMBCommand = smb2.SMB2Logoff_Response()
3631 if recvPacket['SessionID'] != connData['Uid']:
3632 # STATUS_SMB_BAD_UID
3633 errorCode = STATUS_SMB_BAD_UID
3634 else:
3635 errorCode = STATUS_SUCCESS
3637 connData['Uid'] = 0
3638 connData['Authenticated'] = False
3640 smbServer.setConnectionData(connId, connData)
3641 return [respSMBCommand], None, errorCode
3643 @staticmethod
3644 def smb2Ioctl(connId, smbServer, recvPacket):
3645 connData = smbServer.getConnectionData(connId)
3647 respSMBCommand = smb2.SMB2Ioctl_Response()
3648 ioctlRequest = smb2.SMB2Ioctl(recvPacket['Data'])
3650 ioctls = smbServer.getIoctls()
3651 if ioctlRequest['CtlCode'] in ioctls:
3652 outputData, errorCode = ioctls[ioctlRequest['CtlCode']](connId, smbServer, ioctlRequest)
3653 if errorCode == STATUS_SUCCESS:
3654 respSMBCommand['CtlCode'] = ioctlRequest['CtlCode']
3655 respSMBCommand['FileID'] = ioctlRequest['FileID']
3656 respSMBCommand['InputOffset'] = 0
3657 respSMBCommand['InputCount'] = 0
3658 respSMBCommand['OutputOffset'] = 0x70
3659 respSMBCommand['OutputCount'] = len(outputData)
3660 respSMBCommand['Flags'] = 0
3661 respSMBCommand['Buffer'] = outputData
3662 else:
3663 respSMBCommand = outputData
3664 else:
3665 smbServer.log("Ioctl not implemented command: 0x%x" % ioctlRequest['CtlCode'], logging.DEBUG)
3666 errorCode = STATUS_INVALID_DEVICE_REQUEST
3667 respSMBCommand = smb2.SMB2Error()
3669 smbServer.setConnectionData(connId, connData)
3670 return [respSMBCommand], None, errorCode
3672 @staticmethod
3673 def smb2Lock(connId, smbServer, recvPacket):
3674 connData = smbServer.getConnectionData(connId)
3676 respSMBCommand = smb2.SMB2Lock_Response()
3678 # I'm actually doing nothing.. just make MacOS happy ;)
3679 errorCode = STATUS_SUCCESS
3681 smbServer.setConnectionData(connId, connData)
3682 return [respSMBCommand], None, errorCode
3684 @staticmethod
3685 def smb2Cancel(connId, smbServer, recvPacket):
3686 # I'm actually doing nothing
3687 return [smb2.SMB2Error()], None, STATUS_CANCELLED
3689 @staticmethod
3690 def default(connId, smbServer, recvPacket):
3691 # By default we return an SMB Packet with error not implemented
3692 smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'], logging.DEBUG)
3693 return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED
3696class Ioctls:
3697 @staticmethod
3698 def fsctlDfsGetReferrals(connId, smbServer, ioctlRequest):
3699 return smb2.SMB2Error(), STATUS_FS_DRIVER_REQUIRED
3701 @staticmethod
3702 def fsctlPipeTransceive(connId, smbServer, ioctlRequest):
3703 connData = smbServer.getConnectionData(connId)
3705 ioctlResponse = ''
3707 if ioctlRequest['FileID'].getData() in connData['OpenedFiles']:
3708 fileHandle = connData['OpenedFiles'][ioctlRequest['FileID'].getData()]['FileHandle']
3709 errorCode = STATUS_SUCCESS
3710 try:
3711 if fileHandle != PIPE_FILE_DESCRIPTOR:
3712 errorCode = STATUS_INVALID_DEVICE_REQUEST
3713 else:
3714 sock = connData['OpenedFiles'][ioctlRequest['FileID'].getData()]['Socket']
3715 sock.sendall(ioctlRequest['Buffer'])
3716 ioctlResponse = sock.recv(ioctlRequest['MaxOutputResponse'])
3717 except Exception as e:
3718 smbServer.log('fsctlPipeTransceive: %s ' % e, logging.ERROR)
3719 errorCode = STATUS_ACCESS_DENIED
3720 else:
3721 errorCode = STATUS_INVALID_DEVICE_REQUEST
3723 smbServer.setConnectionData(connId, connData)
3724 return ioctlResponse, errorCode
3726 @staticmethod
3727 def fsctlValidateNegotiateInfo(connId, smbServer, ioctlRequest):
3728 connData = smbServer.getConnectionData(connId)
3730 errorCode = STATUS_SUCCESS
3732 validateNegotiateInfo = smb2.VALIDATE_NEGOTIATE_INFO(ioctlRequest['Buffer'])
3733 validateNegotiateInfoResponse = smb2.VALIDATE_NEGOTIATE_INFO_RESPONSE()
3734 validateNegotiateInfoResponse['Capabilities'] = 0
3735 validateNegotiateInfoResponse['Guid'] = b'A' * 16
3736 validateNegotiateInfoResponse['SecurityMode'] = 1
3737 validateNegotiateInfoResponse['Dialect'] = smb2.SMB2_DIALECT_002
3739 smbServer.setConnectionData(connId, connData)
3740 return validateNegotiateInfoResponse.getData(), errorCode
3743class SMBSERVERHandler(socketserver.BaseRequestHandler):
3744 def __init__(self, request, client_address, server, select_poll=False):
3745 self.__SMB = server
3746 # In case of AF_INET6 the client_address contains 4 items, ignore the last 2
3747 self.__ip, self.__port = client_address[:2]
3748 self.__request = request
3749 self.__connId = threading.currentThread().getName()
3750 self.__timeOut = 60 * 5
3751 self.__select_poll = select_poll
3752 # self.__connId = os.getpid()
3753 socketserver.BaseRequestHandler.__init__(self, request, client_address, server)
3755 def handle(self):
3756 self.__SMB.log("Incoming connection (%s,%d)" % (self.__ip, self.__port))
3757 self.__SMB.addConnection(self.__connId, self.__ip, self.__port)
3758 while True:
3759 try:
3760 # First of all let's get the NETBIOS packet
3761 session = nmb.NetBIOSTCPSession(self.__SMB.getServerName(), 'HOST', self.__ip, sess_port=self.__port,
3762 sock=self.__request, select_poll=self.__select_poll)
3763 try:
3764 p = session.recv_packet(self.__timeOut)
3765 except nmb.NetBIOSTimeout:
3766 raise
3767 except nmb.NetBIOSError:
3768 break
3770 if p.get_type() == nmb.NETBIOS_SESSION_REQUEST:
3771 # Someone is requesting a session, we're gonna accept them all :)
3772 _, rn, my = p.get_trailer().split(b' ')
3773 remote_name = nmb.decode_name(b'\x20' + rn)
3774 myname = nmb.decode_name(b'\x20' + my)
3775 self.__SMB.log(
3776 "NetBIOS Session request (%s,%s,%s)" % (self.__ip, remote_name[1].strip(), myname[1]))
3777 r = nmb.NetBIOSSessionPacket()
3778 r.set_type(nmb.NETBIOS_SESSION_POSITIVE_RESPONSE)
3779 r.set_trailer(p.get_trailer())
3780 self.__request.send(r.rawData())
3781 else:
3782 resp = self.__SMB.processRequest(self.__connId, p.get_trailer())
3783 # Send all the packets received. Except for big transactions this should be
3784 # a single packet
3785 for i in resp:
3786 if hasattr(i, 'getData'):
3787 session.send_packet(i.getData())
3788 else:
3789 session.send_packet(i)
3790 except Exception as e:
3791 self.__SMB.log("Handle: %s" % e)
3792 # import traceback
3793 # traceback.print_exc()
3794 break
3796 def finish(self):
3797 # Thread/process is dying, we should tell the main SMB thread to remove all this thread data
3798 self.__SMB.log("Closing down connection (%s,%d)" % (self.__ip, self.__port))
3799 self.__SMB.removeConnection(self.__connId)
3800 return socketserver.BaseRequestHandler.finish(self)
3803class SMBSERVER(socketserver.ThreadingMixIn, socketserver.TCPServer):
3804 # class SMBSERVER(socketserver.ForkingMixIn, socketserver.TCPServer):
3805 def __init__(self, server_address, handler_class=SMBSERVERHandler, config_parser=None):
3806 socketserver.TCPServer.allow_reuse_address = True
3807 socketserver.TCPServer.__init__(self, server_address, handler_class)
3809 # Server name and OS to be presented whenever is necessary
3810 self.__serverName = ''
3811 self.__serverOS = ''
3812 self.__serverDomain = ''
3813 self.__challenge = ''
3814 self.__log = None
3816 # Our ConfigParser data
3817 self.__serverConfig = config_parser
3819 # Our credentials to be used during the server's lifetime
3820 self.__credentials = {}
3822 # Our log file
3823 self.__logFile = ''
3825 # Registered Named Pipes, format is PipeName,Socket
3826 self.__registeredNamedPipes = {}
3828 # JTR dump path
3829 self.__jtr_dump_path = ''
3831 # SMB2 Support flag = default not active
3832 self.__SMB2Support = False
3834 # Our list of commands we will answer, by default the NOT IMPLEMENTED one
3835 self.__smbCommandsHandler = SMBCommands()
3836 self.__smbTrans2Handler = TRANS2Commands()
3837 self.__smbTransHandler = TRANSCommands()
3838 self.__smbNTTransHandler = NTTRANSCommands()
3839 self.__smb2CommandsHandler = SMB2Commands()
3840 self.__IoctlHandler = Ioctls()
3842 self.__smbNTTransCommands = {
3843 # NT IOCTL, can't find doc for this
3844 0xff: self.__smbNTTransHandler.default
3845 }
3847 self.__smbTransCommands = {
3848 '\\PIPE\\LANMAN': self.__smbTransHandler.lanMan,
3849 smb.SMB.TRANS_TRANSACT_NMPIPE: self.__smbTransHandler.transactNamedPipe,
3850 }
3851 self.__smbTrans2Commands = {
3852 smb.SMB.TRANS2_FIND_FIRST2: self.__smbTrans2Handler.findFirst2,
3853 smb.SMB.TRANS2_FIND_NEXT2: self.__smbTrans2Handler.findNext2,
3854 smb.SMB.TRANS2_QUERY_FS_INFORMATION: self.__smbTrans2Handler.queryFsInformation,
3855 smb.SMB.TRANS2_QUERY_PATH_INFORMATION: self.__smbTrans2Handler.queryPathInformation,
3856 smb.SMB.TRANS2_QUERY_FILE_INFORMATION: self.__smbTrans2Handler.queryFileInformation,
3857 smb.SMB.TRANS2_SET_FILE_INFORMATION: self.__smbTrans2Handler.setFileInformation,
3858 smb.SMB.TRANS2_SET_PATH_INFORMATION: self.__smbTrans2Handler.setPathInformation
3859 }
3861 self.__smbCommands = {
3862 # smb.SMB.SMB_COM_FLUSH: self.__smbCommandsHandler.smbComFlush,
3863 smb.SMB.SMB_COM_CREATE_DIRECTORY: self.__smbCommandsHandler.smbComCreateDirectory,
3864 smb.SMB.SMB_COM_DELETE_DIRECTORY: self.__smbCommandsHandler.smbComDeleteDirectory,
3865 smb.SMB.SMB_COM_RENAME: self.__smbCommandsHandler.smbComRename,
3866 smb.SMB.SMB_COM_DELETE: self.__smbCommandsHandler.smbComDelete,
3867 smb.SMB.SMB_COM_NEGOTIATE: self.__smbCommandsHandler.smbComNegotiate,
3868 smb.SMB.SMB_COM_SESSION_SETUP_ANDX: self.__smbCommandsHandler.smbComSessionSetupAndX,
3869 smb.SMB.SMB_COM_LOGOFF_ANDX: self.__smbCommandsHandler.smbComLogOffAndX,
3870 smb.SMB.SMB_COM_TREE_CONNECT_ANDX: self.__smbCommandsHandler.smbComTreeConnectAndX,
3871 smb.SMB.SMB_COM_TREE_DISCONNECT: self.__smbCommandsHandler.smbComTreeDisconnect,
3872 smb.SMB.SMB_COM_ECHO: self.__smbCommandsHandler.smbComEcho,
3873 smb.SMB.SMB_COM_QUERY_INFORMATION: self.__smbCommandsHandler.smbQueryInformation,
3874 smb.SMB.SMB_COM_TRANSACTION2: self.__smbCommandsHandler.smbTransaction2,
3875 smb.SMB.SMB_COM_TRANSACTION: self.__smbCommandsHandler.smbTransaction,
3876 # Not needed for now
3877 smb.SMB.SMB_COM_NT_TRANSACT: self.__smbCommandsHandler.smbNTTransact,
3878 smb.SMB.SMB_COM_QUERY_INFORMATION_DISK: self.__smbCommandsHandler.smbQueryInformationDisk,
3879 smb.SMB.SMB_COM_OPEN_ANDX: self.__smbCommandsHandler.smbComOpenAndX,
3880 smb.SMB.SMB_COM_QUERY_INFORMATION2: self.__smbCommandsHandler.smbComQueryInformation2,
3881 smb.SMB.SMB_COM_READ_ANDX: self.__smbCommandsHandler.smbComReadAndX,
3882 smb.SMB.SMB_COM_READ: self.__smbCommandsHandler.smbComRead,
3883 smb.SMB.SMB_COM_WRITE_ANDX: self.__smbCommandsHandler.smbComWriteAndX,
3884 smb.SMB.SMB_COM_WRITE: self.__smbCommandsHandler.smbComWrite,
3885 smb.SMB.SMB_COM_CLOSE: self.__smbCommandsHandler.smbComClose,
3886 smb.SMB.SMB_COM_LOCKING_ANDX: self.__smbCommandsHandler.smbComLockingAndX,
3887 smb.SMB.SMB_COM_NT_CREATE_ANDX: self.__smbCommandsHandler.smbComNtCreateAndX,
3888 0xFF: self.__smbCommandsHandler.default
3889 }
3891 self.__smb2Ioctls = {
3892 smb2.FSCTL_DFS_GET_REFERRALS: self.__IoctlHandler.fsctlDfsGetReferrals,
3893 # smb2.FSCTL_PIPE_PEEK: self.__IoctlHandler.fsctlPipePeek,
3894 # smb2.FSCTL_PIPE_WAIT: self.__IoctlHandler.fsctlPipeWait,
3895 smb2.FSCTL_PIPE_TRANSCEIVE: self.__IoctlHandler.fsctlPipeTransceive,
3896 # smb2.FSCTL_SRV_COPYCHUNK: self.__IoctlHandler.fsctlSrvCopyChunk,
3897 # smb2.FSCTL_SRV_ENUMERATE_SNAPSHOTS: self.__IoctlHandler.fsctlSrvEnumerateSnapshots,
3898 # smb2.FSCTL_SRV_REQUEST_RESUME_KEY: self.__IoctlHandler.fsctlSrvRequestResumeKey,
3899 # smb2.FSCTL_SRV_READ_HASH: self.__IoctlHandler.fsctlSrvReadHash,
3900 # smb2.FSCTL_SRV_COPYCHUNK_WRITE: self.__IoctlHandler.fsctlSrvCopyChunkWrite,
3901 # smb2.FSCTL_LMR_REQUEST_RESILIENCY: self.__IoctlHandler.fsctlLmrRequestResiliency,
3902 # smb2.FSCTL_QUERY_NETWORK_INTERFACE_INFO: self.__IoctlHandler.fsctlQueryNetworkInterfaceInfo,
3903 # smb2.FSCTL_SET_REPARSE_POINT: self.__IoctlHandler.fsctlSetReparsePoint,
3904 # smb2.FSCTL_DFS_GET_REFERRALS_EX: self.__IoctlHandler.fsctlDfsGetReferralsEx,
3905 # smb2.FSCTL_FILE_LEVEL_TRIM: self.__IoctlHandler.fsctlFileLevelTrim,
3906 smb2.FSCTL_VALIDATE_NEGOTIATE_INFO: self.__IoctlHandler.fsctlValidateNegotiateInfo,
3907 }
3909 self.__smb2Commands = {
3910 smb2.SMB2_NEGOTIATE: self.__smb2CommandsHandler.smb2Negotiate,
3911 smb2.SMB2_SESSION_SETUP: self.__smb2CommandsHandler.smb2SessionSetup,
3912 smb2.SMB2_LOGOFF: self.__smb2CommandsHandler.smb2Logoff,
3913 smb2.SMB2_TREE_CONNECT: self.__smb2CommandsHandler.smb2TreeConnect,
3914 smb2.SMB2_TREE_DISCONNECT: self.__smb2CommandsHandler.smb2TreeDisconnect,
3915 smb2.SMB2_CREATE: self.__smb2CommandsHandler.smb2Create,
3916 smb2.SMB2_CLOSE: self.__smb2CommandsHandler.smb2Close,
3917 smb2.SMB2_FLUSH: self.__smb2CommandsHandler.smb2Flush,
3918 smb2.SMB2_READ: self.__smb2CommandsHandler.smb2Read,
3919 smb2.SMB2_WRITE: self.__smb2CommandsHandler.smb2Write,
3920 smb2.SMB2_LOCK: self.__smb2CommandsHandler.smb2Lock,
3921 smb2.SMB2_IOCTL: self.__smb2CommandsHandler.smb2Ioctl,
3922 smb2.SMB2_CANCEL: self.__smb2CommandsHandler.smb2Cancel,
3923 smb2.SMB2_ECHO: self.__smb2CommandsHandler.smb2Echo,
3924 smb2.SMB2_QUERY_DIRECTORY: self.__smb2CommandsHandler.smb2QueryDirectory,
3925 smb2.SMB2_CHANGE_NOTIFY: self.__smb2CommandsHandler.smb2ChangeNotify,
3926 smb2.SMB2_QUERY_INFO: self.__smb2CommandsHandler.smb2QueryInfo,
3927 smb2.SMB2_SET_INFO: self.__smb2CommandsHandler.smb2SetInfo,
3928 # smb2.SMB2_OPLOCK_BREAK: self.__smb2CommandsHandler.smb2SessionSetup,
3929 0xFF: self.__smb2CommandsHandler.default
3930 }
3932 # List of active connections
3933 self.__activeConnections = {}
3935 def getIoctls(self):
3936 return self.__smb2Ioctls
3938 def getCredentials(self):
3939 return self.__credentials
3941 def removeConnection(self, name):
3942 try:
3943 del (self.__activeConnections[name])
3944 except:
3945 pass
3946 self.log("Remaining connections %s" % list(self.__activeConnections.keys()))
3948 def addConnection(self, name, ip, port):
3949 self.__activeConnections[name] = {}
3950 # Let's init with some know stuff we will need to have
3951 # TODO: Document what's in there
3952 # print "Current Connections", self.__activeConnections.keys()
3953 self.__activeConnections[name]['PacketNum'] = 0
3954 self.__activeConnections[name]['ClientIP'] = ip
3955 self.__activeConnections[name]['ClientPort'] = port
3956 self.__activeConnections[name]['Uid'] = 0
3957 self.__activeConnections[name]['ConnectedShares'] = {}
3958 self.__activeConnections[name]['OpenedFiles'] = {}
3959 # SID results for findfirst2
3960 self.__activeConnections[name]['SIDs'] = {}
3961 self.__activeConnections[name]['LastRequest'] = {}
3962 self.__activeConnections[name]['SignatureEnabled'] = False
3963 self.__activeConnections[name]['SigningChallengeResponse'] = ''
3964 self.__activeConnections[name]['SigningSessionKey'] = b''
3965 self.__activeConnections[name]['Authenticated'] = False
3967 def getActiveConnections(self):
3968 return self.__activeConnections
3970 def setConnectionData(self, connId, data):
3971 self.__activeConnections[connId] = data
3972 # print "setConnectionData"
3973 # print self.__activeConnections
3975 def getConnectionData(self, connId, checkStatus=True):
3976 conn = self.__activeConnections[connId]
3977 if checkStatus is True:
3978 if ('Authenticated' in conn) is not True:
3979 # Can't keep going further
3980 raise Exception("User not Authenticated!")
3981 return conn
3983 def getRegisteredNamedPipes(self):
3984 return self.__registeredNamedPipes
3986 def registerNamedPipe(self, pipeName, address):
3987 self.__registeredNamedPipes[str(pipeName)] = address
3988 return True
3990 def unregisterNamedPipe(self, pipeName):
3991 if pipeName in self.__registeredNamedPipes:
3992 del (self.__registeredNamedPipes[str(pipeName)])
3993 return True
3994 return False
3996 def unregisterTransaction(self, transCommand):
3997 if transCommand in self.__smbTransCommands:
3998 del (self.__smbTransCommands[transCommand])
4000 def hookTransaction(self, transCommand, callback):
4001 # If you call this function, callback will replace
4002 # the current Transaction sub command.
4003 # (don't get confused with the Transaction smbCommand)
4004 # If the transaction sub command doesn't not exist, it is added
4005 # If the transaction sub command exists, it returns the original function # replaced
4006 #
4007 # callback MUST be declared as:
4008 # callback(connId, smbServer, recvPacket, parameters, data, maxDataCount=0)
4009 #
4010 # WHERE:
4011 #
4012 # connId : the connection Id, used to grab/update information about
4013 # the current connection
4014 # smbServer : the SMBServer instance available for you to ask
4015 # configuration data
4016 # recvPacket : the full SMBPacket that triggered this command
4017 # parameters : the transaction parameters
4018 # data : the transaction data
4019 # maxDataCount: the max amount of data that can be transferred agreed
4020 # with the client
4021 #
4022 # and MUST return:
4023 # respSetup, respParameters, respData, errorCode
4024 #
4025 # WHERE:
4026 #
4027 # respSetup: the setup response of the transaction
4028 # respParameters: the parameters response of the transaction
4029 # respData: the data response of the transaction
4030 # errorCode: the NT error code
4032 if transCommand in self.__smbTransCommands:
4033 originalCommand = self.__smbTransCommands[transCommand]
4034 else:
4035 originalCommand = None
4037 self.__smbTransCommands[transCommand] = callback
4038 return originalCommand
4040 def unregisterTransaction2(self, transCommand):
4041 if transCommand in self.__smbTrans2Commands:
4042 del (self.__smbTrans2Commands[transCommand])
4044 def hookTransaction2(self, transCommand, callback):
4045 # Here we should add to __smbTrans2Commands
4046 # Same description as Transaction
4047 if transCommand in self.__smbTrans2Commands:
4048 originalCommand = self.__smbTrans2Commands[transCommand]
4049 else:
4050 originalCommand = None
4052 self.__smbTrans2Commands[transCommand] = callback
4053 return originalCommand
4055 def unregisterNTTransaction(self, transCommand):
4056 if transCommand in self.__smbNTTransCommands:
4057 del (self.__smbNTTransCommands[transCommand])
4059 def hookNTTransaction(self, transCommand, callback):
4060 # Here we should add to __smbNTTransCommands
4061 # Same description as Transaction
4062 if transCommand in self.__smbNTTransCommands:
4063 originalCommand = self.__smbNTTransCommands[transCommand]
4064 else:
4065 originalCommand = None
4067 self.__smbNTTransCommands[transCommand] = callback
4068 return originalCommand
4070 def unregisterSmbCommand(self, smbCommand):
4071 if smbCommand in self.__smbCommands:
4072 del (self.__smbCommands[smbCommand])
4074 def hookSmbCommand(self, smbCommand, callback):
4075 # Here we should add to self.__smbCommands
4076 # If you call this function, callback will replace
4077 # the current smbCommand.
4078 # If smbCommand doesn't not exist, it is added
4079 # If SMB command exists, it returns the original function replaced
4080 #
4081 # callback MUST be declared as:
4082 # callback(connId, smbServer, SMBCommand, recvPacket)
4083 #
4084 # WHERE:
4085 #
4086 # connId : the connection Id, used to grab/update information about
4087 # the current connection
4088 # smbServer : the SMBServer instance available for you to ask
4089 # configuration data
4090 # SMBCommand: the SMBCommand itself, with its data and parameters.
4091 # Check smb.py:SMBCommand() for a reference
4092 # recvPacket: the full SMBPacket that triggered this command
4093 #
4094 # and MUST return:
4095 # <list of respSMBCommands>, <list of packets>, errorCode
4096 # <list of packets> has higher preference over commands, in case you
4097 # want to change the whole packet
4098 # errorCode: the NT error code
4099 #
4100 # For SMB_COM_TRANSACTION2, SMB_COM_TRANSACTION and SMB_COM_NT_TRANSACT
4101 # the callback function is slightly different:
4102 #
4103 # callback(connId, smbServer, SMBCommand, recvPacket, transCommands)
4104 #
4105 # WHERE:
4106 #
4107 # transCommands: a list of transaction subcommands already registered
4108 #
4110 if smbCommand in self.__smbCommands:
4111 originalCommand = self.__smbCommands[smbCommand]
4112 else:
4113 originalCommand = None
4115 self.__smbCommands[smbCommand] = callback
4116 return originalCommand
4118 def unregisterSmb2Command(self, smb2Command):
4119 if smb2Command in self.__smb2Commands:
4120 del (self.__smb2Commands[smb2Command])
4122 def hookSmb2Command(self, smb2Command, callback):
4123 if smb2Command in self.__smb2Commands:
4124 originalCommand = self.__smb2Commands[smb2Command]
4125 else:
4126 originalCommand = None
4128 self.__smb2Commands[smb2Command] = callback
4129 return originalCommand
4131 def log(self, msg, level=logging.INFO):
4132 self.__log.log(level, msg)
4134 def getServerName(self):
4135 return self.__serverName
4137 def getServerOS(self):
4138 return self.__serverOS
4140 def getServerDomain(self):
4141 return self.__serverDomain
4143 def getSMBChallenge(self):
4144 return self.__challenge
4146 def getServerConfig(self):
4147 return self.__serverConfig
4149 def setServerConfig(self, config):
4150 self.__serverConfig = config
4152 def getJTRdumpPath(self):
4153 return self.__jtr_dump_path
4155 def verify_request(self, request, client_address):
4156 # TODO: Control here the max amount of processes we want to launch
4157 # returning False, closes the connection
4158 return True
4160 def signSMBv1(self, connData, packet, signingSessionKey, signingChallengeResponse):
4161 # This logic MUST be applied for messages sent in response to any of the higher-layer actions and in
4162 # compliance with the message sequencing rules.
4163 # * The client or server that sends the message MUST provide the 32-bit sequence number for this
4164 # message, as specified in sections 3.2.4.1 and 3.3.4.1.
4165 # * The SMB_FLAGS2_SMB_SECURITY_SIGNATURE flag in the header MUST be set.
4166 # * To generate the signature, a 32-bit sequence number is copied into the
4167 # least significant 32 bits of the SecuritySignature field and the remaining
4168 # 4 bytes are set to 0x00.
4169 # * The MD5 algorithm, as specified in [RFC1321], MUST be used to generate a hash of the SMB
4170 # message from the start of the SMB Header, which is defined as follows.
4171 # CALL MD5Init( md5context )
4172 # CALL MD5Update( md5context, Connection.SigningSessionKey )
4173 # CALL MD5Update( md5context, Connection.SigningChallengeResponse )
4174 # CALL MD5Update( md5context, SMB message )
4175 # CALL MD5Final( digest, md5context )
4176 # SET signature TO the first 8 bytes of the digest
4177 # The resulting 8-byte signature MUST be copied into the SecuritySignature field of the SMB Header,
4178 # after which the message can be transmitted.
4180 # print "seq(%d) signingSessionKey %r, signingChallengeResponse %r" % (connData['SignSequenceNumber'], signingSessionKey, signingChallengeResponse)
4181 packet['SecurityFeatures'] = struct.pack('<q', connData['SignSequenceNumber'])
4182 # Sign with the sequence
4183 m = hashlib.md5()
4184 m.update(signingSessionKey)
4185 m.update(signingChallengeResponse)
4186 if hasattr(packet, 'getData'):
4187 m.update(packet.getData())
4188 else:
4189 m.update(packet)
4190 # Replace sequence with acual hash
4191 packet['SecurityFeatures'] = m.digest()[:8]
4192 connData['SignSequenceNumber'] += 2
4194 def signSMBv2(self, packet, signingSessionKey):
4195 packet['Signature'] = b'\x00' * 16
4196 packet['Flags'] |= smb2.SMB2_FLAGS_SIGNED
4197 signature = hmac.new(signingSessionKey, packet.getData(), hashlib.sha256).digest()
4198 packet['Signature'] = signature[:16]
4199 # print "%s" % packet['Signature'].encode('hex')
4201 def processRequest(self, connId, data):
4203 # TODO: Process batched commands.
4204 isSMB2 = False
4205 SMBCommand = None
4206 try:
4207 packet = smb.NewSMBPacket(data=data)
4208 SMBCommand = smb.SMBCommand(packet['Data'][0])
4209 except:
4210 # Maybe a SMB2 packet?
4211 packet = smb2.SMB2Packet(data=data)
4212 connData = self.getConnectionData(connId, False)
4213 self.signSMBv2(packet, connData['SigningSessionKey'])
4214 isSMB2 = True
4216 connData = self.getConnectionData(connId, False)
4218 # We might have compound requests
4219 compoundedPacketsResponse = []
4220 compoundedPackets = []
4221 try:
4222 # Search out list of implemented commands
4223 # We provide them with:
4224 # connId : representing the data for this specific connection
4225 # self : the SMBSERVER if they want to ask data to it
4226 # SMBCommand : the SMBCommand they are expecting to process
4227 # packet : the received packet itself, in case they need more data than the actual command
4228 # Only for Transactions
4229 # transCommand: a list of transaction subcommands
4230 # We expect to get:
4231 # respCommands: a list of answers for the commands processed
4232 # respPacket : if the commands chose to directly craft packet/s, we use this and not the previous
4233 # this MUST be a list
4234 # errorCode : self explanatory
4235 if isSMB2 is False:
4236 # Is the client authenticated already?
4237 if connData['Authenticated'] is False and packet['Command'] not in (
4238 smb.SMB.SMB_COM_NEGOTIATE, smb.SMB.SMB_COM_SESSION_SETUP_ANDX):
4239 # Nope.. in that case he should only ask for a few commands, if not throw him out.
4240 errorCode = STATUS_ACCESS_DENIED
4241 respPackets = None
4242 respCommands = [smb.SMBCommand(packet['Command'])]
4243 else:
4244 if packet['Command'] == smb.SMB.SMB_COM_TRANSACTION2:
4245 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4246 connId,
4247 self,
4248 SMBCommand,
4249 packet,
4250 self.__smbTrans2Commands)
4251 elif packet['Command'] == smb.SMB.SMB_COM_NT_TRANSACT:
4252 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4253 connId,
4254 self,
4255 SMBCommand,
4256 packet,
4257 self.__smbNTTransCommands)
4258 elif packet['Command'] == smb.SMB.SMB_COM_TRANSACTION:
4259 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4260 connId,
4261 self,
4262 SMBCommand,
4263 packet,
4264 self.__smbTransCommands)
4265 else:
4266 if packet['Command'] in self.__smbCommands:
4267 if self.__SMB2Support is True:
4268 if packet['Command'] == smb.SMB.SMB_COM_NEGOTIATE:
4269 try:
4270 respCommands, respPackets, errorCode = self.__smb2Commands[smb2.SMB2_NEGOTIATE](
4271 connId, self, packet, True)
4272 isSMB2 = True
4273 except Exception as e:
4274 import traceback
4275 traceback.print_exc()
4276 self.log('SMB2_NEGOTIATE: %s' % e, logging.ERROR)
4277 # If something went wrong, let's fallback to SMB1
4278 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4279 connId,
4280 self,
4281 SMBCommand,
4282 packet)
4283 # self.__SMB2Support = False
4284 pass
4285 else:
4286 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4287 connId,
4288 self,
4289 SMBCommand,
4290 packet)
4291 else:
4292 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
4293 connId,
4294 self,
4295 SMBCommand,
4296 packet)
4297 else:
4298 respCommands, respPackets, errorCode = self.__smbCommands[255](connId, self, SMBCommand,
4299 packet)
4301 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))
4302 compoundedPackets.append(packet)
4304 else:
4305 # Is the client authenticated already?
4306 if connData['Authenticated'] is False and packet['Command'] not in (
4307 smb2.SMB2_NEGOTIATE, smb2.SMB2_SESSION_SETUP):
4308 # Nope.. in that case he should only ask for a few commands, if not throw him out.
4309 errorCode = STATUS_ACCESS_DENIED
4310 respPackets = None
4311 respCommands = ['']
4312 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))
4313 compoundedPackets.append(packet)
4314 else:
4315 done = False
4316 while not done:
4317 if packet['Command'] in self.__smb2Commands:
4318 if self.__SMB2Support is True:
4319 respCommands, respPackets, errorCode = self.__smb2Commands[packet['Command']](
4320 connId,
4321 self,
4322 packet)
4323 else:
4324 respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet)
4325 else:
4326 respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet)
4327 # Let's store the result for this compounded packet
4328 compoundedPacketsResponse.append((respCommands, respPackets, errorCode))
4329 compoundedPackets.append(packet)
4330 if packet['NextCommand'] != 0:
4331 data = data[packet['NextCommand']:]
4332 packet = smb2.SMB2Packet(data=data)
4333 else:
4334 done = True
4336 except Exception as e:
4337 # import traceback
4338 # traceback.print_exc()
4339 # Something wen't wrong, defaulting to Bad user ID
4340 self.log('processRequest (0x%x,%s)' % (packet['Command'], e), logging.ERROR)
4341 raise
4343 # We prepare the response packet to commands don't need to bother about that.
4344 connData = self.getConnectionData(connId, False)
4346 # Force reconnection loop.. This is just a test.. client will send me back credentials :)
4347 # connData['PacketNum'] += 1
4348 # if connData['PacketNum'] == 15:
4349 # connData['PacketNum'] = 0
4350 # # Something wen't wrong, defaulting to Bad user ID
4351 # self.log('Sending BAD USER ID!', logging.ERROR)
4352 # #raise
4353 # packet['Flags1'] |= smb.SMB.FLAGS1_REPLY
4354 # packet['Flags2'] = 0
4355 # errorCode = STATUS_SMB_BAD_UID
4356 # packet['ErrorCode'] = errorCode >> 16
4357 # packet['ErrorClass'] = errorCode & 0xff
4358 # return [packet]
4360 self.setConnectionData(connId, connData)
4362 packetsToSend = []
4363 for packetNum in range(len(compoundedPacketsResponse)):
4364 respCommands, respPackets, errorCode = compoundedPacketsResponse[packetNum]
4365 packet = compoundedPackets[packetNum]
4366 if respPackets is None:
4367 for respCommand in respCommands:
4368 if isSMB2 is False:
4369 respPacket = smb.NewSMBPacket()
4370 respPacket['Flags1'] = smb.SMB.FLAGS1_REPLY
4372 # TODO this should come from a per session configuration
4373 respPacket[
4374 'Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \
4375 packet['Flags2'] & smb.SMB.FLAGS2_UNICODE
4376 # respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES
4377 # respPacket['Flags1'] = 0x98
4378 # respPacket['Flags2'] = 0xc807
4380 respPacket['Tid'] = packet['Tid']
4381 respPacket['Mid'] = packet['Mid']
4382 respPacket['Pid'] = packet['Pid']
4383 respPacket['Uid'] = connData['Uid']
4385 respPacket['ErrorCode'] = errorCode >> 16
4386 respPacket['_reserved'] = errorCode >> 8 & 0xff
4387 respPacket['ErrorClass'] = errorCode & 0xff
4388 respPacket.addCommand(respCommand)
4390 if connData['SignatureEnabled']:
4391 respPacket['Flags2'] |= smb.SMB.FLAGS2_SMB_SECURITY_SIGNATURE
4392 self.signSMBv1(connData, respPacket, connData['SigningSessionKey'],
4393 connData['SigningChallengeResponse'])
4395 packetsToSend.append(respPacket)
4396 else:
4397 respPacket = smb2.SMB2Packet()
4398 respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR
4399 if packetNum > 0:
4400 respPacket['Flags'] |= smb2.SMB2_FLAGS_RELATED_OPERATIONS
4401 respPacket['Status'] = errorCode
4402 respPacket['CreditRequestResponse'] = packet['CreditRequestResponse']
4403 respPacket['Command'] = packet['Command']
4404 respPacket['CreditCharge'] = packet['CreditCharge']
4405 # respPacket['CreditCharge'] = 0
4406 respPacket['Reserved'] = packet['Reserved']
4407 respPacket['SessionID'] = connData['Uid']
4408 respPacket['MessageID'] = packet['MessageID']
4409 respPacket['TreeID'] = packet['TreeID']
4410 if hasattr(respCommand, 'getData'):
4411 respPacket['Data'] = respCommand.getData()
4412 else:
4413 respPacket['Data'] = str(respCommand)
4415 if connData['SignatureEnabled']:
4416 self.signSMBv2(respPacket, connData['SigningSessionKey'])
4418 packetsToSend.append(respPacket)
4419 else:
4420 # The SMBCommand took care of building the packet
4421 packetsToSend = respPackets
4423 if isSMB2 is True:
4424 # Let's build a compound answer
4425 finalData = b''
4426 i = 0
4427 for i in range(len(packetsToSend) - 1):
4428 packet = packetsToSend[i]
4429 # Align to 8-bytes
4430 padLen = (8 - (len(packet) % 8)) % 8
4431 packet['NextCommand'] = len(packet) + padLen
4432 if hasattr(packet, 'getData'):
4433 finalData += packet.getData() + padLen * b'\x00'
4434 else:
4435 finalData += packet + padLen * b'\x00'
4437 # Last one
4438 if hasattr(packetsToSend[len(packetsToSend) - 1], 'getData'):
4439 finalData += packetsToSend[len(packetsToSend) - 1].getData()
4440 else:
4441 finalData += packetsToSend[len(packetsToSend) - 1]
4442 packetsToSend = [finalData]
4444 # We clear the compound requests
4445 connData['LastRequest'] = {}
4447 return packetsToSend
4449 def processConfigFile(self, configFile=None):
4450 # TODO: Do a real config parser
4451 if self.__serverConfig is None:
4452 if configFile is None:
4453 configFile = 'smb.conf'
4454 self.__serverConfig = configparser.ConfigParser()
4455 self.__serverConfig.read(configFile)
4457 self.__serverName = self.__serverConfig.get('global', 'server_name')
4458 self.__serverOS = self.__serverConfig.get('global', 'server_os')
4459 self.__serverDomain = self.__serverConfig.get('global', 'server_domain')
4460 self.__logFile = self.__serverConfig.get('global', 'log_file')
4461 if self.__serverConfig.has_option('global', 'challenge'):
4462 self.__challenge = unhexlify(self.__serverConfig.get('global', 'challenge'))
4463 else:
4464 self.__challenge = b'A' * 16
4466 if self.__serverConfig.has_option("global", "jtr_dump_path"):
4467 self.__jtr_dump_path = self.__serverConfig.get("global", "jtr_dump_path")
4469 if self.__serverConfig.has_option("global", "SMB2Support"):
4470 self.__SMB2Support = self.__serverConfig.getboolean("global", "SMB2Support")
4471 else:
4472 self.__SMB2Support = False
4474 if self.__logFile != 'None':
4475 logging.basicConfig(filename=self.__logFile,
4476 level=logging.DEBUG,
4477 format="%(asctime)s: %(levelname)s: %(message)s",
4478 datefmt='%m/%d/%Y %I:%M:%S %p')
4479 self.__log = LOG
4481 # Process the credentials
4482 credentials_fname = self.__serverConfig.get('global', 'credentials_file')
4483 if credentials_fname != "":
4484 cred = open(credentials_fname)
4485 line = cred.readline()
4486 while line:
4487 name, uid, lmhash, nthash = line.split(':')
4488 self.__credentials[name.lower()] = (uid, lmhash, nthash.strip('\r\n'))
4489 line = cred.readline()
4490 cred.close()
4491 self.log('Config file parsed')
4493 def addCredential(self, name, uid, lmhash, nthash):
4494 # If we have hashes, normalize them
4495 if lmhash != '' or nthash != '':
4496 if len(lmhash) % 2:
4497 lmhash = '0%s' % lmhash
4498 if len(nthash) % 2:
4499 nthash = '0%s' % nthash
4500 try: # just in case they were converted already
4501 lmhash = a2b_hex(lmhash)
4502 nthash = a2b_hex(nthash)
4503 except:
4504 pass
4505 self.__credentials[name.lower()] = (uid, lmhash, nthash)
4508# For windows platforms, opening a directory is not an option, so we set a void FD
4509VOID_FILE_DESCRIPTOR = -1
4510PIPE_FILE_DESCRIPTOR = -2
4512######################################################################
4513# HELPER CLASSES
4514######################################################################
4516from impacket.dcerpc.v5.rpcrt import DCERPCServer
4517from impacket.dcerpc.v5.dtypes import NULL
4518from impacket.dcerpc.v5.srvs import NetrShareEnum, NetrShareEnumResponse, SHARE_INFO_1, NetrServerGetInfo, \
4519 NetrServerGetInfoResponse, NetrShareGetInfo, NetrShareGetInfoResponse
4520from impacket.dcerpc.v5.wkst import NetrWkstaGetInfo, NetrWkstaGetInfoResponse
4521from impacket.system_errors import ERROR_INVALID_LEVEL
4524class WKSTServer(DCERPCServer):
4525 def __init__(self):
4526 DCERPCServer.__init__(self)
4527 self.wkssvcCallBacks = {
4528 0: self.NetrWkstaGetInfo,
4529 }
4530 self.addCallbacks(('6BFFD098-A112-3610-9833-46C3F87E345A', '1.0'), '\\PIPE\\wkssvc', self.wkssvcCallBacks)
4532 def NetrWkstaGetInfo(self, data):
4533 request = NetrWkstaGetInfo(data)
4534 self.log("NetrWkstaGetInfo Level: %d" % request['Level'])
4536 answer = NetrWkstaGetInfoResponse()
4538 if request['Level'] not in (100, 101):
4539 answer['ErrorCode'] = ERROR_INVALID_LEVEL
4540 return answer
4542 answer['WkstaInfo']['tag'] = request['Level']
4544 if request['Level'] == 100:
4545 # Windows. Decimal value 500.
4546 answer['WkstaInfo']['WkstaInfo100']['wki100_platform_id'] = 0x000001F4
4547 answer['WkstaInfo']['WkstaInfo100']['wki100_computername'] = NULL
4548 answer['WkstaInfo']['WkstaInfo100']['wki100_langroup'] = NULL
4549 answer['WkstaInfo']['WkstaInfo100']['wki100_ver_major'] = 5
4550 answer['WkstaInfo']['WkstaInfo100']['wki100_ver_minor'] = 0
4551 else:
4552 # Windows. Decimal value 500.
4553 answer['WkstaInfo']['WkstaInfo101']['wki101_platform_id'] = 0x000001F4
4554 answer['WkstaInfo']['WkstaInfo101']['wki101_computername'] = NULL
4555 answer['WkstaInfo']['WkstaInfo101']['wki101_langroup'] = NULL
4556 answer['WkstaInfo']['WkstaInfo101']['wki101_ver_major'] = 5
4557 answer['WkstaInfo']['WkstaInfo101']['wki101_ver_minor'] = 0
4558 answer['WkstaInfo']['WkstaInfo101']['wki101_lanroot'] = NULL
4560 return answer
4563class SRVSServer(DCERPCServer):
4564 def __init__(self):
4565 DCERPCServer.__init__(self)
4567 self._shares = {}
4568 self.__serverConfig = None
4569 self.__logFile = None
4571 self.srvsvcCallBacks = {
4572 15: self.NetrShareEnum,
4573 16: self.NetrShareGetInfo,
4574 21: self.NetrServerGetInfo,
4575 }
4577 self.addCallbacks(('4B324FC8-1670-01D3-1278-5A47BF6EE188', '3.0'), '\\PIPE\\srvsvc', self.srvsvcCallBacks)
4579 def setServerConfig(self, config):
4580 self.__serverConfig = config
4582 def processConfigFile(self, configFile=None):
4583 if configFile is not None:
4584 self.__serverConfig = configparser.ConfigParser()
4585 self.__serverConfig.read(configFile)
4586 sections = self.__serverConfig.sections()
4587 # Let's check the log file
4588 self.__logFile = self.__serverConfig.get('global', 'log_file')
4589 if self.__logFile != 'None':
4590 logging.basicConfig(filename=self.__logFile,
4591 level=logging.DEBUG,
4592 format="%(asctime)s: %(levelname)s: %(message)s",
4593 datefmt='%m/%d/%Y %I:%M:%S %p')
4595 # Remove the global one
4596 del (sections[sections.index('global')])
4597 self._shares = {}
4598 for i in sections:
4599 self._shares[i] = dict(self.__serverConfig.items(i))
4601 def NetrShareGetInfo(self, data):
4602 request = NetrShareGetInfo(data)
4603 self.log("NetrGetShareInfo Level: %d" % request['Level'])
4605 s = request['NetName'][:-1].upper()
4606 answer = NetrShareGetInfoResponse()
4607 if s in self._shares:
4608 share = self._shares[s]
4610 answer['InfoStruct']['tag'] = 1
4611 answer['InfoStruct']['ShareInfo1']['shi1_netname'] = s + '\x00'
4612 answer['InfoStruct']['ShareInfo1']['shi1_type'] = share['share type']
4613 answer['InfoStruct']['ShareInfo1']['shi1_remark'] = share['comment'] + '\x00'
4614 answer['ErrorCode'] = 0
4615 else:
4616 answer['InfoStruct']['tag'] = 1
4617 answer['InfoStruct']['ShareInfo1'] = NULL
4618 answer['ErrorCode'] = 0x0906 # WERR_NET_NAME_NOT_FOUND
4620 return answer
4622 def NetrServerGetInfo(self, data):
4623 request = NetrServerGetInfo(data)
4624 self.log("NetrServerGetInfo Level: %d" % request['Level'])
4625 answer = NetrServerGetInfoResponse()
4626 answer['InfoStruct']['tag'] = 101
4627 # PLATFORM_ID_NT = 500
4628 answer['InfoStruct']['ServerInfo101']['sv101_platform_id'] = 500
4629 answer['InfoStruct']['ServerInfo101']['sv101_name'] = request['ServerName']
4630 # Windows 7 = 6.1
4631 answer['InfoStruct']['ServerInfo101']['sv101_version_major'] = 6
4632 answer['InfoStruct']['ServerInfo101']['sv101_version_minor'] = 1
4633 # Workstation = 1
4634 answer['InfoStruct']['ServerInfo101']['sv101_type'] = 1
4635 answer['InfoStruct']['ServerInfo101']['sv101_comment'] = NULL
4636 answer['ErrorCode'] = 0
4637 return answer
4639 def NetrShareEnum(self, data):
4640 request = NetrShareEnum(data)
4641 self.log("NetrShareEnum Level: %d" % request['InfoStruct']['Level'])
4642 shareEnum = NetrShareEnumResponse()
4643 shareEnum['InfoStruct']['Level'] = 1
4644 shareEnum['InfoStruct']['ShareInfo']['tag'] = 1
4645 shareEnum['TotalEntries'] = len(self._shares)
4646 shareEnum['InfoStruct']['ShareInfo']['Level1']['EntriesRead'] = len(self._shares)
4647 shareEnum['ErrorCode'] = 0
4649 for i in self._shares:
4650 shareInfo = SHARE_INFO_1()
4651 shareInfo['shi1_netname'] = i + '\x00'
4652 shareInfo['shi1_type'] = self._shares[i]['share type']
4653 shareInfo['shi1_remark'] = self._shares[i]['comment'] + '\x00'
4654 shareEnum['InfoStruct']['ShareInfo']['Level1']['Buffer'].append(shareInfo)
4656 return shareEnum
4659class SimpleSMBServer:
4660 """
4661 SimpleSMBServer class - Implements a simple, customizable SMB Server
4663 :param string listenAddress: the address you want the server to listen on
4664 :param integer listenPort: the port number you want the server to listen on
4665 :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
4666 """
4668 def __init__(self, listenAddress='0.0.0.0', listenPort=445, configFile=''):
4669 if configFile != '':
4670 self.__server = SMBSERVER((listenAddress, listenPort))
4671 self.__server.processConfigFile(configFile)
4672 self.__smbConfig = None
4673 else:
4674 # Here we write a mini config for the server
4675 self.__smbConfig = configparser.ConfigParser()
4676 self.__smbConfig.add_section('global')
4677 self.__smbConfig.set('global', 'server_name',
4678 ''.join([random.choice(string.ascii_letters) for _ in range(8)]))
4679 self.__smbConfig.set('global', 'server_os', ''.join([random.choice(string.ascii_letters) for _ in range(8)])
4680 )
4681 self.__smbConfig.set('global', 'server_domain',
4682 ''.join([random.choice(string.ascii_letters) for _ in range(8)])
4683 )
4684 self.__smbConfig.set('global', 'log_file', 'None')
4685 self.__smbConfig.set('global', 'rpc_apis', 'yes')
4686 self.__smbConfig.set('global', 'credentials_file', '')
4687 self.__smbConfig.set('global', 'challenge', "A" * 16)
4689 # IPC always needed
4690 self.__smbConfig.add_section('IPC$')
4691 self.__smbConfig.set('IPC$', 'comment', '')
4692 self.__smbConfig.set('IPC$', 'read only', 'yes')
4693 self.__smbConfig.set('IPC$', 'share type', '3')
4694 self.__smbConfig.set('IPC$', 'path', '')
4695 self.__server = SMBSERVER((listenAddress, listenPort), config_parser=self.__smbConfig)
4696 self.__server.processConfigFile()
4698 # Now we have to register the MS-SRVS server. This specially important for
4699 # Windows 7+ and Mavericks clients since they WON'T (specially OSX)
4700 # ask for shares using MS-RAP.
4702 self.__srvsServer = SRVSServer()
4703 self.__srvsServer.daemon = True
4704 self.__wkstServer = WKSTServer()
4705 self.__wkstServer.daemon = True
4706 self.__server.registerNamedPipe('srvsvc', ('127.0.0.1', self.__srvsServer.getListenPort()))
4707 self.__server.registerNamedPipe('wkssvc', ('127.0.0.1', self.__wkstServer.getListenPort()))
4709 def start(self):
4710 self.__srvsServer.start()
4711 self.__wkstServer.start()
4712 self.__server.serve_forever()
4714 def registerNamedPipe(self, pipeName, address):
4715 return self.__server.registerNamedPipe(pipeName, address)
4717 def unregisterNamedPipe(self, pipeName):
4718 return self.__server.unregisterNamedPipe(pipeName)
4720 def getRegisteredNamedPipes(self):
4721 return self.__server.getRegisteredNamedPipes()
4723 def addShare(self, shareName, sharePath, shareComment='', shareType='0', readOnly='no'):
4724 share = shareName.upper()
4725 self.__smbConfig.add_section(share)
4726 self.__smbConfig.set(share, 'comment', shareComment)
4727 self.__smbConfig.set(share, 'read only', readOnly)
4728 self.__smbConfig.set(share, 'share type', shareType)
4729 self.__smbConfig.set(share, 'path', sharePath)
4730 self.__server.setServerConfig(self.__smbConfig)
4731 self.__srvsServer.setServerConfig(self.__smbConfig)
4732 self.__server.processConfigFile()
4733 self.__srvsServer.processConfigFile()
4735 def removeShare(self, shareName):
4736 self.__smbConfig.remove_section(shareName.upper())
4737 self.__server.setServerConfig(self.__smbConfig)
4738 self.__srvsServer.setServerConfig(self.__smbConfig)
4739 self.__server.processConfigFile()
4740 self.__srvsServer.processConfigFile()
4742 def setSMBChallenge(self, challenge):
4743 if challenge != '':
4744 self.__smbConfig.set('global', 'challenge', challenge)
4745 self.__server.setServerConfig(self.__smbConfig)
4746 self.__server.processConfigFile()
4748 def setLogFile(self, logFile):
4749 self.__smbConfig.set('global', 'log_file', logFile)
4750 self.__server.setServerConfig(self.__smbConfig)
4751 self.__server.processConfigFile()
4753 def setCredentialsFile(self, logFile):
4754 self.__smbConfig.set('global', 'credentials_file', logFile)
4755 self.__server.setServerConfig(self.__smbConfig)
4756 self.__server.processConfigFile()
4758 def addCredential(self, name, uid, lmhash, nthash):
4759 self.__server.addCredential(name, uid, lmhash, nthash)
4761 def setSMB2Support(self, value):
4762 if value is True:
4763 self.__smbConfig.set("global", "SMB2Support", "True")
4764 else:
4765 self.__smbConfig.set("global", "SMB2Support", "False")
4766 self.__server.setServerConfig(self.__smbConfig)
4767 self.__server.processConfigFile()