Files
s-b-repo-rustsploit/test_servers.py
T
S.B c35bcf50c5 delta wolf spawns
added my to do list this is a backup of progress so far fixed alot fo stuff check change log redisgning the way api runs and works fixing modules ooms adding extra security on prompt handling change some stuff this version is not stable yet so please do be aware
2026-03-04 18:19:35 +02:00

831 lines
33 KiB
Python

#!/usr/bin/env python3
"""
Emulated login servers for RustSploit bruteforce module testing.
Default creds: admin / password123 (for all services)
Default SNMP community string: public
Ports:
SSH → 2222
SMTP → 2525
POP3 → 1110
MQTT → 1884
Telnet → 2323
SNMP → 1161 (UDP)
RTSP → 5554
Fortinet → 4443 (HTTPS)
L2TP → 1701 (UDP)
"""
import socket
import ssl
import threading
import struct
import hashlib
import os
import sys
import time
import traceback
BIND = "127.0.0.1"
CREDS = {"admin": "password123", "root": "toor"}
SNMP_COMMUNITY = "public"
# ─── SSH Server (paramiko) ──────────────────────────────────────────
def start_ssh_server(port=2222):
import paramiko
host_key = paramiko.RSAKey.generate(2048)
class SSHServer(paramiko.ServerInterface):
def __init__(self):
self.event = threading.Event()
self.authenticated = False
def check_channel_request(self, kind, chanid):
if kind == "session":
return paramiko.OPEN_SUCCEEDED
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def check_auth_password(self, username, password):
if CREDS.get(username) == password:
self.authenticated = True
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
def get_allowed_auths(self, username):
return "password"
def handle_client(client_sock):
try:
transport = paramiko.Transport(client_sock)
transport.add_server_key(host_key)
server = SSHServer()
transport.start_server(server=server)
chan = transport.accept(5)
if chan:
chan.close()
transport.close()
except Exception:
pass
finally:
try: client_sock.close()
except: pass
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((BIND, port))
sock.listen(10)
print(f"[SSH] listening on {BIND}:{port} (creds: admin/password123)")
while True:
client, addr = sock.accept()
threading.Thread(target=handle_client, args=(client,), daemon=True).start()
# ─── SMTP Server ────────────────────────────────────────────────────
def start_smtp_server(port=2525):
"""
SMTP server compatible with the RustSploit smtp_bruteforce module.
The module uses the Telnet crate to read responses, so we must:
1. Send banner starting with "220"
2. EHLO response with "250-AUTH LOGIN PLAIN" and final "250 OK" (space after 250)
3. AUTH PLAIN / AUTH LOGIN support
4. Each response must end with \r\n
"""
import base64
def handle_client(conn):
try:
conn.settimeout(10)
# Banner
conn.sendall(b"220 test-smtp ESMTP ready\r\n")
username = None
auth_state = None
while True:
data = conn.recv(4096)
if not data:
break
line = data.decode(errors="replace").strip()
if auth_state == "wait_user":
username = base64.b64decode(line).decode(errors="replace")
conn.sendall(b"334 UGFzc3dvcmQ6\r\n")
auth_state = "wait_pass"
elif auth_state == "wait_pass":
password = base64.b64decode(line).decode(errors="replace")
if CREDS.get(username) == password:
conn.sendall(b"235 2.7.0 Authentication successful\r\n")
else:
conn.sendall(b"535 5.7.8 Authentication failed\r\n")
auth_state = None
elif line.upper().startswith("EHLO") or line.upper().startswith("HELO"):
# Multi-line response: dash continues, space ends
conn.sendall(b"250-test-smtp\r\n")
conn.sendall(b"250-AUTH LOGIN PLAIN\r\n")
conn.sendall(b"250-SIZE 10240000\r\n")
conn.sendall(b"250 OK\r\n")
elif line.upper().startswith("AUTH PLAIN"):
parts = line.split(" ", 2)
if len(parts) == 3:
try:
decoded = base64.b64decode(parts[2]).decode(errors="replace")
pieces = decoded.split("\0")
if len(pieces) >= 3:
u, p = pieces[1], pieces[2]
if CREDS.get(u) == p:
conn.sendall(b"235 2.7.0 Authentication successful\r\n")
else:
conn.sendall(b"535 5.7.8 Authentication failed\r\n")
else:
conn.sendall(b"535 Bad format\r\n")
except Exception:
conn.sendall(b"535 Decode error\r\n")
else:
conn.sendall(b"334\r\n")
elif line.upper().startswith("AUTH LOGIN"):
conn.sendall(b"334 VXNlcm5hbWU6\r\n")
auth_state = "wait_user"
elif line.upper().startswith("QUIT"):
conn.sendall(b"221 Bye\r\n")
break
elif line.upper().startswith("RSET"):
conn.sendall(b"250 OK\r\n")
else:
conn.sendall(b"502 Not implemented\r\n")
except Exception:
pass
finally:
try: conn.close()
except: pass
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((BIND, port))
sock.listen(10)
print(f"[SMTP] listening on {BIND}:{port} (creds: admin/password123)")
while True:
client, addr = sock.accept()
threading.Thread(target=handle_client, args=(client,), daemon=True).start()
# ─── POP3 Server ────────────────────────────────────────────────────
def start_pop3_server(port=1110):
def handle_client(conn):
try:
conn.settimeout(10)
conn.sendall(b"+OK POP3 server ready\r\n")
username = None
while True:
data = conn.recv(1024)
if not data:
break
line = data.decode(errors="replace").strip()
if line.upper().startswith("USER "):
username = line[5:].strip()
conn.sendall(b"+OK\r\n")
elif line.upper().startswith("PASS "):
password = line[5:].strip()
if username and CREDS.get(username) == password:
conn.sendall(b"+OK Logged in\r\n")
else:
conn.sendall(b"-ERR Authentication failed\r\n")
elif line.upper() == "QUIT":
conn.sendall(b"+OK Bye\r\n")
break
elif line.upper() == "CAPA":
conn.sendall(b"+OK\r\nUSER\r\n.\r\n")
else:
conn.sendall(b"-ERR Unknown command\r\n")
except Exception:
pass
finally:
try: conn.close()
except: pass
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((BIND, port))
sock.listen(10)
print(f"[POP3] listening on {BIND}:{port} (creds: admin/password123)")
while True:
client, addr = sock.accept()
threading.Thread(target=handle_client, args=(client,), daemon=True).start()
# ─── MQTT Server (Broker emulation) ─────────────────────────────────
def start_mqtt_server(port=1884):
"""Minimal MQTT 3.1.1 CONNECT/CONNACK handler with auth."""
def handle_client(conn):
try:
conn.settimeout(10)
data = conn.recv(4096)
if not data or (data[0] >> 4) != 1:
conn.close()
return
pos = 1
multiplier = 1
remaining = 0
while pos < len(data):
byte = data[pos]
remaining += (byte & 0x7F) * multiplier
multiplier *= 128
pos += 1
if (byte & 0x80) == 0:
break
payload_start = pos
proto_len = struct.unpack("!H", data[payload_start:payload_start+2])[0]
idx = payload_start + 2 + proto_len
idx += 1
connect_flags = data[idx]
has_username = bool(connect_flags & 0x80)
has_password = bool(connect_flags & 0x40)
idx += 1
idx += 2
cid_len = struct.unpack("!H", data[idx:idx+2])[0]
idx += 2 + cid_len
username = ""
password = ""
if has_username:
ulen = struct.unpack("!H", data[idx:idx+2])[0]
idx += 2
username = data[idx:idx+ulen].decode(errors="replace")
idx += ulen
if has_password:
plen = struct.unpack("!H", data[idx:idx+2])[0]
idx += 2
password = data[idx:idx+plen].decode(errors="replace")
if CREDS.get(username) == password:
conn.sendall(bytes([0x20, 0x02, 0x00, 0x00]))
else:
conn.sendall(bytes([0x20, 0x02, 0x00, 0x05]))
except Exception:
pass
finally:
try: conn.close()
except: pass
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((BIND, port))
sock.listen(10)
print(f"[MQTT] listening on {BIND}:{port} (creds: admin/password123)")
while True:
client, addr = sock.accept()
threading.Thread(target=handle_client, args=(client,), daemon=True).start()
# ─── Telnet Server ──────────────────────────────────────────────────
def start_telnet_server(port=2323):
"""
Telnet server compatible with the RustSploit telnet_bruteforce module.
Sends IAC negotiations first, then login/password prompts.
Success response includes '#' or '$' shell prompt so the module detects it.
"""
def handle_client(conn):
try:
conn.settimeout(10)
# Send IAC + login prompt together so validation sees 'login' immediately
conn.sendall(b"\xff\xfb\x01\xff\xfb\x03\r\nlogin: ")
data = conn.recv(1024)
if not data:
conn.close()
return
# Strip IAC responses the client may send back
raw = data
clean = bytearray()
i = 0
while i < len(raw):
if raw[i] == 0xFF and i + 2 < len(raw):
i += 3 # skip IAC command
else:
clean.append(raw[i])
i += 1
username = bytes(clean).decode(errors="replace").strip().replace("\r", "").replace("\n", "")
conn.sendall(b"Password: ")
data = conn.recv(1024)
if not data:
conn.close()
return
password = data.decode(errors="replace").strip().replace("\r", "").replace("\n", "")
if CREDS.get(username) == password:
# Send shell prompt with $ so module detects success
conn.sendall(f"\r\nWelcome {username}\r\n{username}@test:~$ ".encode())
conn.settimeout(2)
try: conn.recv(1024)
except: pass
else:
conn.sendall(b"\r\nLogin incorrect\r\n")
time.sleep(0.3)
except Exception:
pass
finally:
try: conn.close()
except: pass
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((BIND, port))
sock.listen(10)
print(f"[Telnet] listening on {BIND}:{port} (creds: admin/password123)")
while True:
client, addr = sock.accept()
threading.Thread(target=handle_client, args=(client,), daemon=True).start()
# ─── SNMP Server (UDP) ──────────────────────────────────────────────
def start_snmp_server(port=1161):
"""Minimal SNMPv2c GET-RESPONSE handler."""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((BIND, port))
print(f"[SNMP] listening on {BIND}:{port} (UDP) (community: public)")
while True:
try:
data, addr = sock.recvfrom(4096)
if len(data) < 10:
continue
idx = 0
if data[idx] != 0x30:
continue
idx += 1
if data[idx] & 0x80:
ll = data[idx] & 0x7f
idx += 1 + ll
else:
idx += 1
if data[idx] != 0x02:
continue
idx += 1
vlen = data[idx]
idx += 1 + vlen
if data[idx] != 0x04:
continue
idx += 1
clen = data[idx]
idx += 1
community = data[idx:idx+clen].decode(errors="replace")
if community == SNMP_COMMUNITY:
sys_descr = b"Linux test-server 5.15.0"
oid = bytes([0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00])
val = bytes([0x04, len(sys_descr)]) + sys_descr
varbind = bytes([0x30, len(oid) + len(val)]) + oid + val
varbindlist = bytes([0x30, len(varbind)]) + varbind
req_id = bytes([0x02, 0x01, 0x01])
error_status = bytes([0x02, 0x01, 0x00])
error_index = bytes([0x02, 0x01, 0x00])
pdu_content = req_id + error_status + error_index + varbindlist
pdu = bytes([0xa2, len(pdu_content)]) + pdu_content
version = bytes([0x02, 0x01, 0x01])
comm_bytes = community.encode()
comm = bytes([0x04, len(comm_bytes)]) + comm_bytes
msg_content = version + comm + pdu
msg = bytes([0x30, len(msg_content)]) + msg_content
sock.sendto(msg, addr)
except Exception:
pass
# ─── RTSP Server ────────────────────────────────────────────────────
def start_rtsp_server(port=5554):
import base64
def handle_client(conn):
try:
conn.settimeout(10)
while True:
data = conn.recv(4096)
if not data:
break
request = data.decode(errors="replace")
lines = request.split("\r\n")
auth_header = None
cseq = "1"
for line in lines:
if line.lower().startswith("authorization:"):
auth_header = line.split(":", 1)[1].strip()
if line.lower().startswith("cseq:"):
cseq = line.split(":", 1)[1].strip()
if auth_header and auth_header.lower().startswith("basic "):
encoded = auth_header[6:]
try:
decoded = base64.b64decode(encoded).decode()
user, passwd = decoded.split(":", 1)
if CREDS.get(user) == passwd:
resp = f"RTSP/1.0 200 OK\r\nCSeq: {cseq}\r\n\r\n"
conn.sendall(resp.encode())
continue
except Exception:
pass
resp = (
f"RTSP/1.0 401 Unauthorized\r\n"
f"CSeq: {cseq}\r\n"
f"WWW-Authenticate: Basic realm=\"RTSP Server\"\r\n\r\n"
)
conn.sendall(resp.encode())
except Exception:
pass
finally:
try: conn.close()
except: pass
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((BIND, port))
sock.listen(10)
print(f"[RTSP] listening on {BIND}:{port} (creds: admin/password123)")
while True:
client, addr = sock.accept()
threading.Thread(target=handle_client, args=(client,), daemon=True).start()
# ─── Fortinet HTTPS Login Server ────────────────────────────────────
def start_fortinet_server(port=4443):
"""
Emulates FortiGate SSL VPN login page.
The module sends:
GET /remote/login → expects HTML with CSRF token
POST /remote/logincheck → form with username + credential fields
Success: response body contains "redir" and "portal"
Failure: response body contains "ret=0"
"""
from http.server import HTTPServer, BaseHTTPRequestHandler
import urllib.parse
class FortiHandler(BaseHTTPRequestHandler):
def log_message(self, format, *args):
pass
def do_GET(self):
if "/remote/login" in self.path:
# Return login page with CSRF token (magic field)
html = (
'<html><head><title>SSL VPN</title></head><body>'
'<form method="post" action="/remote/logincheck">'
'<input type="hidden" name="magic" value="test_csrf_token_123">'
'<input name="username"><input name="credential" type="password">'
'<input name="ajax" value="1">'
'</form></body></html>'
)
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.send_header("Content-Length", str(len(html)))
self.end_headers()
self.wfile.write(html.encode())
else:
self.send_response(404)
self.send_header("Content-Length", "0")
self.end_headers()
def do_POST(self):
if "/remote/logincheck" in self.path:
content_len = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(content_len).decode(errors="replace")
params = urllib.parse.parse_qs(body)
username = params.get("username", [""])[0]
# Module sends "credential" not "password"
password = params.get("credential", params.get("password", [""]))[0]
if CREDS.get(username) == password:
# Success response with redirect indicators
response_body = 'redir=/remote/portal&portal_user=admin'
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.send_header("Content-Length", str(len(response_body)))
self.end_headers()
self.wfile.write(response_body.encode())
else:
response_body = 'ret=0&redir='
self.send_response(401)
self.send_header("Content-Type", "text/plain")
self.send_header("Content-Length", str(len(response_body)))
self.end_headers()
self.wfile.write(response_body.encode())
else:
self.send_response(404)
self.send_header("Content-Length", "0")
self.end_headers()
cert_file = "/tmp/test_forti_cert.pem"
key_file = "/tmp/test_forti_key.pem"
if not os.path.exists(cert_file):
os.system(
f'openssl req -x509 -newkey rsa:2048 -keyout {key_file} -out {cert_file} '
f'-days 1 -nodes -subj "/CN=localhost" 2>/dev/null'
)
httpd = HTTPServer((BIND, port), FortiHandler)
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain(cert_file, key_file)
httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True)
print(f"[Fortinet] listening on {BIND}:{port} (HTTPS) (creds: admin/password123)")
httpd.serve_forever()
# ─── L2TP Server (UDP) ──────────────────────────────────────────────
def start_l2tp_server(port=1701):
"""
Minimal L2TP + PPP/CHAP server for bruteforce testing.
Implements: SCCRQ→SCCRP, SCCCN→ack, ICRQ→ICRP, ICCN→CHAP challenge→response check.
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((BIND, port))
print(f"[L2TP] listening on {BIND}:{port} (UDP) (creds: admin/password123)")
sessions = {} # addr -> state dict
while True:
try:
data, addr = sock.recvfrom(8192)
if len(data) < 6:
continue
# Parse L2TP header
flags = struct.unpack("!H", data[0:2])[0]
is_control = bool(flags & 0x8000)
has_length = bool(flags & 0x4000)
if not is_control:
# Data packet — check for CHAP response
state = sessions.get(addr, {})
if state.get("chap_sent"):
# Parse PPP CHAP response from data packet
# Skip L2TP data header (variable)
offset = 4 # minimal L2TP data header
if has_length:
offset += 2
payload = data[offset:]
# Look for PPP CHAP in payload
ppp_off = 0
if len(payload) > 2 and payload[0] == 0xFF and payload[1] == 0x03:
ppp_off = 2
if len(payload) > ppp_off + 6:
proto = struct.unpack("!H", payload[ppp_off:ppp_off+2])[0]
if proto == 0xC223: # PPP CHAP
chap_code = payload[ppp_off + 2]
if chap_code == 2: # CHAP Response
identifier = payload[ppp_off + 3]
chap_len = struct.unpack("!H", payload[ppp_off+4:ppp_off+6])[0]
value_size = payload[ppp_off + 6]
chap_value = payload[ppp_off + 7: ppp_off + 7 + value_size]
# Extract username from remaining bytes
name_start = ppp_off + 7 + value_size
name_end = ppp_off + 2 + chap_len
username = payload[name_start:name_end].decode(errors="replace")
# Verify CHAP: MD5(identifier + password + challenge)
challenge = state.get("challenge", b"")
success = False
for uname, pwd in CREDS.items():
if uname == username:
expected = hashlib.md5(
bytes([identifier]) + pwd.encode() + challenge
).digest()
if expected == chap_value:
success = True
break
# Send CHAP Success or Failure via data packet
tid = state.get("remote_tunnel_id", 1)
sid = state.get("remote_session_id", 1)
chap_code_resp = 3 if success else 4
msg = b"Access granted" if success else b"Access denied"
chap_pkt = struct.pack("!BBH", chap_code_resp, identifier,
4 + len(msg)) + msg
ppp_frame = b"\xff\x03" + struct.pack("!H", 0xC223) + chap_pkt
# L2TP data header
l2tp_hdr = struct.pack("!HH", 0x0002, tid) + struct.pack("!H", sid)
packet = l2tp_hdr + ppp_frame
sock.sendto(packet, addr)
if success:
sessions.pop(addr, None)
continue
# Control packet — needs 12 bytes minimum for header
if len(data) < 12:
continue
offset = 2
length = struct.unpack("!H", data[offset:offset+2])[0]
offset += 2
tunnel_id = struct.unpack("!H", data[offset:offset+2])[0]
offset += 2
session_id = struct.unpack("!H", data[offset:offset+2])[0]
offset += 2
ns = struct.unpack("!H", data[offset:offset+2])[0]
offset += 2
nr = struct.unpack("!H", data[offset:offset+2])[0]
offset += 2
# Parse AVPs to find message type
payload = data[offset:]
msg_type = None
if len(payload) >= 6:
avp_flags = struct.unpack("!H", payload[0:2])[0]
avp_len = avp_flags & 0x03FF
if avp_len >= 6:
attr_type = struct.unpack("!H", payload[4:6])[0]
if attr_type == 0: # Message Type
msg_type = struct.unpack("!H", payload[6:8])[0]
state = sessions.get(addr, {
"tunnel_id": 1,
"session_id": 1,
"ns": 0,
"nr": 0,
"remote_tunnel_id": 0,
"remote_session_id": 0,
})
def build_control(state, avps_data):
"""Build L2TP control message."""
tid = state.get("remote_tunnel_id", 0)
sid = 0
ns_val = state["ns"]
nr_val = state["nr"]
hdr_len = 12 + len(avps_data)
hdr = struct.pack("!HHHHHH", 0xC802, hdr_len, tid, sid, ns_val, nr_val)
state["ns"] += 1
return hdr + avps_data
def build_avp(attr_type, value, mandatory=True):
"""Build AVP."""
avp_len = 6 + len(value)
flags = avp_len | (0x8000 if mandatory else 0)
return struct.pack("!HHH", flags, 0, attr_type) + value
if msg_type == 1: # SCCRQ
state["remote_tunnel_id"] = tunnel_id if tunnel_id else 1
# Find assigned tunnel ID in AVPs
avp_off = 0
while avp_off + 6 <= len(payload):
avp_f = struct.unpack("!H", payload[avp_off:avp_off+2])[0]
avp_l = avp_f & 0x03FF
if avp_l < 6:
break
avp_t = struct.unpack("!H", payload[avp_off+4:avp_off+6])[0]
if avp_t == 9 and avp_l >= 8: # Assigned Tunnel ID
state["remote_tunnel_id"] = struct.unpack("!H", payload[avp_off+6:avp_off+8])[0]
avp_off += avp_l
state["nr"] = ns + 1
# Send SCCRP
avps = (
build_avp(0, struct.pack("!H", 2)) + # Message Type = SCCRP
build_avp(2, struct.pack("!H", 0x0100)) + # Protocol Version
build_avp(3, struct.pack("!I", 0)) + # Framing Capabilities
build_avp(4, struct.pack("!I", 0)) + # Bearer Capabilities
build_avp(9, struct.pack("!H", state["tunnel_id"])) + # Assigned Tunnel ID
build_avp(7, b"test-l2tp-server") + # Host Name
build_avp(6, struct.pack("!I", 65535)) # Receive Window Size
)
pkt = build_control(state, avps)
sock.sendto(pkt, addr)
sessions[addr] = state
elif msg_type == 3: # SCCCN
state["nr"] = ns + 1
# Send ZLB ACK
zlb = struct.pack("!HHHHHH", 0xC802, 12, state["remote_tunnel_id"], 0, state["ns"], state["nr"])
state["ns"] += 1
sock.sendto(zlb, addr)
sessions[addr] = state
elif msg_type == 10: # ICRQ
state["nr"] = ns + 1
# Parse assigned session ID
avp_off = 0
while avp_off + 6 <= len(payload):
avp_f = struct.unpack("!H", payload[avp_off:avp_off+2])[0]
avp_l = avp_f & 0x03FF
if avp_l < 6:
break
avp_t = struct.unpack("!H", payload[avp_off+4:avp_off+6])[0]
if avp_t == 14 and avp_l >= 8: # Assigned Session ID
state["remote_session_id"] = struct.unpack("!H", payload[avp_off+6:avp_off+8])[0]
avp_off += avp_l
# Send ICRP
avps = (
build_avp(0, struct.pack("!H", 11)) + # Message Type = ICRP
build_avp(14, struct.pack("!H", state["session_id"])) # Assigned Session ID
)
pkt = build_control(state, avps)
sock.sendto(pkt, addr)
sessions[addr] = state
elif msg_type == 12: # ICCN
state["nr"] = ns + 1
# Send ZLB ACK
zlb = struct.pack("!HHHHHH", 0xC802, 12, state["remote_tunnel_id"], 0, state["ns"], state["nr"])
state["ns"] += 1
sock.sendto(zlb, addr)
# Send CHAP Challenge as data packet
challenge = os.urandom(16)
state["challenge"] = challenge
state["chap_sent"] = True
identifier = 1
server_name = b"test-l2tp"
chap_len = 4 + 1 + len(challenge) + len(server_name)
chap_pkt = struct.pack("!BBH", 1, identifier, chap_len)
chap_pkt += bytes([len(challenge)]) + challenge + server_name
ppp_frame = b"\xff\x03" + struct.pack("!H", 0xC223) + chap_pkt
# L2TP data header
l2tp_hdr = struct.pack("!HH", 0x0002, state["remote_tunnel_id"])
l2tp_hdr += struct.pack("!H", state.get("remote_session_id", 0))
packet = l2tp_hdr + ppp_frame
sock.sendto(packet, addr)
sessions[addr] = state
else:
# ZLB ACK for anything else
state["nr"] = ns + 1
zlb = struct.pack("!HHHHHH", 0xC802, 12, state.get("remote_tunnel_id", 0), 0, state["ns"], state["nr"])
state["ns"] += 1
sock.sendto(zlb, addr)
sessions[addr] = state
except Exception as e:
print(f"[L2TP] Error: {e}", flush=True)
# ─── Main ───────────────────────────────────────────────────────────
def main():
print("=" * 60)
print(" RustSploit Test Servers — Emulated Login Services")
print(" Default creds: admin / password123")
print("=" * 60)
services = [
("SSH", start_ssh_server, 2222),
("SMTP", start_smtp_server, 2525),
("POP3", start_pop3_server, 1110),
("MQTT", start_mqtt_server, 1884),
("Telnet", start_telnet_server, 2323),
("SNMP", start_snmp_server, 1161),
("RTSP", start_rtsp_server, 5554),
("Fortinet", start_fortinet_server, 4443),
("L2TP", start_l2tp_server, 1701),
]
threads = []
for name, func, port in services:
t = threading.Thread(target=func, args=(port,), daemon=True)
t.start()
threads.append(t)
print("\n✅ All servers started! Press Ctrl+C to stop.\n")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\nShutting down...")
sys.exit(0)
if __name__ == "__main__":
main()