Coverage for /private/tmp/im/impacket/impacket/smbserver.py : 5%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # Author: Alberto Solino (@agsolino) # # TODO: # [-] Functions should return NT error codes # [-] Handling errors in all situations, right now it's just raising exceptions. # [*] Standard authentication support # [ ] Organize the connectionData stuff # [*] Add capability to send a bad user ID if the user is not authenticated, # right now you can ask for any command without actually being authenticated # [ ] PATH TRAVERSALS EVERYWHERE.. BE WARNED! # [ ] Check the credentials.. now we're just letting everybody to log in. # [ ] Check error situation (now many places assume the right data is coming) # [ ] Implement IPC to the main process so the connectionData is on a single place # [ ] Hence.. implement locking # estamos en la B
# For signing STATUS_FILE_CLOSED, STATUS_MORE_PROCESSING_REQUIRED, STATUS_OBJECT_PATH_NOT_FOUND, STATUS_DIRECTORY_NOT_EMPTY, \ STATUS_FILE_IS_A_DIRECTORY, STATUS_NOT_IMPLEMENTED, STATUS_INVALID_HANDLE, STATUS_OBJECT_NAME_COLLISION, \ STATUS_NO_SUCH_FILE, STATUS_CANCELLED, STATUS_OBJECT_NAME_NOT_FOUND, STATUS_SUCCESS, STATUS_ACCESS_DENIED, \ STATUS_NOT_SUPPORTED, STATUS_INVALID_DEVICE_REQUEST, STATUS_FS_DRIVER_REQUIRED, STATUS_INVALID_INFO_CLASS, \ STATUS_LOGON_FAILURE
# Setting LOG to current's module name
# These ones not defined in nt_errors
# Utility functions # and general functions. # There are some common functions that can be accessed from more than one SMB # command (or either TRANSACTION). That's why I'm putting them here # TODO: Return NT ERROR Codes
# Let's calculate the NTLMv2 Response
responseKeyNT = ntlm.NTOWFv2(identity, '', authenticateMessage['domain_name'].decode('utf-16le'), nthash) responseKeyLM = ntlm.LMOWFv2(identity, '', authenticateMessage['domain_name'].decode('utf-16le'), lmhash)
ntProofStr = authenticateMessage['ntlm'][:16] temp = authenticateMessage['ntlm'][16:] ntProofStr2 = ntlm.hmac_md5(responseKeyNT, serverChallenge + temp) lmChallengeResponse = authenticateMessage['lanman'] sessionBaseKey = ntlm.hmac_md5(responseKeyNT, ntProofStr)
responseFlags = type1['flags']
# Let's check the return flags if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) == 0: # No extended session security, taking it out responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_128) == 0: # No support for 128 key len, taking it out responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_128 if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH) == 0: # No key exchange supported, taking it out responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_SEAL) == 0: # No sign available, taking it out responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_SEAL if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_SIGN) == 0: # No sign available, taking it out responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_SIGN if (ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN) == 0: # No sign available, taking it out responseFlags &= 0xffffffff ^ ntlm.NTLMSSP_NEGOTIATE_ALWAYS_SIGN
keyExchangeKey = ntlm.KXKEY(ntlmChallenge['flags'], sessionBaseKey, lmChallengeResponse, ntlmChallenge['challenge'], '', lmhash, nthash, True)
# If we set up key exchange, let's fill the right variables if ntlmChallenge['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH: exportedSessionKey = authenticateMessage['session_key'] exportedSessionKey = ntlm.generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey) else: encryptedRandomSessionKey = None # [MS-NLMP] page 46 exportedSessionKey = keyExchangeKey
# Do they match? if ntProofStr == ntProofStr2: # Yes!, process login return STATUS_SUCCESS, exportedSessionKey else: return STATUS_LOGON_FAILURE, exportedSessionKey
# We don't want to add a possible failure here, since this is an # extra bonus. We try, if it fails, returns nothing # ToDo: Document the parameter's types (bytes / string) and check all the places where it's called ret_value = '' if type(challenge) is not bytes: challenge = challenge.decode('latin-1')
try: if len(ntresponse) > 24: # Extended Security - NTLMv2 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % ( username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(challenge).decode('latin-1'), hexlify(ntresponse).decode('latin-1')[:32], hexlify(ntresponse).decode()[32:]), 'hash_version': 'ntlmv2'} else: # NTLMv1 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % ( username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(lmresponse).decode('latin-1'), hexlify(ntresponse).decode('latin-1'), hexlify(challenge).decode()), 'hash_version': 'ntlm'} except: # Let's try w/o decoding Unicode try: if len(ntresponse) > 24: # Extended Security - NTLMv2 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % ( username.decode('latin-1'), domain.decode('latin-1'), hexlify(challenge).decode('latin-1'), hexlify(ntresponse)[:32].decode('latin-1'), hexlify(ntresponse)[32:].decode('latin-1')), 'hash_version': 'ntlmv2'} else: # NTLMv1 ret_value = {'hash_string': '%s::%s:%s:%s:%s' % ( username, domain, hexlify(lmresponse).decode('latin-1'), hexlify(ntresponse).decode('latin-1'), hexlify(challenge).decode('latin-1')), 'hash_version': 'ntlm'} except Exception as e: import traceback traceback.print_exc() LOG.error("outputToJohnFormat: %s" % e) pass
return ret_value
fn_data = os.path.splitext(file_name) if hash_version == "ntlmv2": output_filename = fn_data[0] + "_ntlmv2" + fn_data[1] else: output_filename = fn_data[0] + "_ntlm" + fn_data[1]
with open(output_filename,"a") as f: f.write(hash_string) f.write('\n')
if flags & smb.SMB.FLAGS2_UNICODE: return text.decode('utf-16le') else: return text
if flags & smb.SMB.FLAGS2_UNICODE: return (text).encode('utf-16le') else: return text.encode('ascii')
t *= 10000000 t += 116444736000000000 return t
t -= 116444736000000000 t //= 10000000 return t
# TODO: Fix this :P d = datetime.date.fromtimestamp(t) year = d.year - 1980 ret = (year << 8) + (d.month << 4) + d.day return ret
# TODO: Fix this :P d = datetime.datetime.fromtimestamp(t) return (d.hour << 8) + (d.minute << 4) + d.second
config = smbServer.getServerConfig() sections = config.sections() # Remove the global one del(sections[sections.index('global')]) shares = {} for i in sections: shares[i] = dict(config.items(i)) return shares
config = smbServer.getServerConfig() if config.has_section(share): return dict(config.items(share)) else: return None
fileName = os.path.normpath(fileName.replace('\\','/')) errorCode = 0 if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) mode = 0 # Check the Open Mode if openMode & 0x10: # If the file does not exist, create it. mode = os.O_CREAT else: # If file does not exist, return an error if os.path.exists(pathName) is not True: errorCode = STATUS_NO_SUCH_FILE return 0,mode, pathName, errorCode
if os.path.isdir(pathName) and (fileAttributes & smb.ATTR_DIRECTORY) == 0: # Request to open a normal file and this is actually a directory errorCode = STATUS_FILE_IS_A_DIRECTORY return 0, mode, pathName, errorCode # Check the Access Mode if accessMode & 0x7 == 1: mode |= os.O_WRONLY elif accessMode & 0x7 == 2: mode |= os.O_RDWR else: mode = os.O_RDONLY
try: if sys.platform == 'win32': mode |= os.O_BINARY fid = os.open(pathName, mode) except Exception as e: LOG.error("openFile: %s,%s" % (pathName, mode) ,e) fid = 0 errorCode = STATUS_ACCESS_DENIED
return fid, mode, pathName, errorCode
if pktFlags & smb.SMB.FLAGS2_UNICODE: encoding = 'utf-16le' else: encoding = 'ascii'
fileName = os.path.normpath(filename.replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) fileSize = os.path.getsize(pathName) (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName) if level == smb.SMB_QUERY_FS_ATTRIBUTE_INFO or level == smb2.SMB2_FILESYSTEM_ATTRIBUTE_INFO: data = smb.SMBQueryFsAttributeInfo() data['FileSystemAttributes'] = smb.FILE_CASE_SENSITIVE_SEARCH | smb.FILE_CASE_PRESERVED_NAMES data['MaxFilenNameLengthInBytes'] = 255 data['LengthOfFileSystemName'] = len('XTFS')*2 data['FileSystemName'] = 'XTFS'.encode('utf-16le') return data.getData() elif level == smb.SMB_INFO_VOLUME: data = smb.SMBQueryFsInfoVolume( flags = pktFlags ) data['VolumeLabel'] = 'SHARE'.encode(encoding) return data.getData() elif level == smb.SMB_QUERY_FS_VOLUME_INFO or level == smb2.SMB2_FILESYSTEM_VOLUME_INFO: data = smb.SMBQueryFsVolumeInfo() data['VolumeLabel'] = '' data['VolumeCreationTime'] = getFileTime(ctime) return data.getData() elif level == smb.SMB_QUERY_FS_SIZE_INFO: data = smb.SMBQueryFsSizeInfo() return data.getData() elif level == smb.FILE_FS_FULL_SIZE_INFORMATION: data = smb.SMBFileFsFullSizeInformation() return data.getData() elif level == smb.FILE_FS_SIZE_INFORMATION: data = smb.FileFsSizeInformation() return data.getData() else: lastWriteTime = mtime attribs = 0 if os.path.isdir(pathName): attribs |= smb.SMB_FILE_ATTRIBUTE_DIRECTORY if os.path.isfile(pathName): attribs |= smb.SMB_FILE_ATTRIBUTE_NORMAL fileAttributes = attribs return fileSize, lastWriteTime, fileAttributes
# TODO: Depending on the level, this could be done much simpler
#print "FindFirs2 path:%s, filename:%s" % (path, fileName) fileName = os.path.normpath(fileName.replace('\\','/')) # Let's choose the right encoding depending on the request if pktFlags & smb.SMB.FLAGS2_UNICODE: encoding = 'utf-16le' else: encoding = 'ascii'
if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:]
pathName = os.path.join(path,fileName) files = []
if pathName.find('*') == -1 and pathName.find('?') == -1: # No search patterns pattern = '' else: pattern = os.path.basename(pathName) dirName = os.path.dirname(pathName)
# Always add . and .. Not that important for Windows, but Samba whines if # not present (for * search only) if pattern == '*': files.append(os.path.join(dirName,'.')) files.append(os.path.join(dirName,'..'))
if pattern != '': for file in os.listdir(dirName): if fnmatch.fnmatch(file.lower(),pattern.lower()): entry = os.path.join(dirName, file) if os.path.isdir(entry): if searchAttributes & smb.ATTR_DIRECTORY: files.append(entry) else: files.append(entry) else: if os.path.exists(pathName): files.append(pathName)
searchResult = [] searchCount = len(files) errorCode = STATUS_SUCCESS
for i in files: if level == smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_BOTH_DIRECTORY_INFO: item = smb.SMBFindFileBothDirectoryInfo( flags = pktFlags ) elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO or level == smb2.SMB2_FILE_DIRECTORY_INFO: item = smb.SMBFindFileDirectoryInfo( flags = pktFlags ) elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO: item = smb.SMBFindFileFullDirectoryInfo( flags = pktFlags ) elif level == smb.SMB_FIND_INFO_STANDARD: item = smb.SMBFindInfoStandard( flags = pktFlags ) elif level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_FULL_DIRECTORY_INFO: item = smb.SMBFindFileIdFullDirectoryInfo( flags = pktFlags ) elif level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO: item = smb.SMBFindFileIdBothDirectoryInfo( flags = pktFlags ) elif level == smb.SMB_FIND_FILE_NAMES_INFO or level == smb2.SMB2_FILE_NAMES_INFO: item = smb.SMBFindFileNamesInfo( flags = pktFlags ) else: LOG.error("Wrong level %d!" % level) return searchResult, searchCount, STATUS_NOT_SUPPORTED
(mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(i) if os.path.isdir(i): item['ExtFileAttributes'] = smb.ATTR_DIRECTORY else: item['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
item['FileName'] = os.path.basename(i).encode(encoding)
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: item['EaSize'] = 0 item['EndOfFile'] = size item['AllocationSize'] = size item['CreationTime'] = getFileTime(ctime) item['LastAccessTime'] = getFileTime(atime) item['LastWriteTime'] = getFileTime(mtime) item['LastChangeTime'] = getFileTime(mtime) item['ShortName'] = '\x00'*24 item['FileName'] = os.path.basename(i).encode(encoding) padLen = (8-(len(item) % 8)) % 8 item['NextEntryOffset'] = len(item) + padLen elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO: item['EndOfFile'] = size item['AllocationSize'] = size item['CreationTime'] = getFileTime(ctime) item['LastAccessTime'] = getFileTime(atime) item['LastWriteTime'] = getFileTime(mtime) item['LastChangeTime'] = getFileTime(mtime) item['FileName'] = os.path.basename(i).encode(encoding) padLen = (8-(len(item) % 8)) % 8 item['NextEntryOffset'] = len(item) + padLen 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: item['EaSize'] = 0 item['EndOfFile'] = size item['AllocationSize'] = size item['CreationTime'] = getFileTime(ctime) item['LastAccessTime'] = getFileTime(atime) item['LastWriteTime'] = getFileTime(mtime) item['LastChangeTime'] = getFileTime(mtime) padLen = (8-(len(item) % 8)) % 8 item['NextEntryOffset'] = len(item) + padLen elif level == smb.SMB_FIND_INFO_STANDARD: item['EaSize'] = size item['CreationDate'] = getSMBDate(ctime) item['CreationTime'] = getSMBTime(ctime) item['LastAccessDate'] = getSMBDate(atime) item['LastAccessTime'] = getSMBTime(atime) item['LastWriteDate'] = getSMBDate(mtime) item['LastWriteTime'] = getSMBTime(mtime) searchResult.append(item)
# No more files if (level >= smb.SMB_FIND_FILE_DIRECTORY_INFO or isSMB2 is True) and searchCount > 0: searchResult[-1]['NextEntryOffset'] = 0
return searchResult, searchCount, errorCode
#print "queryFileInfo path: %s, filename: %s, level:0x%x" % (path,filename,level) return queryPathInformation(path,filename, level)
# TODO: Depending on the level, this could be done much simpler #print("queryPathInfo path: %s, filename: %s, level:0x%x" % (path,filename,level)) try: errorCode = 0 fileName = os.path.normpath(filename.replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '': # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) if os.path.exists(pathName): (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName) if level == smb.SMB_QUERY_FILE_BASIC_INFO: infoRecord = smb.SMBQueryFileBasicInfo() infoRecord['CreationTime'] = getFileTime(ctime) infoRecord['LastAccessTime'] = getFileTime(atime) infoRecord['LastWriteTime'] = getFileTime(mtime) infoRecord['LastChangeTime'] = getFileTime(mtime) if os.path.isdir(pathName): infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY else: infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE elif level == smb.SMB_QUERY_FILE_STANDARD_INFO: infoRecord = smb.SMBQueryFileStandardInfo() infoRecord['AllocationSize'] = size infoRecord['EndOfFile'] = size if os.path.isdir(pathName): infoRecord['Directory'] = 1 else: infoRecord['Directory'] = 0 elif level == smb.SMB_QUERY_FILE_ALL_INFO or level == smb2.SMB2_FILE_ALL_INFO: infoRecord = smb.SMBQueryFileAllInfo() infoRecord['CreationTime'] = getFileTime(ctime) infoRecord['LastAccessTime'] = getFileTime(atime) infoRecord['LastWriteTime'] = getFileTime(mtime) infoRecord['LastChangeTime'] = getFileTime(mtime) if os.path.isdir(pathName): infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY else: infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE infoRecord['AllocationSize'] = size infoRecord['EndOfFile'] = size if os.path.isdir(pathName): infoRecord['Directory'] = 1 else: infoRecord['Directory'] = 0 infoRecord['FileName'] = filename.encode('utf-16le') elif level == smb2.SMB2_FILE_NETWORK_OPEN_INFO: infoRecord = smb.SMBFileNetworkOpenInfo() infoRecord['CreationTime'] = getFileTime(ctime) infoRecord['LastAccessTime'] = getFileTime(atime) infoRecord['LastWriteTime'] = getFileTime(mtime) infoRecord['ChangeTime'] = getFileTime(mtime) infoRecord['AllocationSize'] = size infoRecord['EndOfFile'] = size if os.path.isdir(pathName): infoRecord['FileAttributes'] = smb.ATTR_DIRECTORY else: infoRecord['FileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE elif level == smb.SMB_QUERY_FILE_EA_INFO or level == smb2.SMB2_FILE_EA_INFO: infoRecord = smb.SMBQueryFileEaInfo() elif level == smb2.SMB2_FILE_STREAM_INFO: infoRecord = smb.SMBFileStreamInformation() else: LOG.error('Unknown level for query path info! 0x%x' % level) # UNSUPPORTED return None, STATUS_NOT_SUPPORTED
return infoRecord, errorCode else: # NOT FOUND return None, STATUS_OBJECT_NAME_NOT_FOUND except Exception as e: LOG.error('queryPathInfo: %s' % e) raise
# TODO: Do something useful here :) # For now we just return fake values totalUnits = 65535 freeUnits = 65535 return totalUnits, freeUnits
# Here we implement the NT transaction handlers pass
# Here we implement the NT transaction handlers # Minimal [MS-RAP] implementation, just to return the shares connData = smbServer.getConnectionData(connId)
respSetup = b'' respParameters = b'' respData = b'' errorCode = STATUS_SUCCESS if struct.unpack('<H',parameters[:2])[0] == 0: # NetShareEnum Request netShareEnum = smb.SMBNetShareEnum(parameters) if netShareEnum['InfoLevel'] == 1: shares = getShares(connId, smbServer) respParameters = smb.SMBNetShareEnumResponse() respParameters['EntriesReturned'] = len(shares) respParameters['EntriesAvailable'] = len(shares) tailData = '' for i in shares: # NetShareInfo1 len == 20 entry = smb.NetShareInfo1() entry['NetworkName'] = i + '\x00'*(13-len(i)) entry['Type'] = int(shares[i]['share type']) # (beto) If offset == 0 it crashes explorer.exe on windows 7 entry['RemarkOffsetLow'] = 20 * len(shares) + len(tailData) respData += entry.getData() if 'comment' in shares[i]: tailData += shares[i]['comment'] + '\x00' else: tailData += '\x00' respData += tailData else: # We don't support other info levels errorCode = STATUS_NOT_SUPPORTED elif struct.unpack('<H',parameters[:2])[0] == 13: # NetrServerGetInfo Request respParameters = smb.SMBNetServerGetInfoResponse() netServerInfo = smb.SMBNetServerInfo1() netServerInfo['ServerName'] = smbServer.getServerName() respData = netServerInfo.getData() respParameters['TotalBytesAvailable'] = len(respData) elif struct.unpack('<H',parameters[:2])[0] == 1: # NetrShareGetInfo Request request = smb.SMBNetShareGetInfo(parameters) respParameters = smb.SMBNetShareGetInfoResponse() shares = getShares(connId, smbServer) share = shares[request['ShareName'].upper()] shareInfo = smb.NetShareInfo1() shareInfo['NetworkName'] = request['ShareName'].upper() + '\x00' shareInfo['Type'] = int(share['share type']) respData = shareInfo.getData() if 'comment' in share: shareInfo['RemarkOffsetLow'] = len(respData) respData += share['comment'] + '\x00' respParameters['TotalBytesAvailable'] = len(respData)
else: # We don't know how to handle anything else errorCode = STATUS_NOT_SUPPORTED
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
connData = smbServer.getConnectionData(connId)
respSetup = b'' respParameters = b'' respData = b'' errorCode = STATUS_SUCCESS SMBCommand = smb.SMBCommand(recvPacket['Data'][0]) transParameters= smb.SMBTransaction_Parameters(SMBCommand['Parameters'])
# Extract the FID fid = struct.unpack('<H', transParameters['Setup'][2:])[0]
if fid in connData['OpenedFiles']: fileHandle = connData['OpenedFiles'][fid]['FileHandle'] if fileHandle != PIPE_FILE_DESCRIPTOR: os.write(fileHandle,data) respData = os.read(fileHandle,data) else: sock = connData['OpenedFiles'][fid]['Socket'] sock.send(data) respData = sock.recv(maxDataCount) else: errorCode = STATUS_INVALID_HANDLE
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
# Here we implement the transaction2 handlers # All these commands return setup, parameters, data, errorCode connData = smbServer.getConnectionData(connId)
respSetup = b'' respParameters = b'' respData = b'' errorCode = STATUS_SUCCESS setPathInfoParameters = smb.SMBSetPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters) if recvPacket['Tid'] in connData['ConnectedShares']: path = connData['ConnectedShares'][recvPacket['Tid']]['path'] fileName = decodeSMBString(recvPacket['Flags2'], setPathInfoParameters['FileName']) fileName = os.path.normpath(fileName.replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '': # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) if os.path.exists(pathName): informationLevel = setPathInfoParameters['InformationLevel'] if informationLevel == smb.SMB_SET_FILE_BASIC_INFO: infoRecord = smb.SMBSetFileBasicInfo(data) # Creation time won't be set, the other ones we play with. atime = infoRecord['LastAccessTime'] if atime == 0: atime = -1 else: atime = getUnixTime(atime) mtime = infoRecord['LastWriteTime'] if mtime == 0: mtime = -1 else: mtime = getUnixTime(mtime) if mtime != -1 or atime != -1: os.utime(pathName,(atime,mtime)) else: smbServer.log('Unknown level for set path info! 0x%x' % setPathInfoParameters['InformationLevel'], logging.ERROR) # UNSUPPORTED errorCode = STATUS_NOT_SUPPORTED else: errorCode = STATUS_OBJECT_NAME_NOT_FOUND
if errorCode == STATUS_SUCCESS: respParameters = smb.SMBSetPathInformationResponse_Parameters()
else: errorCode = STATUS_SMB_BAD_TID
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
connData = smbServer.getConnectionData(connId)
respSetup = b'' respParameters = b'' respData = b'' errorCode = STATUS_SUCCESS setFileInfoParameters = smb.SMBSetFileInformation_Parameters(parameters)
if recvPacket['Tid'] in connData['ConnectedShares']: if setFileInfoParameters['FID'] in connData['OpenedFiles']: fileName = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileName'] informationLevel = setFileInfoParameters['InformationLevel'] if informationLevel == smb.SMB_SET_FILE_DISPOSITION_INFO: infoRecord = smb.SMBSetFileDispositionInfo(parameters) if infoRecord['DeletePending'] > 0: # Mark this file for removal after closed connData['OpenedFiles'][setFileInfoParameters['FID']]['DeleteOnClose'] = True respParameters = smb.SMBSetFileInformationResponse_Parameters() elif informationLevel == smb.SMB_SET_FILE_BASIC_INFO: infoRecord = smb.SMBSetFileBasicInfo(data) # Creation time won't be set, the other ones we play with. atime = infoRecord['LastAccessTime'] if atime == 0: atime = -1 else: atime = getUnixTime(atime) mtime = infoRecord['LastWriteTime'] if mtime == 0: mtime = -1 else: mtime = getUnixTime(mtime) os.utime(fileName,(atime,mtime)) elif informationLevel == smb.SMB_SET_FILE_END_OF_FILE_INFO: fileHandle = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileHandle'] infoRecord = smb.SMBSetFileEndOfFileInfo(data) if infoRecord['EndOfFile'] > 0: os.lseek(fileHandle, infoRecord['EndOfFile']-1, 0) os.write(fileHandle, b'\x00') else: smbServer.log('Unknown level for set file info! 0x%x' % setFileInfoParameters['InformationLevel'], logging.ERROR) # UNSUPPORTED errorCode = STATUS_NOT_SUPPORTED else: errorCode = STATUS_NO_SUCH_FILE
if errorCode == STATUS_SUCCESS: respParameters = smb.SMBSetFileInformationResponse_Parameters() else: errorCode = STATUS_SMB_BAD_TID
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
connData = smbServer.getConnectionData(connId)
respSetup = b'' respParameters = b'' respData = b''
queryFileInfoParameters = smb.SMBQueryFileInformation_Parameters(parameters)
if recvPacket['Tid'] in connData['ConnectedShares']: if queryFileInfoParameters['FID'] in connData['OpenedFiles']: fileName = connData['OpenedFiles'][queryFileInfoParameters['FID']]['FileName']
infoRecord, errorCode = queryFileInformation('', fileName, queryFileInfoParameters['InformationLevel'])
if infoRecord is not None: respParameters = smb.SMBQueryFileInformationResponse_Parameters() respData = infoRecord else: errorCode = STATUS_INVALID_HANDLE else: errorCode = STATUS_SMB_BAD_TID
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
connData = smbServer.getConnectionData(connId)
respSetup = b'' respParameters = b'' respData = b'' errorCode = 0
queryPathInfoParameters = smb.SMBQueryPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters)
if recvPacket['Tid'] in connData['ConnectedShares']: path = connData['ConnectedShares'][recvPacket['Tid']]['path'] try: infoRecord, errorCode = queryPathInformation(path, decodeSMBString(recvPacket['Flags2'], queryPathInfoParameters['FileName']), queryPathInfoParameters['InformationLevel']) except Exception as e: smbServer.log("queryPathInformation: %s" % e,logging.ERROR)
if infoRecord is not None: respParameters = smb.SMBQueryPathInformationResponse_Parameters() respData = infoRecord else: errorCode = STATUS_SMB_BAD_TID
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
connData = smbServer.getConnectionData(connId) errorCode = 0 # Get the Tid associated if recvPacket['Tid'] in connData['ConnectedShares']: data = queryFsInformation(connData['ConnectedShares'][recvPacket['Tid']]['path'], '', struct.unpack('<H',parameters)[0], pktFlags = recvPacket['Flags2'])
smbServer.setConnectionData(connId, connData)
return b'',b'', data, errorCode
def findNext2(connId, smbServer, recvPacket, parameters, data, maxDataCount): connData = smbServer.getConnectionData(connId)
respSetup = b'' respParameters = b'' respData = b'' errorCode = STATUS_SUCCESS findNext2Parameters = smb.SMBFindNext2_Parameters(flags = recvPacket['Flags2'], data = parameters)
sid = findNext2Parameters['SID'] if recvPacket['Tid'] in connData['ConnectedShares']: if sid in connData['SIDs']: searchResult = connData['SIDs'][sid] respParameters = smb.SMBFindNext2Response_Parameters() endOfSearch = 1 searchCount = 1 totalData = 0 for i in enumerate(searchResult): data = i[1].getData() lenData = len(data) if (totalData+lenData) >= maxDataCount or (i[0]+1) >= findNext2Parameters['SearchCount']: # We gotta stop here and continue on a find_next2 endOfSearch = 0 connData['SIDs'][sid] = searchResult[i[0]:] respParameters['LastNameOffset'] = totalData break else: searchCount +=1 respData += data totalData += lenData
# Have we reached the end of the search or still stuff to send? if endOfSearch > 0: # Let's remove the SID from our ConnData del(connData['SIDs'][sid])
respParameters['EndOfSearch'] = endOfSearch respParameters['SearchCount'] = searchCount else: errorCode = STATUS_INVALID_HANDLE else: errorCode = STATUS_SMB_BAD_TID
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
def findFirst2(connId, smbServer, recvPacket, parameters, data, maxDataCount): connData = smbServer.getConnectionData(connId)
respSetup = b'' respParameters = b'' respData = b'' findFirst2Parameters = smb.SMBFindFirst2_Parameters( recvPacket['Flags2'], data = parameters)
if recvPacket['Tid'] in connData['ConnectedShares']: path = connData['ConnectedShares'][recvPacket['Tid']]['path']
searchResult, searchCount, errorCode = findFirst2(path, decodeSMBString( recvPacket['Flags2'], findFirst2Parameters['FileName'] ), findFirst2Parameters['InformationLevel'], findFirst2Parameters['SearchAttributes'] , pktFlags = recvPacket['Flags2'])
respParameters = smb.SMBFindFirst2Response_Parameters() endOfSearch = 1 sid = 0x80 # default SID searchCount = 0 totalData = 0 for i in enumerate(searchResult): #i[1].dump() data = i[1].getData() lenData = len(data) if (totalData+lenData) >= maxDataCount or (i[0]+1) > findFirst2Parameters['SearchCount']: # We gotta stop here and continue on a find_next2 endOfSearch = 0 # Simple way to generate a fid if len(connData['SIDs']) == 0: sid = 1 else: sid = list(connData['SIDs'].keys())[-1] + 1 # Store the remaining search results in the ConnData SID connData['SIDs'][sid] = searchResult[i[0]:] respParameters['LastNameOffset'] = totalData break else: searchCount +=1 respData += data
padLen = (8-(lenData % 8)) %8 respData += b'\xaa'*padLen totalData += lenData + padLen
respParameters['SID'] = sid respParameters['EndOfSearch'] = endOfSearch respParameters['SearchCount'] = searchCount else: errorCode = STATUS_SMB_BAD_TID
smbServer.setConnectionData(connId, connData)
return respSetup, respParameters, respData, errorCode
# Here we implement the commands handlers
def smbTransaction(connId, smbServer, SMBCommand, recvPacket, transCommands): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(recvPacket['Command'])
transParameters= smb.SMBTransaction_Parameters(SMBCommand['Parameters'])
# Do the stuff if transParameters['ParameterCount'] != transParameters['TotalParameterCount']: # TODO: Handle partial parameters raise Exception("Unsupported partial parameters in TRANSACT2!") else: transData = smb.SMBTransaction_SData(flags = recvPacket['Flags2']) # Standard says servers shouldn't trust Parameters and Data comes # in order, so we have to parse the offsets, ugly
paramCount = transParameters['ParameterCount'] transData['Trans_ParametersLength'] = paramCount dataCount = transParameters['DataCount'] transData['Trans_DataLength'] = dataCount transData.fromString(SMBCommand['Data']) if transParameters['ParameterOffset'] > 0: paramOffset = transParameters['ParameterOffset'] - 63 - transParameters['SetupLength'] transData['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount] else: transData['Trans_Parameters'] = b''
if transParameters['DataOffset'] > 0: dataOffset = transParameters['DataOffset'] - 63 - transParameters['SetupLength'] transData['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount] else: transData['Trans_Data'] = b''
# Call the handler for this TRANSACTION if transParameters['SetupCount'] == 0: # No subcommand, let's play with the Name command = decodeSMBString(recvPacket['Flags2'],transData['Name']) else: command = struct.unpack('<H', transParameters['Setup'][:2])[0]
if command in transCommands: # Call the TRANS subcommand setup = b'' parameters = b'' data = b'' try: setup, parameters, data, errorCode = transCommands[command](connId, smbServer, recvPacket, transData['Trans_Parameters'], transData['Trans_Data'], transParameters['MaxDataCount']) except Exception as e: #print 'Transaction: %s' % e,e smbServer.log('Transaction: (%r,%s)' % (command, e), logging.ERROR) errorCode = STATUS_ACCESS_DENIED #raise
if setup == b'' and parameters == b'' and data == b'': # Something wen't wrong respParameters = b'' respData = b'' else: # Build the answer if hasattr(data, 'getData'): data = data.getData() remainingData = len(data) if hasattr(parameters, 'getData'): parameters = parameters.getData() remainingParameters = len(parameters) commands = [] dataDisplacement = 0 while remainingData > 0 or remainingParameters > 0: respSMBCommand = smb.SMBCommand(recvPacket['Command']) respParameters = smb.SMBTransactionResponse_Parameters() respData = smb.SMBTransaction2Response_Data()
respParameters['TotalParameterCount'] = len(parameters) respParameters['ParameterCount'] = len(parameters) respData['Trans_ParametersLength'] = len(parameters) respParameters['TotalDataCount'] = len(data) respParameters['DataDisplacement'] = dataDisplacement
# TODO: Do the same for parameters if len(data) > transParameters['MaxDataCount']: # Answer doesn't fit in this packet LOG.debug("Lowering answer from %d to %d" % (len(data),transParameters['MaxDataCount']) ) respParameters['DataCount'] = transParameters['MaxDataCount'] else: respParameters['DataCount'] = len(data)
respData['Trans_DataLength'] = respParameters['DataCount'] respParameters['SetupCount'] = len(setup) respParameters['Setup'] = setup # TODO: Make sure we're calculating the pad right if len(parameters) > 0: #padLen = 4 - (55 + len(setup)) % 4 padLen = (4 - (55 + len(setup)) % 4 ) % 4 padBytes = b'\xFF' * padLen respData['Pad1'] = padBytes respParameters['ParameterOffset'] = 55 + len(setup) + padLen else: padLen = 0 respParameters['ParameterOffset'] = 0 respData['Pad1'] = b''
if len(data) > 0: #pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4 respData['Pad2'] = b'\xFF' * pad2Len respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len else: respParameters['DataOffset'] = 0 respData['Pad2'] = b''
respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']] respData['Trans_Data'] = data[:respParameters['DataCount']] respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData
data = data[respParameters['DataCount']:] remainingData -= respParameters['DataCount'] dataDisplacement += respParameters['DataCount'] + 1
parameters = parameters[respParameters['ParameterCount']:] remainingParameters -= respParameters['ParameterCount'] commands.append(respSMBCommand)
smbServer.setConnectionData(connId, connData) return commands, None, errorCode
else: smbServer.log("Unsupported Transact command %r" % command, logging.ERROR) respParameters = b'' respData = b'' errorCode = STATUS_NOT_IMPLEMENTED
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbNTTransact(connId, smbServer, SMBCommand, recvPacket, transCommands): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(recvPacket['Command'])
NTTransParameters= smb.SMBNTTransaction_Parameters(SMBCommand['Parameters']) # Do the stuff if NTTransParameters['ParameterCount'] != NTTransParameters['TotalParameterCount']: # TODO: Handle partial parameters raise Exception("Unsupported partial parameters in NTTrans!") else: NTTransData = smb.SMBNTTransaction_Data() # Standard says servers shouldn't trust Parameters and Data comes # in order, so we have to parse the offsets, ugly
paramCount = NTTransParameters['ParameterCount'] NTTransData['NT_Trans_ParametersLength'] = paramCount dataCount = NTTransParameters['DataCount'] NTTransData['NT_Trans_DataLength'] = dataCount
if NTTransParameters['ParameterOffset'] > 0: paramOffset = NTTransParameters['ParameterOffset'] - 73 - NTTransParameters['SetupLength'] NTTransData['NT_Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount] else: NTTransData['NT_Trans_Parameters'] = b''
if NTTransParameters['DataOffset'] > 0: dataOffset = NTTransParameters['DataOffset'] - 73 - NTTransParameters['SetupLength'] NTTransData['NT_Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount] else: NTTransData['NT_Trans_Data'] = b''
# Call the handler for this TRANSACTION command = NTTransParameters['Function'] if command in transCommands: # Call the NT TRANS subcommand setup = b'' parameters = b'' data = b'' try: setup, parameters, data, errorCode = transCommands[command](connId, smbServer, recvPacket, NTTransData['NT_Trans_Parameters'], NTTransData['NT_Trans_Data'], NTTransParameters['MaxDataCount']) except Exception as e: smbServer.log('NTTransaction: (0x%x,%s)' % (command, e), logging.ERROR) errorCode = STATUS_ACCESS_DENIED #raise
if setup == b'' and parameters == b'' and data == b'': # Something wen't wrong respParameters = b'' respData = b'' if errorCode == STATUS_SUCCESS: errorCode = STATUS_ACCESS_DENIED else: # Build the answer if hasattr(data, 'getData'): data = data.getData() remainingData = len(data) if hasattr(parameters, 'getData'): parameters = parameters.getData() remainingParameters = len(parameters) commands = [] dataDisplacement = 0 while remainingData > 0 or remainingParameters > 0: respSMBCommand = smb.SMBCommand(recvPacket['Command']) respParameters = smb.SMBNTTransactionResponse_Parameters() respData = smb.SMBNTTransactionResponse_Data()
respParameters['TotalParameterCount'] = len(parameters) respParameters['ParameterCount'] = len(parameters) respData['Trans_ParametersLength'] = len(parameters) respParameters['TotalDataCount'] = len(data) respParameters['DataDisplacement'] = dataDisplacement # TODO: Do the same for parameters if len(data) > NTTransParameters['MaxDataCount']: # Answer doesn't fit in this packet LOG.debug("Lowering answer from %d to %d" % (len(data),NTTransParameters['MaxDataCount']) ) respParameters['DataCount'] = NTTransParameters['MaxDataCount'] else: respParameters['DataCount'] = len(data)
respData['NT_Trans_DataLength'] = respParameters['DataCount'] respParameters['SetupCount'] = len(setup) respParameters['Setup'] = setup # TODO: Make sure we're calculating the pad right if len(parameters) > 0: #padLen = 4 - (71 + len(setup)) % 4 padLen = (4 - (73 + len(setup)) % 4 ) % 4 padBytes = b'\xFF' * padLen respData['Pad1'] = padBytes respParameters['ParameterOffset'] = 73 + len(setup) + padLen else: padLen = 0 respParameters['ParameterOffset'] = 0 respData['Pad1'] = b''
if len(data) > 0: #pad2Len = 4 - (71 + len(setup) + padLen + len(parameters)) % 4 pad2Len = (4 - (73 + len(setup) + padLen + len(parameters)) % 4) % 4 respData['Pad2'] = b'\xFF' * pad2Len respParameters['DataOffset'] = 73 + len(setup) + padLen + len(parameters) + pad2Len else: respParameters['DataOffset'] = 0 respData['Pad2'] = b''
respData['NT_Trans_Parameters'] = parameters[:respParameters['ParameterCount']] respData['NT_Trans_Data'] = data[:respParameters['DataCount']] respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData
data = data[respParameters['DataCount']:] remainingData -= respParameters['DataCount'] dataDisplacement += respParameters['DataCount'] + 1
parameters = parameters[respParameters['ParameterCount']:] remainingParameters -= respParameters['ParameterCount'] commands.append(respSMBCommand)
smbServer.setConnectionData(connId, connData) return commands, None, errorCode
else: #smbServer.log("Unsupported NTTransact command 0x%x" % command, logging.ERROR) respParameters = b'' respData = b'' errorCode = STATUS_NOT_IMPLEMENTED
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smbTransaction2(connId, smbServer, SMBCommand, recvPacket, transCommands): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(recvPacket['Command'])
trans2Parameters= smb.SMBTransaction2_Parameters(SMBCommand['Parameters'])
# Do the stuff if trans2Parameters['ParameterCount'] != trans2Parameters['TotalParameterCount']: # TODO: Handle partial parameters #print "Unsupported partial parameters in TRANSACT2!" raise Exception("Unsupported partial parameters in TRANSACT2!") else: trans2Data = smb.SMBTransaction2_Data() # Standard says servers shouldn't trust Parameters and Data comes # in order, so we have to parse the offsets, ugly
paramCount = trans2Parameters['ParameterCount'] trans2Data['Trans_ParametersLength'] = paramCount dataCount = trans2Parameters['DataCount'] trans2Data['Trans_DataLength'] = dataCount
if trans2Parameters['ParameterOffset'] > 0: paramOffset = trans2Parameters['ParameterOffset'] - 63 - trans2Parameters['SetupLength'] trans2Data['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount] else: trans2Data['Trans_Parameters'] = b''
if trans2Parameters['DataOffset'] > 0: dataOffset = trans2Parameters['DataOffset'] - 63 - trans2Parameters['SetupLength'] trans2Data['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount] else: trans2Data['Trans_Data'] = b''
# Call the handler for this TRANSACTION command = struct.unpack('<H', trans2Parameters['Setup'])[0] if command in transCommands: # Call the TRANS2 subcommand try: setup, parameters, data, errorCode = transCommands[command](connId, smbServer, recvPacket, trans2Data['Trans_Parameters'], trans2Data['Trans_Data'], trans2Parameters['MaxDataCount']) except Exception as e: smbServer.log('Transaction2: (0x%x,%s)' % (command, e), logging.ERROR) #import traceback #traceback.print_exc() raise
if setup == b'' and parameters == b'' and data == b'': # Something wen't wrong respParameters = b'' respData = b'' else: # Build the answer if hasattr(data, 'getData'): data = data.getData() remainingData = len(data) if hasattr(parameters, 'getData'): parameters = parameters.getData() remainingParameters = len(parameters) commands = [] dataDisplacement = 0 while remainingData > 0 or remainingParameters > 0: respSMBCommand = smb.SMBCommand(recvPacket['Command']) respParameters = smb.SMBTransaction2Response_Parameters() respData = smb.SMBTransaction2Response_Data()
respParameters['TotalParameterCount'] = len(parameters) respParameters['ParameterCount'] = len(parameters) respData['Trans_ParametersLength'] = len(parameters) respParameters['TotalDataCount'] = len(data) respParameters['DataDisplacement'] = dataDisplacement # TODO: Do the same for parameters if len(data) > trans2Parameters['MaxDataCount']: # Answer doesn't fit in this packet LOG.debug("Lowering answer from %d to %d" % (len(data),trans2Parameters['MaxDataCount']) ) respParameters['DataCount'] = trans2Parameters['MaxDataCount'] else: respParameters['DataCount'] = len(data)
respData['Trans_DataLength'] = respParameters['DataCount'] respParameters['SetupCount'] = len(setup) respParameters['Setup'] = setup # TODO: Make sure we're calculating the pad right if len(parameters) > 0: #padLen = 4 - (55 + len(setup)) % 4 padLen = (4 - (55 + len(setup)) % 4 ) % 4 padBytes = b'\xFF' * padLen respData['Pad1'] = padBytes respParameters['ParameterOffset'] = 55 + len(setup) + padLen else: padLen = 0 respParameters['ParameterOffset'] = 0 respData['Pad1'] = b''
if len(data) > 0: #pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4 pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4 respData['Pad2'] = b'\xFF' * pad2Len respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len else: respParameters['DataOffset'] = 0 respData['Pad2'] = b''
respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']] respData['Trans_Data'] = data[:respParameters['DataCount']] respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData
data = data[respParameters['DataCount']:] remainingData -= respParameters['DataCount'] dataDisplacement += respParameters['DataCount'] + 1
parameters = parameters[respParameters['ParameterCount']:] remainingParameters -= respParameters['ParameterCount'] commands.append(respSMBCommand)
smbServer.setConnectionData(connId, connData) return commands, None, errorCode
else: smbServer.log("Unsupported Transact/2 command 0x%x" % command, logging.ERROR) respParameters = b'' respData = b'' errorCode = STATUS_NOT_IMPLEMENTED
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smbComLockingAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOCKING_ANDX) respParameters = b'' respData = b''
# I'm actually doing nothing.. just make MacOS happy ;) errorCode = STATUS_SUCCESS
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComClose(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CLOSE) respParameters = b'' respData = b''
comClose = smb.SMBClose_Parameters(SMBCommand['Parameters'])
if comClose['FID'] in connData['OpenedFiles']: errorCode = STATUS_SUCCESS fileHandle = connData['OpenedFiles'][comClose['FID']]['FileHandle'] try: if fileHandle == PIPE_FILE_DESCRIPTOR: connData['OpenedFiles'][comClose['FID']]['Socket'].close() elif fileHandle != VOID_FILE_DESCRIPTOR: os.close(fileHandle) except Exception as e: smbServer.log("comClose %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: # Check if the file was marked for removal if connData['OpenedFiles'][comClose['FID']]['DeleteOnClose'] is True: try: os.remove(connData['OpenedFiles'][comClose['FID']]['FileName']) except Exception as e: smbServer.log("comClose %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED del(connData['OpenedFiles'][comClose['FID']]) else: errorCode = STATUS_INVALID_HANDLE
if errorCode > 0: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComWrite(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE) respParameters = smb.SMBWriteResponse_Parameters() respData = b''
comWriteParameters = smb.SMBWrite_Parameters(SMBCommand['Parameters']) comWriteData = smb.SMBWrite_Data(SMBCommand['Data'])
if comWriteParameters['Fid'] in connData['OpenedFiles']: fileHandle = connData['OpenedFiles'][comWriteParameters['Fid']]['FileHandle'] errorCode = STATUS_SUCCESS try: if fileHandle != PIPE_FILE_DESCRIPTOR: # TODO: Handle big size files # If we're trying to write past the file end we just skip the write call (Vista does this) if os.lseek(fileHandle, 0, 2) >= comWriteParameters['Offset']: os.lseek(fileHandle,comWriteParameters['Offset'],0) os.write(fileHandle,comWriteData['Data']) else: sock = connData['OpenedFiles'][comWriteParameters['Fid']]['Socket'] sock.send(comWriteData['Data']) respParameters['Count'] = comWriteParameters['Count'] except Exception as e: smbServer.log('smbComWrite: %s' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE
if errorCode > 0: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComFlush(connId, smbServer, SMBCommand,recvPacket ): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_FLUSH) respParameters = b'' respData = b''
comFlush = smb.SMBFlush_Parameters(SMBCommand['Parameters'])
if comFlush['FID'] in connData['OpenedFiles']: errorCode = STATUS_SUCCESS fileHandle = connData['OpenedFiles'][comFlush['FID']]['FileHandle'] try: os.fsync(fileHandle) except Exception as e: smbServer.log("comFlush %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE
if errorCode > 0: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComCreateDirectory(connId, smbServer, SMBCommand,recvPacket ): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY) respParameters = b'' respData = b''
comCreateDirectoryData= smb.SMBCreateDirectory_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data'])
# Get the Tid associated if recvPacket['Tid'] in connData['ConnectedShares']: errorCode = STATUS_SUCCESS path = connData['ConnectedShares'][recvPacket['Tid']]['path'] fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comCreateDirectoryData['DirectoryName']).replace('\\','/')) if len(fileName) > 0: if fileName[0] == '/' or fileName[0] == '\\': # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) if os.path.exists(pathName): errorCode = STATUS_OBJECT_NAME_COLLISION
# TODO: More checks here in the future.. Specially when we support # user access else: try: os.mkdir(pathName) except Exception as e: smbServer.log("smbComCreateDirectory: %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID
if errorCode > 0: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComRename(connId, smbServer, SMBCommand, recvPacket ): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_RENAME) respParameters = b'' respData = b''
comRenameData = smb.SMBRename_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data']) # Get the Tid associated if recvPacket['Tid'] in connData['ConnectedShares']: errorCode = STATUS_SUCCESS path = connData['ConnectedShares'][recvPacket['Tid']]['path'] oldFileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comRenameData['OldFileName']).replace('\\','/')) newFileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comRenameData['NewFileName']).replace('\\','/')) if len(oldFileName) > 0 and (oldFileName[0] == '/' or oldFileName[0] == '\\'): # strip leading '/' oldFileName = oldFileName[1:] oldPathName = os.path.join(path,oldFileName) if len(newFileName) > 0 and (newFileName[0] == '/' or newFileName[0] == '\\'): # strip leading '/' newFileName = newFileName[1:] newPathName = os.path.join(path,newFileName)
if os.path.exists(oldPathName) is not True: errorCode = STATUS_NO_SUCH_FILE
# TODO: More checks here in the future.. Specially when we support # user access else: try: os.rename(oldPathName,newPathName) except OSError as e: smbServer.log("smbComRename: %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID
if errorCode > 0: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComDelete(connId, smbServer, SMBCommand, recvPacket ): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE) respParameters = b'' respData = b''
comDeleteData = smb.SMBDelete_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data'])
# Get the Tid associated if recvPacket['Tid'] in connData['ConnectedShares']: errorCode = STATUS_SUCCESS path = connData['ConnectedShares'][recvPacket['Tid']]['path'] fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comDeleteData['FileName']).replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) if os.path.exists(pathName) is not True: errorCode = STATUS_NO_SUCH_FILE
# TODO: More checks here in the future.. Specially when we support # user access else: try: os.remove(pathName) except OSError as e: smbServer.log("smbComDelete: %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID
if errorCode > 0: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComDeleteDirectory(connId, smbServer, SMBCommand, recvPacket ): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY) respParameters = b'' respData = b''
comDeleteDirectoryData= smb.SMBDeleteDirectory_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data'])
# Get the Tid associated if recvPacket['Tid'] in connData['ConnectedShares']: errorCode = STATUS_SUCCESS path = connData['ConnectedShares'][recvPacket['Tid']]['path'] fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comDeleteDirectoryData['DirectoryName']).replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) if os.path.exists(pathName) is not True: errorCode = STATUS_NO_SUCH_FILE
# TODO: More checks here in the future.. Specially when we support # user access else: try: os.rmdir(pathName) except OSError as e: smbServer.log("smbComDeleteDirectory: %s" % e,logging.ERROR) if e.errno == errno.ENOTEMPTY: errorCode = STATUS_DIRECTORY_NOT_EMPTY else: errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID
if errorCode > 0: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComWriteAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE_ANDX) respParameters = smb.SMBWriteAndXResponse_Parameters() respData = b''
if SMBCommand['WordCount'] == 0x0C: writeAndX = smb.SMBWriteAndX_Parameters_Short(SMBCommand['Parameters']) writeAndXData = smb.SMBWriteAndX_Data_Short() else: writeAndX = smb.SMBWriteAndX_Parameters(SMBCommand['Parameters']) writeAndXData = smb.SMBWriteAndX_Data() writeAndXData['DataLength'] = writeAndX['DataLength'] writeAndXData['DataOffset'] = writeAndX['DataOffset'] writeAndXData.fromString(SMBCommand['Data'])
if writeAndX['Fid'] in connData['OpenedFiles']: fileHandle = connData['OpenedFiles'][writeAndX['Fid']]['FileHandle'] errorCode = STATUS_SUCCESS try: if fileHandle != PIPE_FILE_DESCRIPTOR: offset = writeAndX['Offset'] if 'HighOffset' in writeAndX.fields: offset += (writeAndX['HighOffset'] << 32) # If we're trying to write past the file end we just skip the write call (Vista does this) if os.lseek(fileHandle, 0, 2) >= offset: os.lseek(fileHandle,offset,0) os.write(fileHandle,writeAndXData['Data']) else: sock = connData['OpenedFiles'][writeAndX['Fid']]['Socket'] sock.send(writeAndXData['Data'])
respParameters['Count'] = writeAndX['DataLength'] respParameters['Available']= 0xff except Exception as e: smbServer.log('smbComWriteAndx: %s' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE
if errorCode > 0: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComRead(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ) respParameters = smb.SMBReadResponse_Parameters() respData = smb.SMBReadResponse_Data()
comReadParameters = smb.SMBRead_Parameters(SMBCommand['Parameters'])
if comReadParameters['Fid'] in connData['OpenedFiles']: fileHandle = connData['OpenedFiles'][comReadParameters['Fid']]['FileHandle'] errorCode = STATUS_SUCCESS try: if fileHandle != PIPE_FILE_DESCRIPTOR: # TODO: Handle big size files os.lseek(fileHandle,comReadParameters['Offset'],0) content = os.read(fileHandle,comReadParameters['Count']) else: sock = connData['OpenedFiles'][comReadParameters['Fid']]['Socket'] content = sock.recv(comReadParameters['Count']) respParameters['Count'] = len(content) respData['DataLength'] = len(content) respData['Data'] = content except Exception as e: smbServer.log('smbComRead: %s ' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE
if errorCode > 0: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComReadAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ_ANDX) respParameters = smb.SMBReadAndXResponse_Parameters() respData = b''
if SMBCommand['WordCount'] == 0x0A: readAndX = smb.SMBReadAndX_Parameters2(SMBCommand['Parameters']) else: readAndX = smb.SMBReadAndX_Parameters(SMBCommand['Parameters'])
if readAndX['Fid'] in connData['OpenedFiles']: fileHandle = connData['OpenedFiles'][readAndX['Fid']]['FileHandle'] errorCode = 0 try: if fileHandle != PIPE_FILE_DESCRIPTOR: offset = readAndX['Offset'] if 'HighOffset' in readAndX.fields: offset += (readAndX['HighOffset'] << 32) os.lseek(fileHandle,offset,0) content = os.read(fileHandle,readAndX['MaxCount']) else: sock = connData['OpenedFiles'][readAndX['Fid']]['Socket'] content = sock.recv(readAndX['MaxCount']) respParameters['Remaining'] = 0xffff respParameters['DataCount'] = len(content) respParameters['DataOffset'] = 59 respParameters['DataCount_Hi'] = 0 respData = content except Exception as e: smbServer.log('smbComReadAndX: %s ' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE
if errorCode > 0: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbQueryInformation(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION) respParameters = smb.SMBQueryInformationResponse_Parameters() respData = b''
queryInformation= smb.SMBQueryInformation_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data'])
# Get the Tid associated if recvPacket['Tid'] in connData['ConnectedShares']: fileSize, lastWriteTime, fileAttributes = queryFsInformation( connData['ConnectedShares'][recvPacket['Tid']]['path'], decodeSMBString(recvPacket['Flags2'],queryInformation['FileName']), pktFlags = recvPacket['Flags2'])
respParameters['FileSize'] = fileSize respParameters['LastWriteTime'] = lastWriteTime respParameters['FileAttributes'] = fileAttributes errorCode = STATUS_SUCCESS else: # STATUS_SMB_BAD_TID errorCode = STATUS_SMB_BAD_TID respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smbQueryInformationDisk(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION_DISK) respParameters = smb.SMBQueryInformationDiskResponse_Parameters() respData = b''
# Get the Tid associated if recvPacket['Tid'] in connData['ConnectedShares']: totalUnits, freeUnits = queryDiskInformation( connData['ConnectedShares'][recvPacket['Tid']]['path'])
respParameters['TotalUnits'] = totalUnits respParameters['BlocksPerUnit'] = 1 respParameters['BlockSize'] = 1 respParameters['FreeUnits'] = freeUnits errorCode = STATUS_SUCCESS else: # STATUS_SMB_BAD_TID respData = b'' respParameters = b'' errorCode = STATUS_SMB_BAD_TID
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smbComEcho(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO) respParameters = smb.SMBEchoResponse_Parameters() respData = smb.SMBEchoResponse_Data()
echoData = smb.SMBEcho_Data(SMBCommand['Data'])
respParameters['SequenceNumber'] = 1 respData['Data'] = echoData['Data']
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData
errorCode = STATUS_SUCCESS smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smbComTreeDisconnect(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_DISCONNECT)
# Check if the Tid matches the Tid trying to disconnect respParameters = b'' respData = b''
if recvPacket['Tid'] in connData['ConnectedShares']: smbServer.log("Disconnecting Share(%d:%s)" % (recvPacket['Tid'],connData['ConnectedShares'][recvPacket['Tid']]['shareName'])) del(connData['ConnectedShares'][recvPacket['Tid']]) errorCode = STATUS_SUCCESS else: # STATUS_SMB_BAD_TID errorCode = STATUS_SMB_BAD_TID
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smbComLogOffAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOGOFF_ANDX)
# Check if the Uid matches the user trying to logoff respParameters = b'' respData = b'' if recvPacket['Uid'] != connData['Uid']: # STATUS_SMB_BAD_UID errorCode = STATUS_SMB_BAD_UID else: errorCode = STATUS_SUCCESS
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData connData['Uid'] = 0 connData['Authenticated'] = False
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComQueryInformation2(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION2) respParameters = smb.SMBQueryInformation2Response_Parameters() respData = b''
queryInformation2 = smb.SMBQueryInformation2_Parameters(SMBCommand['Parameters']) errorCode = 0xFF if queryInformation2['Fid'] in connData['OpenedFiles']: errorCode = STATUS_SUCCESS pathName = connData['OpenedFiles'][queryInformation2['Fid']]['FileName'] try: (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName) respParameters['CreateDate'] = getSMBDate(ctime) respParameters['CreationTime'] = getSMBTime(ctime) respParameters['LastAccessDate'] = getSMBDate(atime) respParameters['LastAccessTime'] = getSMBTime(atime) respParameters['LastWriteDate'] = getSMBDate(mtime) respParameters['LastWriteTime'] = getSMBTime(mtime) respParameters['FileDataSize'] = size respParameters['FileAllocationSize'] = size attribs = 0 if os.path.isdir(pathName): attribs = smb.SMB_FILE_ATTRIBUTE_DIRECTORY if os.path.isfile(pathName): attribs = smb.SMB_FILE_ATTRIBUTE_NORMAL respParameters['FileAttributes'] = attribs except Exception as e: smbServer.log('smbComQueryInformation2 %s' % e,logging.ERROR) errorCode = STATUS_ACCESS_DENIED
if errorCode > 0: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket): # TODO: Fully implement this connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX) respParameters = smb.SMBNtCreateAndXResponse_Parameters() respData = b''
ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters']) ntCreateAndXData = smb.SMBNtCreateAndX_Data( flags = recvPacket['Flags2'], data = SMBCommand['Data'])
#if ntCreateAndXParameters['CreateFlags'] & 0x10: # NT_CREATE_REQUEST_EXTENDED_RESPONSE # respParameters = smb.SMBNtCreateAndXExtendedResponse_Parameters() # respParameters['VolumeGUID'] = '\x00'
# Get the Tid associated if recvPacket['Tid'] in connData['ConnectedShares']: # If we have a rootFid, the path is relative to that fid errorCode = STATUS_SUCCESS if ntCreateAndXParameters['RootFid'] > 0: path = connData['OpenedFiles'][ntCreateAndXParameters['RootFid']]['FileName'] LOG.debug("RootFid present %s!" % path) else: if 'path' in connData['ConnectedShares'][recvPacket['Tid']]: path = connData['ConnectedShares'][recvPacket['Tid']]['path'] else: path = 'NONE' errorCode = STATUS_ACCESS_DENIED
deleteOnClose = False
fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],ntCreateAndXData['FileName']).replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) createDisposition = ntCreateAndXParameters['Disposition'] mode = 0
if createDisposition == smb.FILE_SUPERSEDE: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb.FILE_OVERWRITE_IF == smb.FILE_OVERWRITE_IF: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb.FILE_OVERWRITE == smb.FILE_OVERWRITE: if os.path.exists(pathName) is True: mode |= os.O_TRUNC else: errorCode = STATUS_NO_SUCH_FILE elif createDisposition & smb.FILE_OPEN_IF == smb.FILE_OPEN_IF: if os.path.exists(pathName) is True: mode |= os.O_TRUNC else: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb.FILE_CREATE == smb.FILE_CREATE: if os.path.exists(pathName) is True: errorCode = STATUS_OBJECT_NAME_COLLISION else: mode |= os.O_CREAT elif createDisposition & smb.FILE_OPEN == smb.FILE_OPEN: if os.path.exists(pathName) is not True and (str(pathName) in smbServer.getRegisteredNamedPipes()) is not True: errorCode = STATUS_NO_SUCH_FILE
if errorCode == STATUS_SUCCESS: desiredAccess = ntCreateAndXParameters['AccessMask'] if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ): mode |= os.O_RDONLY if (desiredAccess & smb.FILE_WRITE_DATA) or (desiredAccess & smb.GENERIC_WRITE): if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ): mode |= os.O_RDWR #| os.O_APPEND else: mode |= os.O_WRONLY #| os.O_APPEND if desiredAccess & smb.GENERIC_ALL: mode |= os.O_RDWR #| os.O_APPEND
createOptions = ntCreateAndXParameters['CreateOptions'] if mode & os.O_CREAT == os.O_CREAT: if createOptions & smb.FILE_DIRECTORY_FILE == smb.FILE_DIRECTORY_FILE: try: # Let's create the directory os.mkdir(pathName) mode = os.O_RDONLY except Exception as e: smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName,mode,e),logging.ERROR) errorCode = STATUS_ACCESS_DENIED if createOptions & smb.FILE_NON_DIRECTORY_FILE == smb.FILE_NON_DIRECTORY_FILE: # If the file being opened is a directory, the server MUST fail the request with # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server # response. if os.path.isdir(pathName) is True: errorCode = STATUS_FILE_IS_A_DIRECTORY
if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE: deleteOnClose = True
if errorCode == STATUS_SUCCESS: try: if os.path.isdir(pathName) and sys.platform == 'win32': fid = VOID_FILE_DESCRIPTOR else: if sys.platform == 'win32': mode |= os.O_BINARY if str(pathName) in smbServer.getRegisteredNamedPipes(): fid = PIPE_FILE_DESCRIPTOR sock = socket.socket() sock.connect(smbServer.getRegisteredNamedPipes()[str(pathName)]) else: fid = os.open(pathName, mode) except Exception as e: smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName,mode,e),logging.ERROR) #print e fid = 0 errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID
if errorCode == STATUS_SUCCESS: # Simple way to generate a fid if len(connData['OpenedFiles']) == 0: fakefid = 1 else: fakefid = list(connData['OpenedFiles'].keys())[-1] + 1 respParameters['Fid'] = fakefid respParameters['CreateAction'] = createDisposition if fid == PIPE_FILE_DESCRIPTOR: respParameters['FileAttributes'] = 0x80 respParameters['IsDirectory'] = 0 respParameters['CreateTime'] = 0 respParameters['LastAccessTime'] = 0 respParameters['LastWriteTime'] = 0 respParameters['LastChangeTime'] = 0 respParameters['AllocationSize'] = 4096 respParameters['EndOfFile'] = 0 respParameters['FileType'] = 2 respParameters['IPCState'] = 0x5ff else: if os.path.isdir(pathName): respParameters['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY respParameters['IsDirectory'] = 1 else: respParameters['IsDirectory'] = 0 respParameters['FileAttributes'] = ntCreateAndXParameters['FileAttributes'] # Let's get this file's information respInfo, errorCode = queryPathInformation('',pathName,level= smb.SMB_QUERY_FILE_ALL_INFO) if errorCode == STATUS_SUCCESS: respParameters['CreateTime'] = respInfo['CreationTime'] respParameters['LastAccessTime'] = respInfo['LastAccessTime'] respParameters['LastWriteTime'] = respInfo['LastWriteTime'] respParameters['LastChangeTime'] = respInfo['LastChangeTime'] respParameters['FileAttributes'] = respInfo['ExtFileAttributes'] respParameters['AllocationSize'] = respInfo['AllocationSize'] respParameters['EndOfFile'] = respInfo['EndOfFile'] else: respParameters = b'' respData = b''
if errorCode == STATUS_SUCCESS: # Let's store the fid for the connection # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode)) connData['OpenedFiles'][fakefid] = {} connData['OpenedFiles'][fakefid]['FileHandle'] = fid connData['OpenedFiles'][fakefid]['FileName'] = pathName connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose if fid == PIPE_FILE_DESCRIPTOR: connData['OpenedFiles'][fakefid]['Socket'] = sock else: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComOpenAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_OPEN_ANDX) respParameters = smb.SMBOpenAndXResponse_Parameters() respData = b''
openAndXParameters = smb.SMBOpenAndX_Parameters(SMBCommand['Parameters']) openAndXData = smb.SMBOpenAndX_Data( flags = recvPacket['Flags2'], data = SMBCommand['Data'])
# Get the Tid associated if recvPacket['Tid'] in connData['ConnectedShares']: path = connData['ConnectedShares'][recvPacket['Tid']]['path'] openedFile, mode, pathName, errorCode = openFile(path, decodeSMBString(recvPacket['Flags2'],openAndXData['FileName']), openAndXParameters['DesiredAccess'], openAndXParameters['FileAttributes'], openAndXParameters['OpenMode']) else: errorCode = STATUS_SMB_BAD_TID
if errorCode == STATUS_SUCCESS: # Simple way to generate a fid fid = len(connData['OpenedFiles']) + 1 if len(connData['OpenedFiles']) == 0: fid = 1 else: fid = list(connData['OpenedFiles'].keys())[-1] + 1 respParameters['Fid'] = fid if mode & os.O_CREAT: # File did not exist and was created respParameters['Action'] = 0x2 elif mode & os.O_RDONLY: # File existed and was opened respParameters['Action'] = 0x1 elif mode & os.O_APPEND: # File existed and was opened respParameters['Action'] = 0x1 else: # File existed and was truncated respParameters['Action'] = 0x3
# Let's store the fid for the connection #smbServer.log('Opening file %s' % pathName) connData['OpenedFiles'][fid] = {} connData['OpenedFiles'][fid]['FileHandle'] = openedFile connData['OpenedFiles'][fid]['FileName'] = pathName connData['OpenedFiles'][fid]['DeleteOnClose'] = False else: respParameters = b'' respData = b''
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId)
resp = smb.NewSMBPacket() resp['Flags1'] = smb.SMB.FLAGS1_REPLY resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE
resp['Tid'] = recvPacket['Tid'] resp['Mid'] = recvPacket['Mid'] resp['Pid'] = connData['Pid']
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX) respParameters = smb.SMBTreeConnectAndXResponse_Parameters() respData = smb.SMBTreeConnectAndXResponse_Data()
treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters'])
if treeConnectAndXParameters['Flags'] & 0x8: respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters()
treeConnectAndXData = smb.SMBTreeConnectAndX_Data( flags = recvPacket['Flags2'] ) treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength'] treeConnectAndXData.fromString(SMBCommand['Data'])
errorCode = STATUS_SUCCESS
## Process here the request, does the share exist? UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])
# Is this a UNC? if ntpath.ismount(UNCOrShare): path = UNCOrShare.split('\\')[3] else: path = ntpath.basename(UNCOrShare)
share = searchShare(connId, path, smbServer) if share is not None: # Simple way to generate a Tid if len(connData['ConnectedShares']) == 0: tid = 1 else: tid = list(connData['ConnectedShares'].keys())[-1] + 1 connData['ConnectedShares'][tid] = share connData['ConnectedShares'][tid]['shareName'] = path resp['Tid'] = tid #smbServer.log("Connecting Share(%d:%s)" % (tid,path)) else: smbServer.log("TreeConnectAndX not found %s" % path, logging.ERROR) errorCode = STATUS_OBJECT_PATH_NOT_FOUND resp['ErrorCode'] = errorCode >> 16 resp['ErrorClass'] = errorCode & 0xff ## respParameters['OptionalSupport'] = smb.SMB.SMB_SUPPORT_SEARCH_BITS
if path == 'IPC$': respData['Service'] = 'IPC' else: respData['Service'] = path respData['PadLen'] = 0 respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS' ).decode()
respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData
resp['Uid'] = connData['Uid'] resp.addCommand(respSMBCommand)
# Sign the packet if needed if connData['SignatureEnabled']: smbServer.signSMBv1(connData, resp, connData['SigningSessionKey'], connData['SigningChallengeResponse']) smbServer.setConnectionData(connId, connData)
return None, [resp], errorCode
def smbComSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus = False)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)
# From [MS-SMB] # When extended security is being used (see section 3.2.4.2.4), the # request MUST take the following form # [..] # WordCount (1 byte): The value of this field MUST be 0x0C. if SMBCommand['WordCount'] == 12: # Extended security. Here we deal with all SPNEGO stuff respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters() respData = smb.SMBSessionSetupAndX_Extended_Response_Data(flags = recvPacket['Flags2']) sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data() sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength'] sessionSetupData.fromString(SMBCommand['Data']) connData['Capabilities'] = sessionSetupParameters['Capabilities']
rawNTLM = False if struct.unpack('B',sessionSetupData['SecurityBlob'][0:1])[0] == ASN1_AID: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob']) token = blob['MechToken'] if len(blob['MechTypes'][0]) > 0: # Is this GSSAPI NTLM or something else we don't support? mechType = blob['MechTypes'][0] if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']: # Nope, do we know it? if mechType in MechTypes: mechStr = MechTypes[mechType] else: mechStr = hexlify(mechType) smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL) # We don't know the token, we answer back again saying # we just support NTLM. # ToDo: Build this into a SPNEGO_NegTokenResp() 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' respParameters['SecurityBlobLength'] = len(respToken) respData['SecurityBlobLength'] = respParameters['SecurityBlobLength'] respData['SecurityBlob'] = respToken respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS()) respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS()) respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED
elif struct.unpack('B',sessionSetupData['SecurityBlob'][0:1])[0] == ASN1_SUPPORTED_MECH: # AUTH packet blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob']) token = blob['ResponseToken'] else: # No GSSAPI stuff, raw NTLMSSP rawNTLM = True token = sessionSetupData['SecurityBlob']
# Here we only handle NTLMSSP, depending on what stage of the # authentication we are, we act on it messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]
if messageType == 0x01: # NEGOTIATE_MESSAGE negotiateMessage = ntlm.NTLMAuthNegotiate() negotiateMessage.fromString(token) # Let's store it in the connection data connData['NEGOTIATE_MESSAGE'] = negotiateMessage # Let's build the answer flags # TODO: Parse all the flags. With this we're leaving some clients out
ansFlags = 0
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM: ansFlags |= ntlm.NTLM_NEGOTIATE_OEM
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET
# Generate the AV_PAIRS av_pairs = ntlm.AV_PAIRS() # TODO: Put the proper data from SMBSERVER config av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le') av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le') av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000) )
challengeMessage = ntlm.NTLMAuthChallenge() challengeMessage['flags'] = ansFlags challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le')) challengeMessage['domain_max_len'] = challengeMessage['domain_len'] challengeMessage['domain_offset'] = 40 + 16 challengeMessage['challenge'] = smbServer.getSMBChallenge() challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le') challengeMessage['TargetInfoFields_len'] = len(av_pairs) challengeMessage['TargetInfoFields_max_len'] = len(av_pairs) challengeMessage['TargetInfoFields'] = av_pairs challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name']) challengeMessage['Version'] = b'\xff'*8 challengeMessage['VersionLen'] = 8
if rawNTLM is False: respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegResult'] = b'\x01' respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
respToken['ResponseToken'] = challengeMessage.getData() else: respToken = challengeMessage
# Setting the packet to STATUS_MORE_PROCESSING errorCode = STATUS_MORE_PROCESSING_REQUIRED # Let's set up an UID for this connection and store it # in the connection's data # Picking a fixed value # TODO: Manage more UIDs for the same session connData['Uid'] = 10 # Let's store it in the connection data connData['CHALLENGE_MESSAGE'] = challengeMessage
elif messageType == 0x02: # CHALLENGE_MESSAGE raise Exception('Challenge Message raise, not implemented!') elif messageType == 0x03: # AUTHENTICATE_MESSAGE, here we deal with authentication authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % ( authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'), authenticateMessage['host_name'].decode('utf-16le'))) # Do we have credentials to check? if len(smbServer.getCredentials()) > 0: identity = authenticateMessage['user_name'].decode('utf-16le') # Do we have this user's credentials? if identity in smbServer.getCredentials(): # Process data: # Let's parse some data and keep it to ourselves in case it is asked uid, lmhash, nthash = smbServer.getCredentials()[identity]
errorCode, sessionKey = computeNTLMv2(identity, lmhash, nthash, smbServer.getSMBChallenge(), authenticateMessage, connData['CHALLENGE_MESSAGE'], connData['NEGOTIATE_MESSAGE'])
if sessionKey is not None: connData['SignatureEnabled'] = False connData['SigningSessionKey'] = sessionKey connData['SignSequenceNumber'] = 1 else: errorCode = STATUS_LOGON_FAILURE else: # No credentials provided, let's grant access errorCode = STATUS_SUCCESS
if errorCode == STATUS_SUCCESS: connData['Authenticated'] = True respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegResult'] = b'\x00'
smbServer.log('User %s\\%s authenticated successfully' % (authenticateMessage['host_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) # Let's store it in the connection data connData['AUTHENTICATE_MESSAGE'] = authenticateMessage try: jtr_dump_path = smbServer.getJTRdumpPath() ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) smbServer.log(ntlm_hash_data['hash_string']) if jtr_dump_path is not '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path) except: smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path) else: respToken = SPNEGO_NegTokenResp() respToken['NegResult'] = b'\x02' smbServer.log("Could not authenticate user!") else: raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
respParameters['SecurityBlobLength'] = len(respToken) respData['SecurityBlobLength'] = respParameters['SecurityBlobLength'] respData['SecurityBlob'] = respToken.getData()
else: # Process Standard Security respParameters = smb.SMBSessionSetupAndXResponse_Parameters() respData = smb.SMBSessionSetupAndXResponse_Data() sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters']) sessionSetupData = smb.SMBSessionSetupAndX_Data() sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength'] sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength'] sessionSetupData.fromString(SMBCommand['Data']) connData['Capabilities'] = sessionSetupParameters['Capabilities'] # Do the verification here, for just now we grant access # TODO: Manage more UIDs for the same session errorCode = STATUS_SUCCESS connData['Uid'] = 10 connData['Authenticated'] = True respParameters['Action'] = 0 smbServer.log('User %s\\%s authenticated successfully (basic)' % (sessionSetupData['PrimaryDomain'], sessionSetupData['Account'])) try: jtr_dump_path = smbServer.getJTRdumpPath() ntlm_hash_data = outputToJohnFormat( b'', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd'] ) smbServer.log(ntlm_hash_data['hash_string']) if jtr_dump_path is not '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path) except: smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
respData['NativeOS'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS()) respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS()) respSMBCommand['Parameters'] = respParameters respSMBCommand['Data'] = respData
# From now on, the client can ask for other commands connData['Authenticated'] = True # For now, just switching to nobody #os.setregid(65534,65534) #os.setreuid(65534,65534) smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smbComNegotiate(connId, smbServer, SMBCommand, recvPacket ): connData = smbServer.getConnectionData(connId, checkStatus = False) connData['Pid'] = recvPacket['Pid']
SMBCommand = smb.SMBCommand(recvPacket['Data'][0]) respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NEGOTIATE)
resp = smb.NewSMBPacket() resp['Flags1'] = smb.SMB.FLAGS1_REPLY resp['Pid'] = connData['Pid'] resp['Tid'] = recvPacket['Tid'] resp['Mid'] = recvPacket['Mid']
# TODO: We support more dialects, and parse them accordingly dialects = SMBCommand['Data'].split(b'\x02') try: index = dialects.index(b'NT LM 0.12\x00') - 1 # Let's fill the data for NTLM if recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY: resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE #resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS _dialects_data = smb.SMBExtended_Security_Data() _dialects_data['ServerGUID'] = b'A'*16 blob = SPNEGO_NegTokenInit() blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] _dialects_data['SecurityBlob'] = blob.getData()
_dialects_parameters = smb.SMBExtended_Security_Parameters() _dialects_parameters['Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY | smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS | smb.SMB.CAP_UNICODE _dialects_parameters['ChallengeLength'] = 0
else: resp['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE _dialects_parameters = smb.SMBNTLMDialect_Parameters() _dialects_data= smb.SMBNTLMDialect_Data() _dialects_data['Payload'] = '' if 'EncryptionKey' in connData: _dialects_data['Challenge'] = connData['EncryptionKey'] _dialects_parameters['ChallengeLength'] = len(_dialects_data.getData()) else: # TODO: Handle random challenges, now one that can be used with rainbow tables _dialects_data['Challenge'] = b'\x11\x22\x33\x44\x55\x66\x77\x88' _dialects_parameters['ChallengeLength'] = 8 _dialects_parameters['Capabilities'] = smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS
# Let's see if we need to support RPC_REMOTE_APIS config = smbServer.getServerConfig() if config.has_option('global','rpc_apis'): if config.getboolean('global', 'rpc_apis') is True: _dialects_parameters['Capabilities'] |= smb.SMB.CAP_RPC_REMOTE_APIS
_dialects_parameters['DialectIndex'] = index #_dialects_parameters['SecurityMode'] = smb.SMB.SECURITY_AUTH_ENCRYPTED | smb.SMB.SECURITY_SHARE_USER | smb.SMB.SECURITY_SIGNATURES_REQUIRED _dialects_parameters['SecurityMode'] = smb.SMB.SECURITY_AUTH_ENCRYPTED | smb.SMB.SECURITY_SHARE_USER _dialects_parameters['MaxMpxCount'] = 1 _dialects_parameters['MaxNumberVcs'] = 1 _dialects_parameters['MaxBufferSize'] = 64000 _dialects_parameters['MaxRawSize'] = 65536 _dialects_parameters['SessionKey'] = 0 _dialects_parameters['LowDateTime'] = 0 _dialects_parameters['HighDateTime'] = 0 _dialects_parameters['ServerTimeZone'] = 0
respSMBCommand['Data'] = _dialects_data respSMBCommand['Parameters'] = _dialects_parameters connData['_dialects_data'] = _dialects_data connData['_dialects_parameters'] = _dialects_parameters
except Exception as e: # No NTLM throw an error smbServer.log('smbComNegotiate: %s' % e, logging.ERROR) respSMBCommand['Data'] = struct.pack('<H',0xffff)
smbServer.setConnectionData(connId, connData)
resp.addCommand(respSMBCommand)
return None, [resp], STATUS_SUCCESS
def default(connId, smbServer, SMBCommand, recvPacket): # By default we return an SMB Packet with error not implemented smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'],logging.DEBUG) packet = smb.NewSMBPacket() packet['Flags1'] = smb.SMB.FLAGS1_REPLY packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS packet['Command'] = recvPacket['Command'] packet['Pid'] = recvPacket['Pid'] packet['Tid'] = recvPacket['Tid'] packet['Mid'] = recvPacket['Mid'] packet['Uid'] = recvPacket['Uid'] packet['Data'] = b'\x00\x00\x00' errorCode = STATUS_NOT_IMPLEMENTED packet['ErrorCode'] = errorCode >> 16 packet['ErrorClass'] = errorCode & 0xff
return None, [packet], errorCode
connData = smbServer.getConnectionData(connId, checkStatus = False)
respPacket = smb2.SMB2Packet() respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR respPacket['Status'] = STATUS_SUCCESS respPacket['CreditRequestResponse'] = 1 respPacket['Command'] = smb2.SMB2_NEGOTIATE respPacket['SessionID'] = 0 if isSMB1 is False: respPacket['MessageID'] = recvPacket['MessageID'] else: respPacket['MessageID'] = 0 respPacket['TreeID'] = 0
respSMBCommand = smb2.SMB2Negotiate_Response()
respSMBCommand['SecurityMode'] = 1 if isSMB1 is True: # Let's first parse the packet to see if the client supports SMB2 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
dialects = SMBCommand['Data'].split(b'\x02') if b'SMB 2.002\x00' in dialects or b'SMB 2.???\x00' in dialects: respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002 else: # Client does not support SMB2 fallbacking raise Exception('SMB2 not supported, fallbacking') else: respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002 respSMBCommand['ServerGuid'] = b'A'*16 respSMBCommand['Capabilities'] = 0 respSMBCommand['MaxTransactSize'] = 65536 respSMBCommand['MaxReadSize'] = 65536 respSMBCommand['MaxWriteSize'] = 65536 respSMBCommand['SystemTime'] = getFileTime(calendar.timegm(time.gmtime())) respSMBCommand['ServerStartTime'] = getFileTime(calendar.timegm(time.gmtime())) respSMBCommand['SecurityBufferOffset'] = 0x80
blob = SPNEGO_NegTokenInit() blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
respSMBCommand['Buffer'] = blob.getData() respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer'])
respPacket['Data'] = respSMBCommand
smbServer.setConnectionData(connId, connData)
return None, [respPacket], STATUS_SUCCESS
def smb2SessionSetup(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId, checkStatus = False)
respSMBCommand = smb2.SMB2SessionSetup_Response()
sessionSetupData = smb2.SMB2SessionSetup(recvPacket['Data'])
connData['Capabilities'] = sessionSetupData['Capabilities']
securityBlob = sessionSetupData['Buffer']
rawNTLM = False if struct.unpack('B',securityBlob[0:1])[0] == ASN1_AID: # NEGOTIATE packet blob = SPNEGO_NegTokenInit(securityBlob) token = blob['MechToken'] if len(blob['MechTypes'][0]) > 0: # Is this GSSAPI NTLM or something else we don't support? mechType = blob['MechTypes'][0] if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']: # Nope, do we know it? if mechType in MechTypes: mechStr = MechTypes[mechType] else: mechStr = hexlify(mechType) smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL) # We don't know the token, we answer back again saying # we just support NTLM. # ToDo: Build this into a SPNEGO_NegTokenResp() 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' respSMBCommand['SecurityBufferOffset'] = 0x48 respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken
return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED elif struct.unpack('B',securityBlob[0:1])[0] == ASN1_SUPPORTED_MECH: # AUTH packet blob = SPNEGO_NegTokenResp(securityBlob) token = blob['ResponseToken'] else: # No GSSAPI stuff, raw NTLMSSP rawNTLM = True token = securityBlob
# Here we only handle NTLMSSP, depending on what stage of the # authentication we are, we act on it messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]
if messageType == 0x01: # NEGOTIATE_MESSAGE negotiateMessage = ntlm.NTLMAuthNegotiate() negotiateMessage.fromString(token) # Let's store it in the connection data connData['NEGOTIATE_MESSAGE'] = negotiateMessage # Let's build the answer flags # TODO: Parse all the flags. With this we're leaving some clients out
ansFlags = 0
if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128 if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE: ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM: ansFlags |= ntlm.NTLM_NEGOTIATE_OEM
ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET
# Generate the AV_PAIRS av_pairs = ntlm.AV_PAIRS() # TODO: Put the proper data from SMBSERVER config av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le') av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le') av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000) )
challengeMessage = ntlm.NTLMAuthChallenge() challengeMessage['flags'] = ansFlags challengeMessage['domain_len'] = len(smbServer.getServerDomain().encode('utf-16le')) challengeMessage['domain_max_len'] = challengeMessage['domain_len'] challengeMessage['domain_offset'] = 40 + 16 challengeMessage['challenge'] = smbServer.getSMBChallenge() challengeMessage['domain_name'] = smbServer.getServerDomain().encode('utf-16le') challengeMessage['TargetInfoFields_len'] = len(av_pairs) challengeMessage['TargetInfoFields_max_len'] = len(av_pairs) challengeMessage['TargetInfoFields'] = av_pairs challengeMessage['TargetInfoFields_offset'] = 40 + 16 + len(challengeMessage['domain_name']) challengeMessage['Version'] = b'\xff'*8 challengeMessage['VersionLen'] = 8
if rawNTLM is False: respToken = SPNEGO_NegTokenResp() # accept-incomplete. We want more data respToken['NegResult'] = b'\x01' respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
respToken['ResponseToken'] = challengeMessage.getData() else: respToken = challengeMessage
# Setting the packet to STATUS_MORE_PROCESSING errorCode = STATUS_MORE_PROCESSING_REQUIRED # Let's set up an UID for this connection and store it # in the connection's data # Picking a fixed value # TODO: Manage more UIDs for the same session connData['Uid'] = random.randint(1,0xffffffff) # Let's store it in the connection data connData['CHALLENGE_MESSAGE'] = challengeMessage
elif messageType == 0x02: # CHALLENGE_MESSAGE raise Exception('Challenge Message raise, not implemented!') elif messageType == 0x03: # AUTHENTICATE_MESSAGE, here we deal with authentication authenticateMessage = ntlm.NTLMAuthChallengeResponse() authenticateMessage.fromString(token) smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % ( authenticateMessage['domain_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'), authenticateMessage['host_name'].decode('utf-16le'))) # TODO: Check the credentials! Now granting permissions # Do we have credentials to check? if len(smbServer.getCredentials()) > 0: isGuest = False identity = authenticateMessage['user_name'].decode('utf-16le') # Do we have this user's credentials? if identity in smbServer.getCredentials(): # Process data: # Let's parse some data and keep it to ourselves in case it is asked uid, lmhash, nthash = smbServer.getCredentials()[identity]
errorCode, sessionKey = computeNTLMv2(identity, lmhash, nthash, smbServer.getSMBChallenge(), authenticateMessage, connData['CHALLENGE_MESSAGE'], connData['NEGOTIATE_MESSAGE'])
if sessionKey is not None: connData['SignatureEnabled'] = True connData['SigningSessionKey'] = sessionKey connData['SignSequenceNumber'] = 1 else: errorCode = STATUS_LOGON_FAILURE else: # No credentials provided, let's grant access isGuest = True errorCode = STATUS_SUCCESS
if errorCode == STATUS_SUCCESS: connData['Authenticated'] = True respToken = SPNEGO_NegTokenResp() # accept-completed respToken['NegResult'] = b'\x00' smbServer.log('User %s\\%s authenticated successfully' % ( authenticateMessage['host_name'].decode('utf-16le'), authenticateMessage['user_name'].decode('utf-16le'))) # Let's store it in the connection data connData['AUTHENTICATE_MESSAGE'] = authenticateMessage try: jtr_dump_path = smbServer.getJTRdumpPath() ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm']) smbServer.log(ntlm_hash_data['hash_string']) if jtr_dump_path is not '': writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path) except: smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
if isGuest: respSMBCommand['SessionFlags'] = 1
else: respToken = SPNEGO_NegTokenResp() respToken['NegResult'] = b'\x02' smbServer.log("Could not authenticate user!") else: raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
respSMBCommand['SecurityBufferOffset'] = 0x48 respSMBCommand['SecurityBufferLength'] = len(respToken) respSMBCommand['Buffer'] = respToken.getData()
# From now on, the client can ask for other commands connData['Authenticated'] = True # For now, just switching to nobody #os.setregid(65534,65534) #os.setreuid(65534,65534) smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smb2TreeConnect(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respPacket = smb2.SMB2Packet() respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR respPacket['Status'] = STATUS_SUCCESS respPacket['CreditRequestResponse'] = 1 respPacket['Command'] = recvPacket['Command'] respPacket['SessionID'] = connData['Uid'] respPacket['Reserved'] = recvPacket['Reserved'] respPacket['MessageID'] = recvPacket['MessageID'] respPacket['TreeID'] = recvPacket['TreeID']
respSMBCommand = smb2.SMB2TreeConnect_Response()
treeConnectRequest = smb2.SMB2TreeConnect(recvPacket['Data'])
errorCode = STATUS_SUCCESS
## Process here the request, does the share exist? path = recvPacket.getData()[treeConnectRequest['PathOffset']:][:treeConnectRequest['PathLength']] UNCOrShare = path.decode('utf-16le')
# Is this a UNC? if ntpath.ismount(UNCOrShare): path = UNCOrShare.split('\\')[3] else: path = ntpath.basename(UNCOrShare)
share = searchShare(connId, path.upper(), smbServer) if share is not None: # Simple way to generate a Tid if len(connData['ConnectedShares']) == 0: tid = 1 else: tid = list(connData['ConnectedShares'].keys())[-1] + 1 connData['ConnectedShares'][tid] = share connData['ConnectedShares'][tid]['shareName'] = path respPacket['TreeID'] = tid smbServer.log("Connecting Share(%d:%s)" % (tid,path)) else: smbServer.log("SMB2_TREE_CONNECT not found %s" % path, logging.ERROR) errorCode = STATUS_OBJECT_PATH_NOT_FOUND respPacket['Status'] = errorCode ##
if path.upper() == 'IPC$': respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_PIPE respSMBCommand['ShareFlags'] = 0x30 else: respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_DISK respSMBCommand['ShareFlags'] = 0x0
respSMBCommand['Capabilities'] = 0 respSMBCommand['MaximalAccess'] = 0x000f01ff
respPacket['Data'] = respSMBCommand
# Sign the packet if needed if connData['SignatureEnabled']: smbServer.signSMBv2(respPacket, connData['SigningSessionKey']) smbServer.setConnectionData(connId, connData)
return None, [respPacket], errorCode
def smb2Create(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Create_Response()
ntCreateRequest = smb2.SMB2Create(recvPacket['Data'])
respSMBCommand['Buffer'] = b'\x00' # Get the Tid associated if recvPacket['TreeID'] in connData['ConnectedShares']: # If we have a rootFid, the path is relative to that fid errorCode = STATUS_SUCCESS if 'path' in connData['ConnectedShares'][recvPacket['TreeID']]: path = connData['ConnectedShares'][recvPacket['TreeID']]['path'] else: path = 'NONE' errorCode = STATUS_ACCESS_DENIED
deleteOnClose = False
fileName = os.path.normpath(ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le').replace('\\','/')) if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'): # strip leading '/' fileName = fileName[1:] pathName = os.path.join(path,fileName) createDisposition = ntCreateRequest['CreateDisposition'] mode = 0
if createDisposition == smb2.FILE_SUPERSEDE: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE: if os.path.exists(pathName) is True: mode |= os.O_TRUNC else: errorCode = STATUS_NO_SUCH_FILE elif createDisposition & smb2.FILE_OPEN_IF == smb2.FILE_OPEN_IF: if os.path.exists(pathName) is True: mode |= os.O_TRUNC else: mode |= os.O_TRUNC | os.O_CREAT elif createDisposition & smb2.FILE_CREATE == smb2.FILE_CREATE: if os.path.exists(pathName) is True: errorCode = STATUS_OBJECT_NAME_COLLISION else: mode |= os.O_CREAT elif createDisposition & smb2.FILE_OPEN == smb2.FILE_OPEN: if os.path.exists(pathName) is not True and (str(pathName) in smbServer.getRegisteredNamedPipes()) is not True: errorCode = STATUS_NO_SUCH_FILE
if errorCode == STATUS_SUCCESS: desiredAccess = ntCreateRequest['DesiredAccess'] if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ): mode |= os.O_RDONLY if (desiredAccess & smb2.FILE_WRITE_DATA) or (desiredAccess & smb2.GENERIC_WRITE): if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ): mode |= os.O_RDWR #| os.O_APPEND else: mode |= os.O_WRONLY #| os.O_APPEND if desiredAccess & smb2.GENERIC_ALL: mode |= os.O_RDWR #| os.O_APPEND
createOptions = ntCreateRequest['CreateOptions'] if mode & os.O_CREAT == os.O_CREAT: if createOptions & smb2.FILE_DIRECTORY_FILE == smb2.FILE_DIRECTORY_FILE: try: # Let's create the directory os.mkdir(pathName) mode = os.O_RDONLY except Exception as e: smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName,mode,e),logging.ERROR) errorCode = STATUS_ACCESS_DENIED if createOptions & smb2.FILE_NON_DIRECTORY_FILE == smb2.FILE_NON_DIRECTORY_FILE: # If the file being opened is a directory, the server MUST fail the request with # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server # response. if os.path.isdir(pathName) is True: errorCode = STATUS_FILE_IS_A_DIRECTORY
if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE: deleteOnClose = True
if errorCode == STATUS_SUCCESS: try: if os.path.isdir(pathName) and sys.platform == 'win32': fid = VOID_FILE_DESCRIPTOR else: if sys.platform == 'win32': mode |= os.O_BINARY if str(pathName) in smbServer.getRegisteredNamedPipes(): fid = PIPE_FILE_DESCRIPTOR sock = socket.socket() sock.connect(smbServer.getRegisteredNamedPipes()[str(pathName)]) else: fid = os.open(pathName, mode) except Exception as e: smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName,mode,e),logging.ERROR) #print e fid = 0 errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_SMB_BAD_TID
if errorCode == STATUS_SUCCESS: # Simple way to generate a fid fakefid = uuid.generate()
respSMBCommand['FileID'] = fakefid respSMBCommand['CreateAction'] = createDisposition
if fid == PIPE_FILE_DESCRIPTOR: respSMBCommand['CreationTime'] = 0 respSMBCommand['LastAccessTime'] = 0 respSMBCommand['LastWriteTime'] = 0 respSMBCommand['ChangeTime'] = 0 respSMBCommand['AllocationSize'] = 4096 respSMBCommand['EndOfFile'] = 0 respSMBCommand['FileAttributes'] = 0x80
else: if os.path.isdir(pathName): respSMBCommand['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY else: respSMBCommand['FileAttributes'] = ntCreateRequest['FileAttributes'] # Let's get this file's information respInfo, errorCode = queryPathInformation('',pathName,level= smb.SMB_QUERY_FILE_ALL_INFO) if errorCode == STATUS_SUCCESS: respSMBCommand['CreationTime'] = respInfo['CreationTime'] respSMBCommand['LastAccessTime'] = respInfo['LastAccessTime'] respSMBCommand['LastWriteTime'] = respInfo['LastWriteTime'] respSMBCommand['LastChangeTime'] = respInfo['LastChangeTime'] respSMBCommand['FileAttributes'] = respInfo['ExtFileAttributes'] respSMBCommand['AllocationSize'] = respInfo['AllocationSize'] respSMBCommand['EndOfFile'] = respInfo['EndOfFile']
if errorCode == STATUS_SUCCESS: # Let's store the fid for the connection # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode)) connData['OpenedFiles'][fakefid] = {} connData['OpenedFiles'][fakefid]['FileHandle'] = fid connData['OpenedFiles'][fakefid]['FileName'] = pathName connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose connData['OpenedFiles'][fakefid]['Open'] = {} connData['OpenedFiles'][fakefid]['Open']['EnumerationLocation'] = 0 connData['OpenedFiles'][fakefid]['Open']['EnumerationSearchPattern'] = '' if fid == PIPE_FILE_DESCRIPTOR: connData['OpenedFiles'][fakefid]['Socket'] = sock else: respSMBCommand = smb2.SMB2Error()
if errorCode == STATUS_SUCCESS: connData['LastRequest']['SMB2_CREATE'] = respSMBCommand smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
def smb2Close(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Close_Response()
closeRequest = smb2.SMB2Close(recvPacket['Data'])
if closeRequest['FileID'].getData() == b'\xff'*16: # Let's take the data from the lastRequest if 'SMB2_CREATE' in connData['LastRequest']: fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = closeRequest['FileID'].getData() else: fileID = closeRequest['FileID'].getData()
if fileID in connData['OpenedFiles']: errorCode = STATUS_SUCCESS fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] pathName = connData['OpenedFiles'][fileID]['FileName'] infoRecord = None try: if fileHandle == PIPE_FILE_DESCRIPTOR: connData['OpenedFiles'][fileID]['Socket'].close() elif fileHandle != VOID_FILE_DESCRIPTOR: os.close(fileHandle) infoRecord, errorCode = queryFileInformation(os.path.dirname(pathName), os.path.basename(pathName), smb2.SMB2_FILE_NETWORK_OPEN_INFO) except Exception as e: smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR) errorCode = STATUS_INVALID_HANDLE else: # Check if the file was marked for removal if connData['OpenedFiles'][fileID]['DeleteOnClose'] is True: try: if os.path.isdir(pathName): shutil.rmtree(connData['OpenedFiles'][fileID]['FileName']) else: os.remove(connData['OpenedFiles'][fileID]['FileName']) except Exception as e: smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED
# Now fill out the response if infoRecord is not None: respSMBCommand['CreationTime'] = infoRecord['CreationTime'] respSMBCommand['LastAccessTime'] = infoRecord['LastAccessTime'] respSMBCommand['LastWriteTime'] = infoRecord['LastWriteTime'] respSMBCommand['ChangeTime'] = infoRecord['ChangeTime'] respSMBCommand['AllocationSize'] = infoRecord['AllocationSize'] respSMBCommand['EndofFile'] = infoRecord['EndOfFile'] respSMBCommand['FileAttributes'] = infoRecord['FileAttributes'] if errorCode == STATUS_SUCCESS: del(connData['OpenedFiles'][fileID]) else: errorCode = STATUS_INVALID_HANDLE
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smb2QueryInfo(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2QueryInfo_Response()
queryInfo = smb2.SMB2QueryInfo(recvPacket['Data'])
errorCode = STATUS_SUCCESS
respSMBCommand['OutputBufferOffset'] = 0x48 respSMBCommand['Buffer'] = b'\x00'
if queryInfo['FileID'].getData() == b'\xff'*16: # Let's take the data from the lastRequest if 'SMB2_CREATE' in connData['LastRequest']: fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = queryInfo['FileID'].getData() else: fileID = queryInfo['FileID'].getData()
if recvPacket['TreeID'] in connData['ConnectedShares']: if fileID in connData['OpenedFiles']: fileName = connData['OpenedFiles'][fileID]['FileName']
if queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILE: if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_INTERNAL_INFO: # No need to call queryFileInformation, we have the data here infoRecord = smb2.FileInternalInformation() infoRecord['IndexNumber'] = fileID else: infoRecord, errorCode = queryFileInformation(os.path.dirname(fileName), os.path.basename(fileName), queryInfo['FileInfoClass']) elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM: if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_EA_INFO: infoRecord = b'\x00'*4 else: infoRecord = queryFsInformation(os.path.dirname(fileName), os.path.basename(fileName), queryInfo['FileInfoClass']) elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY: # Failing for now, until we support it infoRecord = None errorCode = STATUS_ACCESS_DENIED else: smbServer.log("queryInfo not supported (%x)" % queryInfo['InfoType'], logging.ERROR)
if infoRecord is not None: respSMBCommand['OutputBufferLength'] = len(infoRecord) respSMBCommand['Buffer'] = infoRecord else: errorCode = STATUS_INVALID_HANDLE else: errorCode = STATUS_SMB_BAD_TID
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smb2SetInfo(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2SetInfo_Response()
setInfo = smb2.SMB2SetInfo(recvPacket['Data'])
errorCode = STATUS_SUCCESS
if setInfo['FileID'].getData() == b'\xff'*16: # Let's take the data from the lastRequest if 'SMB2_CREATE' in connData['LastRequest']: fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = setInfo['FileID'].getData() else: fileID = setInfo['FileID'].getData()
if recvPacket['TreeID'] in connData['ConnectedShares']: path = connData['ConnectedShares'][recvPacket['TreeID']]['path'] if fileID in connData['OpenedFiles']: pathName = connData['OpenedFiles'][fileID]['FileName']
if setInfo['InfoType'] == smb2.SMB2_0_INFO_FILE: # The file information is being set informationLevel = setInfo['FileInfoClass'] if informationLevel == smb2.SMB2_FILE_DISPOSITION_INFO: infoRecord = smb.SMBSetFileDispositionInfo(setInfo['Buffer']) if infoRecord['DeletePending'] > 0: # Mark this file for removal after closed connData['OpenedFiles'][fileID]['DeleteOnClose'] = True elif informationLevel == smb2.SMB2_FILE_BASIC_INFO: infoRecord = smb.SMBSetFileBasicInfo(setInfo['Buffer']) # Creation time won't be set, the other ones we play with. atime = infoRecord['LastWriteTime'] if atime == 0: atime = -1 else: atime = getUnixTime(atime) mtime = infoRecord['ChangeTime'] if mtime == 0: mtime = -1 else: mtime = getUnixTime(mtime) if atime > 0 and mtime > 0: os.utime(pathName,(atime,mtime)) elif informationLevel == smb2.SMB2_FILE_END_OF_FILE_INFO: fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] infoRecord = smb.SMBSetFileEndOfFileInfo(setInfo['Buffer']) if infoRecord['EndOfFile'] > 0: os.lseek(fileHandle, infoRecord['EndOfFile']-1, 0) os.write(fileHandle, b'\x00') elif informationLevel == smb2.SMB2_FILE_RENAME_INFO: renameInfo = smb2.FILE_RENAME_INFORMATION_TYPE_2(setInfo['Buffer']) newPathName = os.path.join(path,renameInfo['FileName'].decode('utf-16le').replace('\\', '/')) if renameInfo['ReplaceIfExists'] == 0 and os.path.exists(newPathName): return [smb2.SMB2Error()], None, STATUS_OBJECT_NAME_COLLISION try: os.rename(pathName,newPathName) connData['OpenedFiles'][fileID]['FileName'] = newPathName except Exception as e: smbServer.log("smb2SetInfo: %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: smbServer.log('Unknown level for set file info! 0x%x' % informationLevel, logging.ERROR) # UNSUPPORTED errorCode = STATUS_NOT_SUPPORTED #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM: # # The underlying object store information is being set. # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass']) #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY: # # The security information is being set. # # Failing for now, until we support it # infoRecord = None # errorCode = STATUS_ACCESS_DENIED #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_QUOTA: # # The underlying object store quota information is being set. # setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass']) else: smbServer.log("setInfo not supported (%x)" % setInfo['InfoType'], logging.ERROR)
else: errorCode = STATUS_INVALID_HANDLE else: errorCode = STATUS_SMB_BAD_TID
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smb2Write(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Write_Response() writeRequest = smb2.SMB2Write(recvPacket['Data'])
respSMBCommand['Buffer'] = b'\x00'
if writeRequest['FileID'].getData() == b'\xff'*16: # Let's take the data from the lastRequest if 'SMB2_CREATE' in connData['LastRequest']: fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = writeRequest['FileID'].getData() else: fileID = writeRequest['FileID'].getData()
if fileID in connData['OpenedFiles']: fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] errorCode = STATUS_SUCCESS try: if fileHandle != PIPE_FILE_DESCRIPTOR: offset = writeRequest['Offset'] # If we're trying to write past the file end we just skip the write call (Vista does this) if os.lseek(fileHandle, 0, 2) >= offset: os.lseek(fileHandle,offset,0) os.write(fileHandle,writeRequest['Buffer']) else: sock = connData['OpenedFiles'][fileID]['Socket'] sock.send(writeRequest['Buffer'])
respSMBCommand['Count'] = writeRequest['Length'] respSMBCommand['Remaining']= 0xff except Exception as e: smbServer.log('SMB2_WRITE: %s' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smb2Read(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Read_Response() readRequest = smb2.SMB2Read(recvPacket['Data'])
respSMBCommand['Buffer'] = b'\x00'
if readRequest['FileID'].getData() == b'\xff'*16: # Let's take the data from the lastRequest if 'SMB2_CREATE' in connData['LastRequest']: fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = readRequest['FileID'].getData() else: fileID = readRequest['FileID'].getData()
if fileID in connData['OpenedFiles']: fileHandle = connData['OpenedFiles'][fileID]['FileHandle'] errorCode = 0 try: if fileHandle != PIPE_FILE_DESCRIPTOR: offset = readRequest['Offset'] os.lseek(fileHandle,offset,0) content = os.read(fileHandle,readRequest['Length']) else: sock = connData['OpenedFiles'][fileID]['Socket'] content = sock.recv(readRequest['Length'])
respSMBCommand['DataOffset'] = 0x50 respSMBCommand['DataLength'] = len(content) respSMBCommand['DataRemaining']= 0 respSMBCommand['Buffer'] = content except Exception as e: smbServer.log('SMB2_READ: %s ' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smb2Flush(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Flush_Response() flushRequest = smb2.SMB2Flush(recvPacket['Data'])
if flushRequest['FileID'].getData() in connData['OpenedFiles']: fileHandle = connData['OpenedFiles'][flushRequest['FileID'].getData()]['FileHandle'] errorCode = STATUS_SUCCESS try: os.fsync(fileHandle) except Exception as e: smbServer.log("SMB2_FLUSH %s" % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_HANDLE
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smb2QueryDirectory(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId) respSMBCommand = smb2.SMB2QueryDirectory_Response() queryDirectoryRequest = smb2.SMB2QueryDirectory(recvPacket['Data'])
respSMBCommand['Buffer'] = b'\x00'
# The server MUST locate the tree connection, as specified in section 3.3.5.2.11. if (recvPacket['TreeID'] in connData['ConnectedShares']) is False: return [smb2.SMB2Error()], None, STATUS_NETWORK_NAME_DELETED
# Next, the server MUST locate the open for the directory to be queried # If no open is found, the server MUST fail the request with STATUS_FILE_CLOSED if queryDirectoryRequest['FileID'].getData() == b'\xff'*16: # Let's take the data from the lastRequest if 'SMB2_CREATE' in connData['LastRequest']: fileID = connData['LastRequest']['SMB2_CREATE']['FileID'] else: fileID = queryDirectoryRequest['FileID'].getData() else: fileID = queryDirectoryRequest['FileID'].getData()
if (fileID in connData['OpenedFiles']) is False: return [smb2.SMB2Error()], None, STATUS_FILE_CLOSED
# If the open is not an open to a directory, the request MUST be failed # with STATUS_INVALID_PARAMETER. if os.path.isdir(connData['OpenedFiles'][fileID]['FileName']) is False: return [smb2.SMB2Error()], None, STATUS_INVALID_PARAMETER
# If any other information class is specified in the FileInformationClass # field of the SMB2 QUERY_DIRECTORY Request, the server MUST fail the # operation with STATUS_INVALID_INFO_CLASS. if queryDirectoryRequest['FileInformationClass'] not in ( smb2.FILE_DIRECTORY_INFORMATION, smb2.FILE_FULL_DIRECTORY_INFORMATION, smb2.FILEID_FULL_DIRECTORY_INFORMATION, smb2.FILE_BOTH_DIRECTORY_INFORMATION, smb2.FILEID_BOTH_DIRECTORY_INFORMATION, smb2.FILENAMES_INFORMATION): return [smb2.SMB2Error()], None, STATUS_INVALID_INFO_CLASS
# If SMB2_REOPEN is set in the Flags field of the SMB2 QUERY_DIRECTORY # Request, the server SHOULD<326> set Open.EnumerationLocation to 0 # and Open.EnumerationSearchPattern to an empty string. if queryDirectoryRequest['Flags'] & smb2.SMB2_REOPEN: connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0 connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = ''
# If SMB2_RESTART_SCANS is set in the Flags field of the SMB2 # QUERY_DIRECTORY Request, the server MUST set # Open.EnumerationLocation to 0. if queryDirectoryRequest['Flags'] & smb2.SMB2_RESTART_SCANS: connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0
# If Open.EnumerationLocation is 0 and Open.EnumerationSearchPattern # is an empty string, then Open.EnumerationSearchPattern MUST be set # to the search pattern specified in the SMB2 QUERY_DIRECTORY by # FileNameOffset and FileNameLength. If FileNameLength is 0, the server # SHOULD<327> set Open.EnumerationSearchPattern as "*" to search all entries.
pattern = queryDirectoryRequest['Buffer'].decode('utf-16le') if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0 and \ connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] == '': if pattern == '': pattern = '*' connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern
# If SMB2_INDEX_SPECIFIED is set and FileNameLength is not zero, # the server MUST set Open.EnumerationSearchPattern to the search pattern # specified in the request by FileNameOffset and FileNameLength. if queryDirectoryRequest['Flags'] & smb2.SMB2_INDEX_SPECIFIED and \ queryDirectoryRequest['FileNameLength'] > 0: connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern
pathName = os.path.join(os.path.normpath(connData['OpenedFiles'][fileID]['FileName']),pattern) searchResult, searchCount, errorCode = findFirst2(os.path.dirname(pathName), os.path.basename(pathName), queryDirectoryRequest['FileInformationClass'], smb.ATTR_DIRECTORY, isSMB2 = True )
if errorCode != STATUS_SUCCESS: return [smb2.SMB2Error()], None, errorCode
if searchCount > 2 and pattern == '*': # strip . and .. searchCount -= 2 searchResult = searchResult[2:]
if searchCount == 0 and connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0: return [smb2.SMB2Error()], None, STATUS_NO_SUCH_FILE
if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] < 0: return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES
totalData = 0 respData = b'' for nItem in range(connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'], searchCount): connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] += 1 if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY: # If single entry is requested we must clear the NextEntryOffset searchResult[nItem]['NextEntryOffset'] = 0 data = searchResult[nItem].getData() lenData = len(data) padLen = (8-(lenData % 8)) %8
if (totalData+lenData) >= queryDirectoryRequest['OutputBufferLength']: connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] -= 1 break else: respData += data + b'\x00'*padLen totalData += lenData + padLen
if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY: break
if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] >= searchCount: connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = -1
respSMBCommand['OutputBufferOffset'] = 0x48 respSMBCommand['OutputBufferLength'] = totalData respSMBCommand['Buffer'] = respData
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smb2ChangeNotify(connId, smbServer, recvPacket):
return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED
def smb2Echo(connId, smbServer, recvPacket):
respSMBCommand = smb2.SMB2Echo_Response()
return [respSMBCommand], None, STATUS_SUCCESS
def smb2TreeDisconnect(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2TreeDisconnect_Response()
if recvPacket['TreeID'] in connData['ConnectedShares']: smbServer.log("Disconnecting Share(%d:%s)" % ( recvPacket['TreeID'], connData['ConnectedShares'][recvPacket['TreeID']]['shareName'])) del(connData['ConnectedShares'][recvPacket['TreeID']]) errorCode = STATUS_SUCCESS else: # STATUS_SMB_BAD_TID errorCode = STATUS_SMB_BAD_TID
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smb2Logoff(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Logoff_Response()
if recvPacket['SessionID'] != connData['Uid']: # STATUS_SMB_BAD_UID errorCode = STATUS_SMB_BAD_UID else: errorCode = STATUS_SUCCESS
connData['Uid'] = 0 connData['Authenticated'] = False
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smb2Ioctl(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Ioctl_Response() ioctlRequest = smb2.SMB2Ioctl(recvPacket['Data'])
ioctls = smbServer.getIoctls() if ioctlRequest['CtlCode'] in ioctls: outputData, errorCode = ioctls[ioctlRequest['CtlCode']](connId, smbServer, ioctlRequest) if errorCode == STATUS_SUCCESS: respSMBCommand['CtlCode'] = ioctlRequest['CtlCode'] respSMBCommand['FileID'] = ioctlRequest['FileID'] respSMBCommand['InputOffset'] = 0 respSMBCommand['InputCount'] = 0 respSMBCommand['OutputOffset'] = 0x70 respSMBCommand['OutputCount'] = len(outputData) respSMBCommand['Flags'] = 0 respSMBCommand['Buffer'] = outputData else: respSMBCommand = outputData else: smbServer.log("Ioctl not implemented command: 0x%x" % ioctlRequest['CtlCode'],logging.DEBUG) errorCode = STATUS_INVALID_DEVICE_REQUEST respSMBCommand = smb2.SMB2Error()
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smb2Lock(connId, smbServer, recvPacket): connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Lock_Response()
# I'm actually doing nothing.. just make MacOS happy ;) errorCode = STATUS_SUCCESS
smbServer.setConnectionData(connId, connData) return [respSMBCommand], None, errorCode
def smb2Cancel(connId, smbServer, recvPacket): # I'm actually doing nothing return [smb2.SMB2Error()], None, STATUS_CANCELLED
def default(connId, smbServer, recvPacket): # By default we return an SMB Packet with error not implemented smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'],logging.DEBUG) return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED
def fsctlDfsGetReferrals(connId, smbServer, ioctlRequest): return smb2.SMB2Error(), STATUS_FS_DRIVER_REQUIRED
def fsctlPipeTransceive(connId, smbServer, ioctlRequest): connData = smbServer.getConnectionData(connId)
ioctlResponse = ''
if ioctlRequest['FileID'].getData() in connData['OpenedFiles']: fileHandle = connData['OpenedFiles'][ioctlRequest['FileID'].getData()]['FileHandle'] errorCode = STATUS_SUCCESS try: if fileHandle != PIPE_FILE_DESCRIPTOR: errorCode = STATUS_INVALID_DEVICE_REQUEST else: sock = connData['OpenedFiles'][ioctlRequest['FileID'].getData()]['Socket'] sock.sendall(ioctlRequest['Buffer']) ioctlResponse = sock.recv(ioctlRequest['MaxOutputResponse']) except Exception as e: smbServer.log('fsctlPipeTransceive: %s ' % e, logging.ERROR) errorCode = STATUS_ACCESS_DENIED else: errorCode = STATUS_INVALID_DEVICE_REQUEST
smbServer.setConnectionData(connId, connData) return ioctlResponse, errorCode
def fsctlValidateNegotiateInfo(connId, smbServer, ioctlRequest): connData = smbServer.getConnectionData(connId)
errorCode = STATUS_SUCCESS
validateNegotiateInfo = smb2.VALIDATE_NEGOTIATE_INFO(ioctlRequest['Buffer']) validateNegotiateInfoResponse = smb2.VALIDATE_NEGOTIATE_INFO_RESPONSE() validateNegotiateInfoResponse['Capabilities'] = 0 validateNegotiateInfoResponse['Guid'] = b'A'*16 validateNegotiateInfoResponse['SecurityMode'] = 1 validateNegotiateInfoResponse['Dialect'] = smb2.SMB2_DIALECT_002
smbServer.setConnectionData(connId, connData) return validateNegotiateInfoResponse.getData(), errorCode
self.__SMB = server # In case of AF_INET6 the client_address contains 4 items, ignore the last 2 self.__ip, self.__port = client_address[:2] self.__request = request self.__connId = threading.currentThread().getName() self.__timeOut = 60*5 self.__select_poll = select_poll #self.__connId = os.getpid() socketserver.BaseRequestHandler.__init__(self, request, client_address, server)
self.__SMB.log("Incoming connection (%s,%d)" % (self.__ip, self.__port)) self.__SMB.addConnection(self.__connId, self.__ip, self.__port) while True: try: # First of all let's get the NETBIOS packet session = nmb.NetBIOSTCPSession(self.__SMB.getServerName(), 'HOST', self.__ip, sess_port=self.__port, sock=self.__request, select_poll=self.__select_poll) try: p = session.recv_packet(self.__timeOut) except nmb.NetBIOSTimeout: raise except nmb.NetBIOSError: break
if p.get_type() == nmb.NETBIOS_SESSION_REQUEST: # Someone is requesting a session, we're gonna accept them all :) _, rn, my = p.get_trailer().split(' ') remote_name = nmb.decode_name(b'\x20'+rn) myname = nmb.decode_name(b'\x20'+my) self.__SMB.log("NetBIOS Session request (%s,%s,%s)" % (self.__ip, remote_name[1].strip(), myname[1])) r = nmb.NetBIOSSessionPacket() r.set_type(nmb.NETBIOS_SESSION_POSITIVE_RESPONSE) r.set_trailer(p.get_trailer()) self.__request.send(r.rawData()) else: resp = self.__SMB.processRequest(self.__connId, p.get_trailer()) # Send all the packets received. Except for big transactions this should be # a single packet for i in resp: if hasattr(i, 'getData'): session.send_packet(i.getData()) else: session.send_packet(i) except Exception as e: self.__SMB.log("Handle: %s" % e) #import traceback #traceback.print_exc() break
# Thread/process is dying, we should tell the main SMB thread to remove all this thread data self.__SMB.log("Closing down connection (%s,%d)" % (self.__ip, self.__port)) self.__SMB.removeConnection(self.__connId) return socketserver.BaseRequestHandler.finish(self)
#class SMBSERVER(socketserver.ForkingMixIn, socketserver.TCPServer): socketserver.TCPServer.allow_reuse_address = True socketserver.TCPServer.__init__(self, server_address, handler_class)
# Server name and OS to be presented whenever is necessary self.__serverName = '' self.__serverOS = '' self.__serverDomain = '' self.__challenge = '' self.__log = None
# Our ConfigParser data self.__serverConfig = config_parser
# Our credentials to be used during the server's lifetime self.__credentials = {}
# Our log file self.__logFile = ''
# Registered Named Pipes, format is PipeName,Socket self.__registeredNamedPipes = {}
# JTR dump path self.__jtr_dump_path = ''
# SMB2 Support flag = default not active self.__SMB2Support = False
# Our list of commands we will answer, by default the NOT IMPLEMENTED one self.__smbCommandsHandler = SMBCommands() self.__smbTrans2Handler = TRANS2Commands() self.__smbTransHandler = TRANSCommands() self.__smbNTTransHandler = NTTRANSCommands() self.__smb2CommandsHandler = SMB2Commands() self.__IoctlHandler = Ioctls()
self.__smbNTTransCommands = { # NT IOCTL, can't find doc for this 0xff :self.__smbNTTransHandler.default }
self.__smbTransCommands = { '\\PIPE\\LANMAN' :self.__smbTransHandler.lanMan, smb.SMB.TRANS_TRANSACT_NMPIPE :self.__smbTransHandler.transactNamedPipe, } self.__smbTrans2Commands = { smb.SMB.TRANS2_FIND_FIRST2 :self.__smbTrans2Handler.findFirst2, smb.SMB.TRANS2_FIND_NEXT2 :self.__smbTrans2Handler.findNext2, smb.SMB.TRANS2_QUERY_FS_INFORMATION :self.__smbTrans2Handler.queryFsInformation, smb.SMB.TRANS2_QUERY_PATH_INFORMATION :self.__smbTrans2Handler.queryPathInformation, smb.SMB.TRANS2_QUERY_FILE_INFORMATION :self.__smbTrans2Handler.queryFileInformation, smb.SMB.TRANS2_SET_FILE_INFORMATION :self.__smbTrans2Handler.setFileInformation, smb.SMB.TRANS2_SET_PATH_INFORMATION :self.__smbTrans2Handler.setPathInformation }
self.__smbCommands = { #smb.SMB.SMB_COM_FLUSH: self.__smbCommandsHandler.smbComFlush, smb.SMB.SMB_COM_CREATE_DIRECTORY: self.__smbCommandsHandler.smbComCreateDirectory, smb.SMB.SMB_COM_DELETE_DIRECTORY: self.__smbCommandsHandler.smbComDeleteDirectory, smb.SMB.SMB_COM_RENAME: self.__smbCommandsHandler.smbComRename, smb.SMB.SMB_COM_DELETE: self.__smbCommandsHandler.smbComDelete, smb.SMB.SMB_COM_NEGOTIATE: self.__smbCommandsHandler.smbComNegotiate, smb.SMB.SMB_COM_SESSION_SETUP_ANDX: self.__smbCommandsHandler.smbComSessionSetupAndX, smb.SMB.SMB_COM_LOGOFF_ANDX: self.__smbCommandsHandler.smbComLogOffAndX, smb.SMB.SMB_COM_TREE_CONNECT_ANDX: self.__smbCommandsHandler.smbComTreeConnectAndX, smb.SMB.SMB_COM_TREE_DISCONNECT: self.__smbCommandsHandler.smbComTreeDisconnect, smb.SMB.SMB_COM_ECHO: self.__smbCommandsHandler.smbComEcho, smb.SMB.SMB_COM_QUERY_INFORMATION: self.__smbCommandsHandler.smbQueryInformation, smb.SMB.SMB_COM_TRANSACTION2: self.__smbCommandsHandler.smbTransaction2, smb.SMB.SMB_COM_TRANSACTION: self.__smbCommandsHandler.smbTransaction, # Not needed for now smb.SMB.SMB_COM_NT_TRANSACT: self.__smbCommandsHandler.smbNTTransact, smb.SMB.SMB_COM_QUERY_INFORMATION_DISK: self.__smbCommandsHandler.smbQueryInformationDisk, smb.SMB.SMB_COM_OPEN_ANDX: self.__smbCommandsHandler.smbComOpenAndX, smb.SMB.SMB_COM_QUERY_INFORMATION2: self.__smbCommandsHandler.smbComQueryInformation2, smb.SMB.SMB_COM_READ_ANDX: self.__smbCommandsHandler.smbComReadAndX, smb.SMB.SMB_COM_READ: self.__smbCommandsHandler.smbComRead, smb.SMB.SMB_COM_WRITE_ANDX: self.__smbCommandsHandler.smbComWriteAndX, smb.SMB.SMB_COM_WRITE: self.__smbCommandsHandler.smbComWrite, smb.SMB.SMB_COM_CLOSE: self.__smbCommandsHandler.smbComClose, smb.SMB.SMB_COM_LOCKING_ANDX: self.__smbCommandsHandler.smbComLockingAndX, smb.SMB.SMB_COM_NT_CREATE_ANDX: self.__smbCommandsHandler.smbComNtCreateAndX, 0xFF: self.__smbCommandsHandler.default }
self.__smb2Ioctls = { smb2.FSCTL_DFS_GET_REFERRALS: self.__IoctlHandler.fsctlDfsGetReferrals, # smb2.FSCTL_PIPE_PEEK: self.__IoctlHandler.fsctlPipePeek, # smb2.FSCTL_PIPE_WAIT: self.__IoctlHandler.fsctlPipeWait, smb2.FSCTL_PIPE_TRANSCEIVE: self.__IoctlHandler.fsctlPipeTransceive, # smb2.FSCTL_SRV_COPYCHUNK: self.__IoctlHandler.fsctlSrvCopyChunk, # smb2.FSCTL_SRV_ENUMERATE_SNAPSHOTS: self.__IoctlHandler.fsctlSrvEnumerateSnapshots, # smb2.FSCTL_SRV_REQUEST_RESUME_KEY: self.__IoctlHandler.fsctlSrvRequestResumeKey, # smb2.FSCTL_SRV_READ_HASH: self.__IoctlHandler.fsctlSrvReadHash, # smb2.FSCTL_SRV_COPYCHUNK_WRITE: self.__IoctlHandler.fsctlSrvCopyChunkWrite, # smb2.FSCTL_LMR_REQUEST_RESILIENCY: self.__IoctlHandler.fsctlLmrRequestResiliency, # smb2.FSCTL_QUERY_NETWORK_INTERFACE_INFO: self.__IoctlHandler.fsctlQueryNetworkInterfaceInfo, # smb2.FSCTL_SET_REPARSE_POINT: self.__IoctlHandler.fsctlSetReparsePoint, # smb2.FSCTL_DFS_GET_REFERRALS_EX: self.__IoctlHandler.fsctlDfsGetReferralsEx, # smb2.FSCTL_FILE_LEVEL_TRIM: self.__IoctlHandler.fsctlFileLevelTrim, smb2.FSCTL_VALIDATE_NEGOTIATE_INFO: self.__IoctlHandler.fsctlValidateNegotiateInfo, }
self.__smb2Commands = { smb2.SMB2_NEGOTIATE: self.__smb2CommandsHandler.smb2Negotiate, smb2.SMB2_SESSION_SETUP: self.__smb2CommandsHandler.smb2SessionSetup, smb2.SMB2_LOGOFF: self.__smb2CommandsHandler.smb2Logoff, smb2.SMB2_TREE_CONNECT: self.__smb2CommandsHandler.smb2TreeConnect, smb2.SMB2_TREE_DISCONNECT: self.__smb2CommandsHandler.smb2TreeDisconnect, smb2.SMB2_CREATE: self.__smb2CommandsHandler.smb2Create, smb2.SMB2_CLOSE: self.__smb2CommandsHandler.smb2Close, smb2.SMB2_FLUSH: self.__smb2CommandsHandler.smb2Flush, smb2.SMB2_READ: self.__smb2CommandsHandler.smb2Read, smb2.SMB2_WRITE: self.__smb2CommandsHandler.smb2Write, smb2.SMB2_LOCK: self.__smb2CommandsHandler.smb2Lock, smb2.SMB2_IOCTL: self.__smb2CommandsHandler.smb2Ioctl, smb2.SMB2_CANCEL: self.__smb2CommandsHandler.smb2Cancel, smb2.SMB2_ECHO: self.__smb2CommandsHandler.smb2Echo, smb2.SMB2_QUERY_DIRECTORY: self.__smb2CommandsHandler.smb2QueryDirectory, smb2.SMB2_CHANGE_NOTIFY: self.__smb2CommandsHandler.smb2ChangeNotify, smb2.SMB2_QUERY_INFO: self.__smb2CommandsHandler.smb2QueryInfo, smb2.SMB2_SET_INFO: self.__smb2CommandsHandler.smb2SetInfo, # smb2.SMB2_OPLOCK_BREAK: self.__smb2CommandsHandler.smb2SessionSetup, 0xFF: self.__smb2CommandsHandler.default }
# List of active connections self.__activeConnections = {}
return self.__smb2Ioctls
return self.__credentials
try: del(self.__activeConnections[name]) except: pass self.log("Remaining connections %s" % list(self.__activeConnections.keys()))
self.__activeConnections[name] = {} # Let's init with some know stuff we will need to have # TODO: Document what's in there #print "Current Connections", self.__activeConnections.keys() self.__activeConnections[name]['PacketNum'] = 0 self.__activeConnections[name]['ClientIP'] = ip self.__activeConnections[name]['ClientPort'] = port self.__activeConnections[name]['Uid'] = 0 self.__activeConnections[name]['ConnectedShares'] = {} self.__activeConnections[name]['OpenedFiles'] = {} # SID results for findfirst2 self.__activeConnections[name]['SIDs'] = {} self.__activeConnections[name]['LastRequest'] = {} self.__activeConnections[name]['SignatureEnabled']= False self.__activeConnections[name]['SigningChallengeResponse']= '' self.__activeConnections[name]['SigningSessionKey']= b'' self.__activeConnections[name]['Authenticated']= False
return self.__activeConnections
self.__activeConnections[connId] = data #print "setConnectionData" #print self.__activeConnections
conn = self.__activeConnections[connId] if checkStatus is True: if ('Authenticated' in conn) is not True: # Can't keep going further raise Exception("User not Authenticated!") return conn
return self.__registeredNamedPipes
self.__registeredNamedPipes[str(pipeName)] = address return True
if pipeName in self.__registeredNamedPipes: del(self.__registeredNamedPipes[str(pipeName)]) return True return False
if transCommand in self.__smbTransCommands: del(self.__smbTransCommands[transCommand])
# If you call this function, callback will replace # the current Transaction sub command. # (don't get confused with the Transaction smbCommand) # If the transaction sub command doesn't not exist, it is added # If the transaction sub command exists, it returns the original function # replaced # # callback MUST be declared as: # callback(connId, smbServer, recvPacket, parameters, data, maxDataCount=0) # # WHERE: # # connId : the connection Id, used to grab/update information about # the current connection # smbServer : the SMBServer instance available for you to ask # configuration data # recvPacket : the full SMBPacket that triggered this command # parameters : the transaction parameters # data : the transaction data # maxDataCount: the max amount of data that can be transferred agreed # with the client # # and MUST return: # respSetup, respParameters, respData, errorCode # # WHERE: # # respSetup: the setup response of the transaction # respParameters: the parameters response of the transaction # respData: the data response of the transaction # errorCode: the NT error code
if transCommand in self.__smbTransCommands: originalCommand = self.__smbTransCommands[transCommand] else: originalCommand = None
self.__smbTransCommands[transCommand] = callback return originalCommand
if transCommand in self.__smbTrans2Commands: del(self.__smbTrans2Commands[transCommand])
# Here we should add to __smbTrans2Commands # Same description as Transaction if transCommand in self.__smbTrans2Commands: originalCommand = self.__smbTrans2Commands[transCommand] else: originalCommand = None
self.__smbTrans2Commands[transCommand] = callback return originalCommand
if transCommand in self.__smbNTTransCommands: del(self.__smbNTTransCommands[transCommand])
# Here we should add to __smbNTTransCommands # Same description as Transaction if transCommand in self.__smbNTTransCommands: originalCommand = self.__smbNTTransCommands[transCommand] else: originalCommand = None
self.__smbNTTransCommands[transCommand] = callback return originalCommand
if smbCommand in self.__smbCommands: del(self.__smbCommands[smbCommand])
# Here we should add to self.__smbCommands # If you call this function, callback will replace # the current smbCommand. # If smbCommand doesn't not exist, it is added # If SMB command exists, it returns the original function replaced # # callback MUST be declared as: # callback(connId, smbServer, SMBCommand, recvPacket) # # WHERE: # # connId : the connection Id, used to grab/update information about # the current connection # smbServer : the SMBServer instance available for you to ask # configuration data # SMBCommand: the SMBCommand itself, with its data and parameters. # Check smb.py:SMBCommand() for a reference # recvPacket: the full SMBPacket that triggered this command # # and MUST return: # <list of respSMBCommands>, <list of packets>, errorCode # <list of packets> has higher preference over commands, in case you # want to change the whole packet # errorCode: the NT error code # # For SMB_COM_TRANSACTION2, SMB_COM_TRANSACTION and SMB_COM_NT_TRANSACT # the callback function is slightly different: # # callback(connId, smbServer, SMBCommand, recvPacket, transCommands) # # WHERE: # # transCommands: a list of transaction subcommands already registered #
if smbCommand in self.__smbCommands: originalCommand = self.__smbCommands[smbCommand] else: originalCommand = None
self.__smbCommands[smbCommand] = callback return originalCommand
if smb2Command in self.__smb2Commands: del(self.__smb2Commands[smb2Command])
if smb2Command in self.__smb2Commands: originalCommand = self.__smb2Commands[smb2Command] else: originalCommand = None
self.__smb2Commands[smb2Command] = callback return originalCommand
self.__log.log(level,msg)
return self.__serverName
return self.__serverOS
return self.__serverDomain
return self.__challenge
return self.__serverConfig
self.__serverConfig = config
return self.__jtr_dump_path
# TODO: Control here the max amount of processes we want to launch # returning False, closes the connection return True
# This logic MUST be applied for messages sent in response to any of the higher-layer actions and in # compliance with the message sequencing rules. # * The client or server that sends the message MUST provide the 32-bit sequence number for this # message, as specified in sections 3.2.4.1 and 3.3.4.1. # * The SMB_FLAGS2_SMB_SECURITY_SIGNATURE flag in the header MUST be set. # * To generate the signature, a 32-bit sequence number is copied into the # least significant 32 bits of the SecuritySignature field and the remaining # 4 bytes are set to 0x00. # * The MD5 algorithm, as specified in [RFC1321], MUST be used to generate a hash of the SMB # message from the start of the SMB Header, which is defined as follows. # CALL MD5Init( md5context ) # CALL MD5Update( md5context, Connection.SigningSessionKey ) # CALL MD5Update( md5context, Connection.SigningChallengeResponse ) # CALL MD5Update( md5context, SMB message ) # CALL MD5Final( digest, md5context ) # SET signature TO the first 8 bytes of the digest # The resulting 8-byte signature MUST be copied into the SecuritySignature field of the SMB Header, # after which the message can be transmitted.
#print "seq(%d) signingSessionKey %r, signingChallengeResponse %r" % (connData['SignSequenceNumber'], signingSessionKey, signingChallengeResponse) packet['SecurityFeatures'] = struct.pack('<q',connData['SignSequenceNumber']) # Sign with the sequence m = hashlib.md5() m.update( signingSessionKey ) m.update( signingChallengeResponse ) if hasattr(packet, 'getData'): m.update( packet.getData() ) else: m.update( packet ) # Replace sequence with acual hash packet['SecurityFeatures'] = m.digest()[:8] connData['SignSequenceNumber'] +=2
packet['Signature'] = b'\x00'*16 packet['Flags'] |= smb2.SMB2_FLAGS_SIGNED signature = hmac.new(signingSessionKey, packet.getData(), hashlib.sha256).digest() packet['Signature'] = signature[:16] #print "%s" % packet['Signature'].encode('hex')
# TODO: Process batched commands. isSMB2 = False SMBCommand = None try: packet = smb.NewSMBPacket(data = data) SMBCommand = smb.SMBCommand(packet['Data'][0]) except: # Maybe a SMB2 packet? packet = smb2.SMB2Packet(data = data) connData = self.getConnectionData(connId, False) self.signSMBv2(packet, connData['SigningSessionKey']) isSMB2 = True
connData = self.getConnectionData(connId, False)
# We might have compound requests compoundedPacketsResponse = [] compoundedPackets = [] try: # Search out list of implemented commands # We provide them with: # connId : representing the data for this specific connection # self : the SMBSERVER if they want to ask data to it # SMBCommand : the SMBCommand they are expecting to process # packet : the received packet itself, in case they need more data than the actual command # Only for Transactions # transCommand: a list of transaction subcommands # We expect to get: # respCommands: a list of answers for the commands processed # respPacket : if the commands chose to directly craft packet/s, we use this and not the previous # this MUST be a list # errorCode : self explanatory if isSMB2 is False: # Is the client authenticated already? if connData['Authenticated'] is False and packet['Command'] not in (smb.SMB.SMB_COM_NEGOTIATE, smb.SMB.SMB_COM_SESSION_SETUP_ANDX): # Nope.. in that case he should only ask for a few commands, if not throw him out. errorCode = STATUS_ACCESS_DENIED respPackets = None respCommands = [smb.SMBCommand(packet['Command'])] else: if packet['Command'] == smb.SMB.SMB_COM_TRANSACTION2: respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet, self.__smbTrans2Commands) elif packet['Command'] == smb.SMB.SMB_COM_NT_TRANSACT: respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet, self.__smbNTTransCommands) elif packet['Command'] == smb.SMB.SMB_COM_TRANSACTION: respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet, self.__smbTransCommands) else: if packet['Command'] in self.__smbCommands: if self.__SMB2Support is True: if packet['Command'] == smb.SMB.SMB_COM_NEGOTIATE: try: respCommands, respPackets, errorCode = self.__smb2Commands[smb2.SMB2_NEGOTIATE](connId, self, packet, True) isSMB2 = True except Exception as e: import traceback traceback.print_exc() self.log('SMB2_NEGOTIATE: %s' % e, logging.ERROR) # If something went wrong, let's fallback to SMB1 respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet) #self.__SMB2Support = False pass else: respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet) else: respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']]( connId, self, SMBCommand, packet) else: respCommands, respPackets, errorCode = self.__smbCommands[255](connId, self, SMBCommand, packet)
compoundedPacketsResponse.append((respCommands, respPackets, errorCode)) compoundedPackets.append(packet)
else: # Is the client authenticated already? if connData['Authenticated'] is False and packet['Command'] not in (smb2.SMB2_NEGOTIATE, smb2.SMB2_SESSION_SETUP): # Nope.. in that case he should only ask for a few commands, if not throw him out. errorCode = STATUS_ACCESS_DENIED respPackets = None respCommands = [''] compoundedPacketsResponse.append((respCommands, respPackets, errorCode)) compoundedPackets.append(packet) else: done = False while not done: if packet['Command'] in self.__smb2Commands: if self.__SMB2Support is True: respCommands, respPackets, errorCode = self.__smb2Commands[packet['Command']]( connId, self, packet) else: respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet) else: respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet) # Let's store the result for this compounded packet compoundedPacketsResponse.append((respCommands, respPackets, errorCode)) compoundedPackets.append(packet) if packet['NextCommand'] != 0: data = data[packet['NextCommand']:] packet = smb2.SMB2Packet(data = data) else: done = True
except Exception as e: #import traceback #traceback.print_exc() # Something wen't wrong, defaulting to Bad user ID self.log('processRequest (0x%x,%s)' % (packet['Command'],e), logging.ERROR) raise
# We prepare the response packet to commands don't need to bother about that. connData = self.getConnectionData(connId, False)
# Force reconnection loop.. This is just a test.. client will send me back credentials :) #connData['PacketNum'] += 1 #if connData['PacketNum'] == 15: # connData['PacketNum'] = 0 # # Something wen't wrong, defaulting to Bad user ID # self.log('Sending BAD USER ID!', logging.ERROR) # #raise # packet['Flags1'] |= smb.SMB.FLAGS1_REPLY # packet['Flags2'] = 0 # errorCode = STATUS_SMB_BAD_UID # packet['ErrorCode'] = errorCode >> 16 # packet['ErrorClass'] = errorCode & 0xff # return [packet]
self.setConnectionData(connId, connData)
packetsToSend = [] for packetNum in range(len(compoundedPacketsResponse)): respCommands, respPackets, errorCode = compoundedPacketsResponse[packetNum] packet = compoundedPackets[packetNum] if respPackets is None: for respCommand in respCommands: if isSMB2 is False: respPacket = smb.NewSMBPacket() respPacket['Flags1'] = smb.SMB.FLAGS1_REPLY
# TODO this should come from a per session configuration respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | packet['Flags2'] & smb.SMB.FLAGS2_UNICODE #respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES #respPacket['Flags1'] = 0x98 #respPacket['Flags2'] = 0xc807
respPacket['Tid'] = packet['Tid'] respPacket['Mid'] = packet['Mid'] respPacket['Pid'] = packet['Pid'] respPacket['Uid'] = connData['Uid']
respPacket['ErrorCode'] = errorCode >> 16 respPacket['_reserved'] = errorCode >> 8 & 0xff respPacket['ErrorClass'] = errorCode & 0xff respPacket.addCommand(respCommand)
if connData['SignatureEnabled']: respPacket['Flags2'] |= smb.SMB.FLAGS2_SMB_SECURITY_SIGNATURE self.signSMBv1(connData, respPacket, connData['SigningSessionKey'], connData['SigningChallengeResponse'])
packetsToSend.append(respPacket) else: respPacket = smb2.SMB2Packet() respPacket['Flags'] = smb2.SMB2_FLAGS_SERVER_TO_REDIR if packetNum > 0: respPacket['Flags'] |= smb2.SMB2_FLAGS_RELATED_OPERATIONS respPacket['Status'] = errorCode respPacket['CreditRequestResponse'] = packet['CreditRequestResponse'] respPacket['Command'] = packet['Command'] respPacket['CreditCharge'] = packet['CreditCharge'] #respPacket['CreditCharge'] = 0 respPacket['Reserved'] = packet['Reserved'] respPacket['SessionID'] = connData['Uid'] respPacket['MessageID'] = packet['MessageID'] respPacket['TreeID'] = packet['TreeID'] if hasattr(respCommand, 'getData'): respPacket['Data'] = respCommand.getData() else: respPacket['Data'] = str(respCommand)
if connData['SignatureEnabled']: self.signSMBv2(respPacket, connData['SigningSessionKey'])
packetsToSend.append(respPacket) else: # The SMBCommand took care of building the packet packetsToSend = respPackets
if isSMB2 is True: # Let's build a compound answer finalData = b'' i = 0 for i in range(len(packetsToSend)-1): packet = packetsToSend[i] # Align to 8-bytes padLen = (8 - (len(packet) % 8) ) % 8 packet['NextCommand'] = len(packet) + padLen if hasattr(packet, 'getData'): finalData += packet.getData() + padLen*b'\x00' else: finalData += packet + padLen*b'\x00'
# Last one if hasattr(packetsToSend[len(packetsToSend)-1], 'getData'): finalData += packetsToSend[len(packetsToSend)-1].getData() else: finalData += packetsToSend[len(packetsToSend)-1] packetsToSend = [finalData]
# We clear the compound requests connData['LastRequest'] = {}
return packetsToSend
# TODO: Do a real config parser if self.__serverConfig is None: if configFile is None: configFile = 'smb.conf' self.__serverConfig = configparser.ConfigParser() self.__serverConfig.read(configFile)
self.__serverName = self.__serverConfig.get('global','server_name') self.__serverOS = self.__serverConfig.get('global','server_os') self.__serverDomain = self.__serverConfig.get('global','server_domain') self.__logFile = self.__serverConfig.get('global','log_file') if self.__serverConfig.has_option('global', 'challenge'): self.__challenge = b(self.__serverConfig.get('global', 'challenge')) else: self.__challenge = b'A'*8
if self.__serverConfig.has_option("global", "jtr_dump_path"): self.__jtr_dump_path = self.__serverConfig.get("global", "jtr_dump_path")
if self.__serverConfig.has_option("global", "SMB2Support"): self.__SMB2Support = self.__serverConfig.getboolean("global","SMB2Support") else: self.__SMB2Support = False
if self.__logFile != 'None': logging.basicConfig(filename = self.__logFile, level = logging.DEBUG, format="%(asctime)s: %(levelname)s: %(message)s", datefmt = '%m/%d/%Y %I:%M:%S %p') self.__log = LOG
# Process the credentials credentials_fname = self.__serverConfig.get('global','credentials_file') if credentials_fname is not "": cred = open(credentials_fname) line = cred.readline() while line: name, uid, lmhash, nthash = line.split(':') self.__credentials[name] = (uid, lmhash, nthash.strip('\r\n')) line = cred.readline() cred.close() self.log('Config file parsed')
# If we have hashes, normalize them if lmhash != '' or nthash != '': if len(lmhash) % 2: lmhash = '0%s' % lmhash if len(nthash) % 2: nthash = '0%s' % nthash try: # just in case they were converted already lmhash = a2b_hex(lmhash) nthash = a2b_hex(nthash) except: pass self.__credentials[name] = (uid, lmhash, nthash)
# For windows platforms, opening a directory is not an option, so we set a void FD
###################################################################### # HELPER CLASSES ######################################################################
DCERPCServer.__init__(self) self.wkssvcCallBacks = { 0: self.NetrWkstaGetInfo, } self.addCallbacks(('6BFFD098-A112-3610-9833-46C3F87E345A', '1.0'),'\\PIPE\\wkssvc', self.wkssvcCallBacks)
request = NetrWkstaGetInfo(data) self.log("NetrWkstaGetInfo Level: %d" % request['Level'])
answer = NetrWkstaGetInfoResponse()
if request['Level'] not in (100, 101): answer['ErrorCode'] = ERROR_INVALID_LEVEL return answer
answer['WkstaInfo']['tag'] = request['Level']
if request['Level'] == 100: # Windows. Decimal value 500. answer['WkstaInfo']['WkstaInfo100']['wki100_platform_id'] = 0x000001F4 answer['WkstaInfo']['WkstaInfo100']['wki100_computername'] = NULL answer['WkstaInfo']['WkstaInfo100']['wki100_langroup'] = NULL answer['WkstaInfo']['WkstaInfo100']['wki100_ver_major'] = 5 answer['WkstaInfo']['WkstaInfo100']['wki100_ver_minor'] = 0 else: # Windows. Decimal value 500. answer['WkstaInfo']['WkstaInfo101']['wki101_platform_id'] = 0x000001F4 answer['WkstaInfo']['WkstaInfo101']['wki101_computername'] = NULL answer['WkstaInfo']['WkstaInfo101']['wki101_langroup'] = NULL answer['WkstaInfo']['WkstaInfo101']['wki101_ver_major'] = 5 answer['WkstaInfo']['WkstaInfo101']['wki101_ver_minor'] = 0 answer['WkstaInfo']['WkstaInfo101']['wki101_lanroot'] = NULL
return answer
DCERPCServer.__init__(self)
self._shares = {} self.__serverConfig = None self.__logFile = None
self.srvsvcCallBacks = { 15: self.NetrShareEnum, 16: self.NetrShareGetInfo, 21: self.NetrServerGetInfo, }
self.addCallbacks(('4B324FC8-1670-01D3-1278-5A47BF6EE188', '3.0'),'\\PIPE\\srvsvc', self.srvsvcCallBacks)
self.__serverConfig = config
if configFile is not None: self.__serverConfig = configparser.ConfigParser() self.__serverConfig.read(configFile) sections = self.__serverConfig.sections() # Let's check the log file self.__logFile = self.__serverConfig.get('global','log_file') if self.__logFile != 'None': logging.basicConfig(filename = self.__logFile, level = logging.DEBUG, format="%(asctime)s: %(levelname)s: %(message)s", datefmt = '%m/%d/%Y %I:%M:%S %p')
# Remove the global one del(sections[sections.index('global')]) self._shares = {} for i in sections: self._shares[i] = dict(self.__serverConfig.items(i))
request = NetrShareGetInfo(data) self.log("NetrGetShareInfo Level: %d" % request['Level'])
s = request['NetName'][:-1].upper() answer = NetrShareGetInfoResponse() if s in self._shares: share = self._shares[s]
answer['InfoStruct']['tag'] = 1 answer['InfoStruct']['ShareInfo1']['shi1_netname']= s+'\x00' answer['InfoStruct']['ShareInfo1']['shi1_type'] = share['share type'] answer['InfoStruct']['ShareInfo1']['shi1_remark'] = share['comment']+'\x00' answer['ErrorCode'] = 0 else: answer['InfoStruct']['tag'] = 1 answer['InfoStruct']['ShareInfo1']= NULL answer['ErrorCode'] = 0x0906 #WERR_NET_NAME_NOT_FOUND
return answer
request = NetrServerGetInfo(data) self.log("NetrServerGetInfo Level: %d" % request['Level']) answer = NetrServerGetInfoResponse() answer['InfoStruct']['tag'] = 101 # PLATFORM_ID_NT = 500 answer['InfoStruct']['ServerInfo101']['sv101_platform_id'] = 500 answer['InfoStruct']['ServerInfo101']['sv101_name'] = request['ServerName'] # Windows 7 = 6.1 answer['InfoStruct']['ServerInfo101']['sv101_version_major'] = 6 answer['InfoStruct']['ServerInfo101']['sv101_version_minor'] = 1 # Workstation = 1 answer['InfoStruct']['ServerInfo101']['sv101_type'] = 1 answer['InfoStruct']['ServerInfo101']['sv101_comment'] = NULL answer['ErrorCode'] = 0 return answer
request = NetrShareEnum(data) self.log("NetrShareEnum Level: %d" % request['InfoStruct']['Level']) shareEnum = NetrShareEnumResponse() shareEnum['InfoStruct']['Level'] = 1 shareEnum['InfoStruct']['ShareInfo']['tag'] = 1 shareEnum['TotalEntries'] = len(self._shares) shareEnum['InfoStruct']['ShareInfo']['Level1']['EntriesRead'] = len(self._shares) shareEnum['ErrorCode'] = 0
for i in self._shares: shareInfo = SHARE_INFO_1() shareInfo['shi1_netname'] = i+'\x00' shareInfo['shi1_type'] = self._shares[i]['share type'] shareInfo['shi1_remark'] = self._shares[i]['comment']+'\x00' shareEnum['InfoStruct']['ShareInfo']['Level1']['Buffer'].append(shareInfo)
return shareEnum
""" SimpleSMBServer class - Implements a simple, customizable SMB Server
:param string listenAddress: the address you want the server to listen on :param integer listenPort: the port number you want the server to listen on :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 """ if configFile != '': self.__server = SMBSERVER((listenAddress,listenPort)) self.__server.processConfigFile(configFile) self.__smbConfig = None else: # Here we write a mini config for the server self.__smbConfig = configparser.ConfigParser() self.__smbConfig.add_section('global') self.__smbConfig.set('global','server_name',''.join([random.choice(string.ascii_letters) for _ in range(8)])) self.__smbConfig.set('global','server_os',''.join([random.choice(string.ascii_letters) for _ in range(8)]) ) self.__smbConfig.set('global','server_domain',''.join([random.choice(string.ascii_letters) for _ in range(8)]) ) self.__smbConfig.set('global','log_file','None') self.__smbConfig.set('global','rpc_apis','yes') self.__smbConfig.set('global','credentials_file','') self.__smbConfig.set('global', 'challenge', "A"*8)
# IPC always needed self.__smbConfig.add_section('IPC$') self.__smbConfig.set('IPC$','comment','') self.__smbConfig.set('IPC$','read only','yes') self.__smbConfig.set('IPC$','share type','3') self.__smbConfig.set('IPC$','path','') self.__server = SMBSERVER((listenAddress,listenPort), config_parser = self.__smbConfig) self.__server.processConfigFile()
# Now we have to register the MS-SRVS server. This specially important for # Windows 7+ and Mavericks clients since they WON'T (specially OSX) # ask for shares using MS-RAP.
self.__srvsServer = SRVSServer() self.__srvsServer.daemon = True self.__wkstServer = WKSTServer() self.__wkstServer.daemon = True self.__server.registerNamedPipe('srvsvc',('127.0.0.1',self.__srvsServer.getListenPort())) self.__server.registerNamedPipe('wkssvc',('127.0.0.1',self.__wkstServer.getListenPort()))
self.__srvsServer.start() self.__wkstServer.start() self.__server.serve_forever()
return self.__server.registerNamedPipe(pipeName, address)
return self.__server.unregisterNamedPipe(pipeName)
return self.__server.getRegisteredNamedPipes()
share = shareName.upper() self.__smbConfig.add_section(share) self.__smbConfig.set(share, 'comment', shareComment) self.__smbConfig.set(share, 'read only', readOnly) self.__smbConfig.set(share, 'share type', shareType) self.__smbConfig.set(share, 'path', sharePath) self.__server.setServerConfig(self.__smbConfig) self.__srvsServer.setServerConfig(self.__smbConfig) self.__server.processConfigFile() self.__srvsServer.processConfigFile()
self.__smbConfig.remove_section(shareName.upper()) self.__server.setServerConfig(self.__smbConfig) self.__srvsServer.setServerConfig(self.__smbConfig) self.__server.processConfigFile() self.__srvsServer.processConfigFile()
if challenge != '': self.__smbConfig.set('global', 'challenge', unhexlify(challenge)) self.__server.setServerConfig(self.__smbConfig) self.__server.processConfigFile()
self.__smbConfig.set('global','log_file',logFile) self.__server.setServerConfig(self.__smbConfig) self.__server.processConfigFile()
self.__smbConfig.set('global','credentials_file',logFile) self.__server.setServerConfig(self.__smbConfig) self.__server.processConfigFile()
self.__server.addCredential(name, uid, lmhash, nthash)
if value is True: self.__smbConfig.set("global", "SMB2Support", "True") else: self.__smbConfig.set("global", "SMB2Support", "False") self.__server.setServerConfig(self.__smbConfig) self.__server.processConfigFile() |