1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-24 17:18:48 -05:00
curl/tests/python_dependencies/impacket/smbserver.py
2020-02-27 18:05:43 +01:00

4178 lines
190 KiB
Python

# Copyright (c) 2003-2016 CORE Security Technologies
#
# 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
from __future__ import with_statement
import calendar
import socket
import time
import datetime
import struct
try: # Python 3
import configparser
except ImportError: # Python 2
import ConfigParser as configparser
import SocketServer
import threading
import logging
import logging.config
import ntpath
import os
import fnmatch
import errno
import sys
import random
import shutil
from binascii import hexlify
# For signing
from impacket import smb, nmb, ntlm, uuid, LOG
from impacket import smb3structs as smb2
from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, MechTypes, SPNEGO_NegTokenResp, ASN1_AID, ASN1_SUPPORTED_MECH
from impacket.nt_errors import STATUS_NO_MORE_FILES, STATUS_NETWORK_NAME_DELETED, STATUS_INVALID_PARAMETER, \
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
# These ones not defined in nt_errors
STATUS_SMB_BAD_UID = 0x005B0002
STATUS_SMB_BAD_TID = 0x00050002
try:
unicode # Python 2
except NameError:
unicode = str # Python 3
# 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
def outputToJohnFormat(challenge, username, domain, lmresponse, ntresponse):
# We don't want to add a possible failure here, since this is an
# extra bonus. We try, if it fails, returns nothing
ret_value = ''
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), hexlify(ntresponse)[:32], hexlify(ntresponse)[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), hexlify(ntresponse), hexlify(challenge)), '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, domain, hexlify(challenge), hexlify(ntresponse)[:32], hexlify(ntresponse)[32:]), 'hash_version':'ntlmv2'}
else:
# NTLMv1
ret_value = {'hash_string':'%s::%s:%s:%s:%s' % (username, domain, hexlify(lmresponse), hexlify(ntresponse), hexlify(challenge)), 'hash_version':'ntlm'}
except Exception as e:
LOG.error("outputToJohnFormat: %s" % e)
pass
return ret_value
def writeJohnOutputToFile(hash_string, hash_version, file_name):
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')
def decodeSMBString( flags, text ):
if flags & smb.SMB.FLAGS2_UNICODE:
return text.decode('utf-16le')
else:
return text
def encodeSMBString( flags, text ):
if flags & smb.SMB.FLAGS2_UNICODE:
return (text).encode('utf-16le')
else:
return text
def getFileTime(t):
t *= 10000000
t += 116444736000000000
return t
def getUnixTime(t):
t -= 116444736000000000
t /= 10000000
return t
def getSMBDate(t):
# TODO: Fix this :P
d = datetime.date.fromtimestamp(t)
year = d.year - 1980
ret = (year << 8) + (d.month << 4) + d.day
return ret
def getSMBTime(t):
# TODO: Fix this :P
d = datetime.datetime.fromtimestamp(t)
return (d.hour << 8) + (d.minute << 4) + d.second
def getShares(connId, smbServer):
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
def searchShare(connId, share, smbServer):
config = smbServer.getServerConfig()
if config.has_section(share):
return dict(config.items(share))
else:
return None
def openFile(path,fileName, accessMode, fileAttributes, openMode):
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
def queryFsInformation(path, filename, level=0):
if isinstance(filename,unicode):
encoding = 'utf-16le'
flags = smb.SMB.FLAGS2_UNICODE
else:
encoding = 'ascii'
flags = 0
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 = flags )
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
def findFirst2(path, fileName, level, searchAttributes, isSMB2 = False):
# 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 isinstance(fileName,unicode):
encoding = 'utf-16le'
flags = smb.SMB.FLAGS2_UNICODE
else:
encoding = 'ascii'
flags = 0
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 = flags )
elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO or level == smb2.SMB2_FILE_DIRECTORY_INFO:
item = smb.SMBFindFileDirectoryInfo( flags = flags )
elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO:
item = smb.SMBFindFileFullDirectoryInfo( flags = flags )
elif level == smb.SMB_FIND_INFO_STANDARD:
item = smb.SMBFindInfoStandard( flags = flags )
elif level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_FULL_DIRECTORY_INFO:
item = smb.SMBFindFileIdFullDirectoryInfo( flags = flags )
elif level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO:
item = smb.SMBFindFileIdBothDirectoryInfo( flags = flags )
elif level == smb.SMB_FIND_FILE_NAMES_INFO or level == smb2.SMB2_FILE_NAMES_INFO:
item = smb.SMBFindFileNamesInfo( flags = flags )
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 == True) and searchCount > 0:
searchResult[-1]['NextEntryOffset'] = 0
return searchResult, searchCount, errorCode
def queryFileInformation(path, filename, level):
#print "queryFileInfo path: %s, filename: %s, level:0x%x" % (path,filename,level)
return queryPathInformation(path,filename, level)
def 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
def queryDiskInformation(path):
# 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
class NTTRANSCommands:
def default(self, connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
pass
# Here we implement the NT transaction handlers
class TRANSCommands:
@staticmethod
def lanMan(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
# Minimal [MS-RAP] implementation, just to return the shares
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
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 = str(netServerInfo)
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
@staticmethod
def transactNamedPipe(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
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
class TRANS2Commands:
# All these commands return setup, parameters, data, errorCode
@staticmethod
def setPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
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
@staticmethod
def setFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
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, '\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
@staticmethod
def queryFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
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
@staticmethod
def queryPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
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
@staticmethod
def queryFsInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
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])
smbServer.setConnectionData(connId, connData)
return '','', data, errorCode
@staticmethod
def findNext2(connId, smbServer, recvPacket, parameters, data, maxDataCount):
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
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
@staticmethod
def findFirst2(connId, smbServer, recvPacket, parameters, data, maxDataCount):
connData = smbServer.getConnectionData(connId)
respSetup = ''
respParameters = ''
respData = ''
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'] )
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 = 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 += '\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
class SMBCommands:
@staticmethod
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'] = ''
if transParameters['DataOffset'] > 0:
dataOffset = transParameters['DataOffset'] - 63 - transParameters['SetupLength']
transData['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
else:
transData['Trans_Data'] = ''
# 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 = ''
parameters = ''
data = ''
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 == '' and parameters == '' and data == '':
# Something wen't wrong
respParameters = ''
respData = ''
else:
# Build the answer
data = str(data)
remainingData = len(data)
parameters = str(parameters)
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 = '\xFF' * padLen
respData['Pad1'] = padBytes
respParameters['ParameterOffset'] = 55 + len(setup) + padLen
else:
padLen = 0
respParameters['ParameterOffset'] = 0
respData['Pad1'] = ''
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'] = '\xFF' * pad2Len
respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len
else:
respParameters['DataOffset'] = 0
respData['Pad2'] = ''
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 = ''
respData = ''
errorCode = STATUS_NOT_IMPLEMENTED
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
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'] = ''
if NTTransParameters['DataOffset'] > 0:
dataOffset = NTTransParameters['DataOffset'] - 73 - NTTransParameters['SetupLength']
NTTransData['NT_Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
else:
NTTransData['NT_Trans_Data'] = ''
# Call the handler for this TRANSACTION
command = NTTransParameters['Function']
if command in transCommands:
# Call the NT TRANS subcommand
setup = ''
parameters = ''
data = ''
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 == '' and parameters == '' and data == '':
# Something wen't wrong
respParameters = ''
respData = ''
if errorCode == STATUS_SUCCESS:
errorCode = STATUS_ACCESS_DENIED
else:
# Build the answer
data = str(data)
remainingData = len(data)
parameters = str(parameters)
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 = '\xFF' * padLen
respData['Pad1'] = padBytes
respParameters['ParameterOffset'] = 73 + len(setup) + padLen
else:
padLen = 0
respParameters['ParameterOffset'] = 0
respData['Pad1'] = ''
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'] = '\xFF' * pad2Len
respParameters['DataOffset'] = 73 + len(setup) + padLen + len(parameters) + pad2Len
else:
respParameters['DataOffset'] = 0
respData['Pad2'] = ''
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 = ''
respData = ''
errorCode = STATUS_NOT_IMPLEMENTED
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
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'] = ''
if trans2Parameters['DataOffset'] > 0:
dataOffset = trans2Parameters['DataOffset'] - 63 - trans2Parameters['SetupLength']
trans2Data['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
else:
trans2Data['Trans_Data'] = ''
# 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 == '' and parameters == '' and data == '':
# Something wen't wrong
respParameters = ''
respData = ''
else:
# Build the answer
data = str(data)
remainingData = len(data)
parameters = str(parameters)
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 = '\xFF' * padLen
respData['Pad1'] = padBytes
respParameters['ParameterOffset'] = 55 + len(setup) + padLen
else:
padLen = 0
respParameters['ParameterOffset'] = 0
respData['Pad1'] = ''
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'] = '\xFF' * pad2Len
respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len
else:
respParameters['DataOffset'] = 0
respData['Pad2'] = ''
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 = ''
respData = ''
errorCode = STATUS_NOT_IMPLEMENTED
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComLockingAndX(connId, smbServer, SMBCommand, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_LOCKING_ANDX)
respParameters = ''
respData = ''
# 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
@staticmethod
def smbComClose(connId, smbServer, SMBCommand, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CLOSE)
respParameters = ''
respData = ''
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComWrite(connId, smbServer, SMBCommand, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE)
respParameters = smb.SMBWriteResponse_Parameters()
respData = ''
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComFlush(connId, smbServer, SMBCommand,recvPacket ):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_FLUSH)
respParameters = ''
respData = ''
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComCreateDirectory(connId, smbServer, SMBCommand,recvPacket ):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY)
respParameters = ''
respData = ''
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComRename(connId, smbServer, SMBCommand, recvPacket ):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_RENAME)
respParameters = ''
respData = ''
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComDelete(connId, smbServer, SMBCommand, recvPacket ):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE)
respParameters = ''
respData = ''
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComDeleteDirectory(connId, smbServer, SMBCommand, recvPacket ):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY)
respParameters = ''
respData = ''
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComWriteAndX(connId, smbServer, SMBCommand, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_WRITE_ANDX)
respParameters = smb.SMBWriteAndXResponse_Parameters()
respData = ''
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComReadAndX(connId, smbServer, SMBCommand, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_READ_ANDX)
respParameters = smb.SMBReadAndXResponse_Parameters()
respData = ''
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbQueryInformation(connId, smbServer, SMBCommand, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION)
respParameters = smb.SMBQueryInformationResponse_Parameters()
respData = ''
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']))
respParameters['FileSize'] = fileSize
respParameters['LastWriteTime'] = lastWriteTime
respParameters['FileAttributes'] = fileAttributes
errorCode = STATUS_SUCCESS
else:
# STATUS_SMB_BAD_TID
errorCode = STATUS_SMB_BAD_TID
respParameters = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
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 = ''
# 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 = ''
respParameters = ''
errorCode = STATUS_SMB_BAD_TID
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
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
@staticmethod
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 = ''
respData = ''
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
@staticmethod
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 = ''
respData = ''
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
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComQueryInformation2(connId, smbServer, SMBCommand, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION2)
respParameters = smb.SMBQueryInformation2Response_Parameters()
respData = ''
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
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 = ''
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 (unicode(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 unicode(pathName) in smbServer.getRegisteredNamedPipes():
fid = PIPE_FILE_DESCRIPTOR
sock = socket.socket()
sock.connect(smbServer.getRegisteredNamedPipes()[unicode(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 = 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 = ''
respData = ''
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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
def smbComOpenAndX(connId, smbServer, SMBCommand, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_OPEN_ANDX)
respParameters = smb.SMBOpenAndXResponse_Parameters()
respData = ''
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 = 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 = ''
respData = ''
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
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 = 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' )
respSMBCommand['Parameters'] = respParameters
respSMBCommand['Data'] = respData
resp['Uid'] = connData['Uid']
resp.addCommand(respSMBCommand)
smbServer.setConnectionData(connId, connData)
return None, [resp], errorCode
@staticmethod
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])[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 = '\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])[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'] = '\xff'*8
challengeMessage['VersionLen'] = 8
if rawNTLM is False:
respToken = SPNEGO_NegTokenResp()
# accept-incomplete. We want more data
respToken['NegResult'] = '\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'], authenticateMessage['user_name'], authenticateMessage['host_name']))
# TODO: Check the credentials! Now granting permissions
respToken = SPNEGO_NegTokenResp()
# accept-completed
respToken['NegResult'] = '\x00'
# Status SUCCESS
errorCode = STATUS_SUCCESS
smbServer.log('User %s\\%s authenticated successfully' % (authenticateMessage['user_name'], authenticateMessage['host_name']))
# 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:
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
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( '', 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
@staticmethod
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('\x02')
try:
index = dialects.index('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'] = '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(str(_dialects_data))
else:
# TODO: Handle random challenges, now one that can be used with rainbow tables
_dialects_data['Challenge'] = '\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
_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
@staticmethod
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'] = '\x00\x00\x00'
errorCode = STATUS_NOT_IMPLEMENTED
packet['ErrorCode'] = errorCode >> 16
packet['ErrorClass'] = errorCode & 0xff
return None, [packet], errorCode
class SMB2Commands:
@staticmethod
def smb2Negotiate(connId, smbServer, recvPacket, isSMB1 = False):
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('\x02')
if 'SMB 2.002\x00' in dialects or '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'] = '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
@staticmethod
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])[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 = '\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])[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'] = '\xff'*8
challengeMessage['VersionLen'] = 8
if rawNTLM is False:
respToken = SPNEGO_NegTokenResp()
# accept-incomplete. We want more data
respToken['NegResult'] = '\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'], authenticateMessage['user_name'], authenticateMessage['host_name']))
# TODO: Check the credentials! Now granting permissions
respToken = SPNEGO_NegTokenResp()
# accept-completed
respToken['NegResult'] = '\x00'
# Status SUCCESS
errorCode = STATUS_SUCCESS
smbServer.log('User %s\\%s authenticated successfully' % (authenticateMessage['user_name'], authenticateMessage['host_name']))
# 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)
respSMBCommand['SessionFlags'] = 1
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
@staticmethod
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 = str(recvPacket)[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 = 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 == '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
smbServer.setConnectionData(connId, connData)
return None, [respPacket], errorCode
@staticmethod
def smb2Create(connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Create_Response()
ntCreateRequest = smb2.SMB2Create(recvPacket['Data'])
respSMBCommand['Buffer'] = '\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 (unicode(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 unicode(pathName) in smbServer.getRegisteredNamedPipes():
fid = PIPE_FILE_DESCRIPTOR
sock = socket.socket()
sock.connect(smbServer.getRegisteredNamedPipes()[unicode(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
@staticmethod
def smb2Close(connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Close_Response()
closeRequest = smb2.SMB2Close(recvPacket['Data'])
if str(closeRequest['FileID']) == '\xff'*16:
# Let's take the data from the lastRequest
if 'SMB2_CREATE' in connData['LastRequest']:
fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
else:
fileID = str(closeRequest['FileID'])
else:
fileID = str(closeRequest['FileID'])
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
@staticmethod
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'] = '\x00'
if str(queryInfo['FileID']) == '\xff'*16:
# Let's take the data from the lastRequest
if 'SMB2_CREATE' in connData['LastRequest']:
fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
else:
fileID = str(queryInfo['FileID'])
else:
fileID = str(queryInfo['FileID'])
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:
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
@staticmethod
def smb2SetInfo(connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2SetInfo_Response()
setInfo = smb2.SMB2SetInfo(recvPacket['Data'])
errorCode = STATUS_SUCCESS
if str(setInfo['FileID']) == '\xff'*16:
# Let's take the data from the lastRequest
if 'SMB2_CREATE' in connData['LastRequest']:
fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
else:
fileID = str(setInfo['FileID'])
else:
fileID = str(setInfo['FileID'])
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, '\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
@staticmethod
def smb2Write(connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Write_Response()
writeRequest = smb2.SMB2Write(recvPacket['Data'])
respSMBCommand['Buffer'] = '\x00'
if str(writeRequest['FileID']) == '\xff'*16:
# Let's take the data from the lastRequest
if 'SMB2_CREATE' in connData['LastRequest']:
fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
else:
fileID = str(writeRequest['FileID'])
else:
fileID = str(writeRequest['FileID'])
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
@staticmethod
def smb2Read(connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Read_Response()
readRequest = smb2.SMB2Read(recvPacket['Data'])
respSMBCommand['Buffer'] = '\x00'
if str(readRequest['FileID']) == '\xff'*16:
# Let's take the data from the lastRequest
if 'SMB2_CREATE' in connData['LastRequest']:
fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
else:
fileID = str(readRequest['FileID'])
else:
fileID = str(readRequest['FileID'])
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
@staticmethod
def smb2Flush(connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Flush_Response()
flushRequest = smb2.SMB2Flush(recvPacket['Data'])
if str(flushRequest['FileID']) in connData['OpenedFiles']:
fileHandle = connData['OpenedFiles'][str(flushRequest['FileID'])]['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
@staticmethod
def smb2QueryDirectory(connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2QueryDirectory_Response()
queryDirectoryRequest = smb2.SMB2QueryDirectory(recvPacket['Data'])
respSMBCommand['Buffer'] = '\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 str(queryDirectoryRequest['FileID']) == '\xff'*16:
# Let's take the data from the lastRequest
if 'SMB2_CREATE' in connData['LastRequest']:
fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
else:
fileID = str(queryDirectoryRequest['FileID'])
else:
fileID = str(queryDirectoryRequest['FileID'])
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 = ''
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 + '\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
@staticmethod
def smb2ChangeNotify(connId, smbServer, recvPacket):
return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED
@staticmethod
def smb2Echo(connId, smbServer, recvPacket):
respSMBCommand = smb2.SMB2Echo_Response()
return [respSMBCommand], None, STATUS_SUCCESS
@staticmethod
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
@staticmethod
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
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
@staticmethod
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
@staticmethod
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
@staticmethod
def smb2Cancel(connId, smbServer, recvPacket):
# I'm actually doing nothing
return [smb2.SMB2Error()], None, STATUS_CANCELLED
@staticmethod
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
class Ioctls:
@staticmethod
def fsctlDfsGetReferrals(connId, smbServer, ioctlRequest):
return smb2.SMB2Error(), STATUS_FS_DRIVER_REQUIRED
@staticmethod
def fsctlPipeTransceive(connId, smbServer, ioctlRequest):
connData = smbServer.getConnectionData(connId)
ioctlResponse = ''
if str(ioctlRequest['FileID']) in connData['OpenedFiles']:
fileHandle = connData['OpenedFiles'][str(ioctlRequest['FileID'])]['FileHandle']
errorCode = STATUS_SUCCESS
try:
if fileHandle != PIPE_FILE_DESCRIPTOR:
errorCode = STATUS_INVALID_DEVICE_REQUEST
else:
sock = connData['OpenedFiles'][str(ioctlRequest['FileID'])]['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
@staticmethod
def fsctlValidateNegotiateInfo(connId, smbServer, ioctlRequest):
connData = smbServer.getConnectionData(connId)
errorCode = STATUS_SUCCESS
validateNegotiateInfo = smb2.VALIDATE_NEGOTIATE_INFO(ioctlRequest['Buffer'])
validateNegotiateInfo['Capabilities'] = 0
validateNegotiateInfo['Guid'] = 'A'*16
validateNegotiateInfo['SecurityMode'] = 1
validateNegotiateInfo['Dialects'] = (smb2.SMB2_DIALECT_002,)
smbServer.setConnectionData(connId, connData)
return validateNegotiateInfo.getData(), errorCode
class SMBSERVERHandler(SocketServer.BaseRequestHandler):
def __init__(self, request, client_address, server, select_poll = False):
self.__SMB = server
self.__ip, self.__port = client_address
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)
def handle(self):
self.__SMB.log("Incoming connection (%s,%d)" % (self.__ip, self.__port))
self.__SMB.addConnection(self.__connId, self.__ip, self.__port)
while True:
try:
# Firt 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('\x20'+rn)
myname = nmb.decode_name('\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 recevied. Except for big transactions this should be
# a single packet
for i in resp:
session.send_packet(str(i))
except Exception as e:
self.__SMB.log("Handle: %s" % e)
#import traceback
#traceback.print_exc()
break
def finish(self):
# 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.ThreadingMixIn, SocketServer.TCPServer):
#class SMBSERVER(SocketServer.ForkingMixIn, SocketServer.TCPServer):
def __init__(self, server_address, handler_class=SMBSERVERHandler, config_parser = None):
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 = {}
def getIoctls(self):
return self.__smb2Ioctls
def getCredentials(self):
return self.__credentials
def removeConnection(self, name):
try:
del(self.__activeConnections[name])
except:
pass
self.log("Remaining connections %s" % self.__activeConnections.keys())
def addConnection(self, name, ip, port):
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'] = {}
def getActiveConnections(self):
return self.__activeConnections
def setConnectionData(self, connId, data):
self.__activeConnections[connId] = data
#print "setConnectionData"
#print self.__activeConnections
def getConnectionData(self, connId, checkStatus = True):
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
def getRegisteredNamedPipes(self):
return self.__registeredNamedPipes
def registerNamedPipe(self, pipeName, address):
self.__registeredNamedPipes[unicode(pipeName)] = address
return True
def unregisterNamedPipe(self, pipeName):
if pipeName in self.__registeredNamedPipes:
del(self.__registeredNamedPipes[unicode(pipeName)])
return True
return False
def unregisterTransaction(self, transCommand):
if transCommand in self.__smbTransCommands:
del(self.__smbTransCommands[transCommand])
def hookTransaction(self, transCommand, callback):
# 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 transfered 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 reponse 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
def unregisterTransaction2(self, transCommand):
if transCommand in self.__smbTrans2Commands:
del(self.__smbTrans2Commands[transCommand])
def hookTransaction2(self, transCommand, callback):
# 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
def unregisterNTTransaction(self, transCommand):
if transCommand in self.__smbNTTransCommands:
del(self.__smbNTTransCommands[transCommand])
def hookNTTransaction(self, transCommand, callback):
# 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
def unregisterSmbCommand(self, smbCommand):
if smbCommand in self.__smbCommands:
del(self.__smbCommands[smbCommand])
def hookSmbCommand(self, smbCommand, callback):
# 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
def unregisterSmb2Command(self, smb2Command):
if smb2Command in self.__smb2Commands:
del(self.__smb2Commands[smb2Command])
def hookSmb2Command(self, smb2Command, callback):
if smb2Command in self.__smb2Commands:
originalCommand = self.__smb2Commands[smb2Command]
else:
originalCommand = None
self.__smb2Commands[smb2Command] = callback
return originalCommand
def log(self, msg, level=logging.INFO):
self.__log.log(level,msg)
def getServerName(self):
return self.__serverName
def getServerOS(self):
return self.__serverOS
def getServerDomain(self):
return self.__serverDomain
def getSMBChallenge(self):
return self.__challenge
def getServerConfig(self):
return self.__serverConfig
def setServerConfig(self, config):
self.__serverConfig = config
def getJTRdumpPath(self):
return self.__jtr_dump_path
def verify_request(self, request, client_address):
# TODO: Control here the max amount of processes we want to launch
# returning False, closes the connection
return True
def processRequest(self, connId, data):
# 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)
isSMB2 = True
# 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:
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:
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:
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)
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']
respPacket['Data'] = str(respCommand)
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 = ''
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
finalData += str(packet) + padLen*'\x00'
# Last one
finalData += str(packetsToSend[len(packetsToSend)-1])
packetsToSend = [finalData]
# We clear the compound requests
connData['LastRequest'] = {}
return packetsToSend
def processConfigFile(self, configFile = None):
# 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 = self.__serverConfig.get('global', 'challenge')
else:
self.__challenge = '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, domain, lmhash, nthash = line.split(':')
self.__credentials[name] = (domain, lmhash, nthash.strip('\r\n'))
line = cred.readline()
cred.close()
self.log('Config file parsed')
# For windows platforms, opening a directory is not an option, so we set a void FD
VOID_FILE_DESCRIPTOR = -1
PIPE_FILE_DESCRIPTOR = -2