mirror of
https://github.com/lgandx/Responder.git
synced 2025-07-06 04:51:23 -07:00
Merge pull request #310 from ctjf/master
added quic support based on xpn's work
This commit is contained in:
commit
e346c01695
5 changed files with 178 additions and 0 deletions
|
@ -8,6 +8,7 @@ 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
|
||||||
|
|
|
@ -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:
|
||||||
|
@ -362,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,)))
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
aioquic
|
||||||
netifaces>=0.10.4
|
netifaces>=0.10.4
|
||||||
|
|
168
servers/QUIC.py
Normal file
168
servers/QUIC.py
Normal 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
|
|
@ -124,6 +124,7 @@ class Settings:
|
||||||
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'))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue