Added proxy integration: ProxyManager module with SOCKS5/HTTP support, proxy tab in GUI with fetch/test/rotate/on-off toggle, proxy routing for scanner connections and password spray

This commit is contained in:
drjones
2026-05-05 22:38:50 -07:00
parent 2f6d989539
commit ef91f8219b
4 changed files with 712 additions and 20 deletions

251
gui.py
View File

@@ -19,6 +19,7 @@ from dataclasses import dataclass
from scanner import scan_ips, RDP_PORTS
from ip_utils import parse_ranges_file, count_ips
from bruteforce import spray_single_host, TOP50_PASSWORDS, TOP_USERNAMES
from proxy import ProxyManager
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
RESULTS_DIR = os.path.join(BASE_DIR, "results")
@@ -89,6 +90,9 @@ class FastRDPGUI:
self.passwords_file = os.path.join(WORDLISTS_DIR, "passwords.txt")
self._metric_labels: dict = {}
self._spray_queue: asyncio.Queue = None
# Proxy manager
self.proxy_manager = ProxyManager()
os.makedirs(RESULTS_DIR, exist_ok=True)
os.makedirs(WORDLISTS_DIR, exist_ok=True)
@@ -162,6 +166,10 @@ class FastRDPGUI:
self.notebook.add(self.tab_results, text=" \U0001f4ca Results ")
self._build_results_tab()
self.tab_proxy = tk.Frame(self.notebook, bg=Colors.BG_DARK)
self.notebook.add(self.tab_proxy, text=" \U0001f310 Proxy ")
self._build_proxy_tab()
self.tab_settings = tk.Frame(self.notebook, bg=Colors.BG_DARK)
self.notebook.add(self.tab_settings, text=" \u2699 Settings ")
self._build_settings_tab()
@@ -412,7 +420,242 @@ class FastRDPGUI:
)
self.good_text.pack(fill="both", expand=True)
# ── TAB 3: SETTINGS ────────────────────────────────────
# ── TAB 3: PROXY ────────────────────────────────────────
def _build_proxy_tab(self):
"""Build the proxy management tab."""
# ── Top controls ──
ctrl_frame = tk.Frame(self.tab_proxy, bg=Colors.BG_MID, height=50)
ctrl_frame.pack(fill="x")
ctrl_frame.pack_propagate(False)
# Master on/off toggle
self.proxy_enabled_var = tk.BooleanVar(value=False)
self.proxy_toggle = tk.Checkbutton(
ctrl_frame,
text="\U0001f6e1 PROXY ON",
variable=self.proxy_enabled_var,
fg=Colors.GREEN, bg=Colors.BG_MID,
selectcolor=Colors.BG_DARK,
activebackground=Colors.BG_MID,
activeforeground=Colors.GREEN,
font=("Segoe UI", 12, "bold"),
command=self._on_proxy_toggle
)
self.proxy_toggle.pack(side="left", padx=15, pady=5)
# Auto-rotate toggle
self.proxy_rotate_var = tk.BooleanVar(value=True)
tk.Checkbutton(
ctrl_frame,
text="\U0001f504 Auto-Rotate",
variable=self.proxy_rotate_var,
fg=Colors.TEXT, bg=Colors.BG_MID,
selectcolor=Colors.BG_DARK,
activebackground=Colors.BG_MID,
activeforeground=Colors.ACCENT,
font=("Segoe UI", 9, "bold"),
command=self._on_rotate_toggle
).pack(side="left", padx=10)
# Buttons
self.fetch_btn = ttk.Button(ctrl_frame, text="\U0001f4e1 Fetch Proxies",
command=self._fetch_proxies)
self.fetch_btn.pack(side="left", padx=5)
self.test_btn = ttk.Button(ctrl_frame, text="\U0001f50d Test All",
command=self._test_proxies, state="disabled")
self.test_btn.pack(side="left", padx=5)
self.clear_btn = ttk.Button(ctrl_frame, text="\U0001f5d1 Clear",
command=self._clear_proxies, state="disabled")
self.clear_btn.pack(side="left", padx=5)
# ── Proxy list ──
list_frame = ttk.LabelFrame(self.tab_proxy, text="\U0001f4cb Proxy List", padding=3)
list_frame.pack(fill="both", expand=True, pady=(3, 3))
columns = ("host", "port", "protocol", "alive", "latency", "failures")
self.proxy_tree = ttk.Treeview(list_frame, columns=columns,
show="headings", height=10)
headings = [("host", "IP Address", 160), ("port", "Port", 55),
("protocol", "Type", 70),
("alive", "Alive", 55),
("latency", "Latency", 80),
("failures", "Failures", 65)]
for col, text, width in headings:
self.proxy_tree.heading(col, text=text)
self.proxy_tree.column(col, width=width, anchor="center")
self.proxy_tree.column("host", anchor="w")
scroll_p = ttk.Scrollbar(list_frame, orient="vertical",
command=self.proxy_tree.yview)
self.proxy_tree.configure(yscrollcommand=scroll_p.set)
self.proxy_tree.pack(side="left", fill="both", expand=True)
scroll_p.pack(side="right", fill="y")
# Tag for alive/dead rows
self.proxy_tree.tag_configure("alive", foreground=Colors.GREEN)
self.proxy_tree.tag_configure("dead", foreground=Colors.RED)
self.proxy_tree.tag_configure("testing", foreground=Colors.YELLOW)
# ── Proxy stats ──
stats_frame = tk.Frame(self.tab_proxy, bg=Colors.BG_MID, height=35)
stats_frame.pack(fill="x")
stats_frame.pack_propagate(False)
self.proxy_stats_label = tk.Label(
stats_frame,
text="\U0001f4ca Total: 0 | \u2713 Working: 0 | \u2717 Failed: 0 | Status: idle",
fg=Colors.TEXT_DIM, bg=Colors.BG_MID,
font=("Consolas", 9)
)
self.proxy_stats_label.pack(side="left", padx=10)
# Progress bar (hidden by default)
self.proxy_progress = ttk.Progressbar(
stats_frame, mode="determinate", length=200
)
self.proxy_progress.pack(side="right", padx=10)
# ── Log output ──
log_frame = ttk.LabelFrame(self.tab_proxy, text="\U0001f4dd Proxy Log", padding=3)
log_frame.pack(fill="both", expand=True)
self.proxy_log = scrolledtext.ScrolledText(
log_frame, font=("Consolas", 9), height=6,
bg="#0d1117", fg="#c9d1d9", insertbackground="white",
relief="flat", borderwidth=0
)
self.proxy_log.pack(fill="both", expand=True)
self.proxy_log.tag_configure("info", foreground="#79c0ff")
self.proxy_log.tag_configure("ok", foreground=Colors.GREEN)
self.proxy_log.tag_configure("error", foreground=Colors.RED)
self.proxy_log.tag_configure("system", foreground=Colors.TEXT_DIM)
def _proxy_log(self, msg: str, tag: str = "info"):
ts = time.strftime("%H:%M:%S")
try:
self.proxy_log.insert("end", f"[{ts}] {msg}\n", tag)
self.proxy_log.see("end")
except tk.TclError:
pass
def _on_proxy_toggle(self):
enabled = self.proxy_enabled_var.get()
self.proxy_manager.enabled = enabled
if enabled:
self.proxy_toggle.config(fg=Colors.GREEN,
text="\U0001f6e1 PROXY ON")
self._proxy_log("\U0001f6e1 Proxy routing ENABLED", "ok")
# Auto-fetch if no proxies yet
if not self.proxy_manager.proxies:
self.root.after(100, self._fetch_proxies)
else:
self.proxy_toggle.config(fg=Colors.RED,
text="\U0001f6e1 PROXY OFF")
self._proxy_log("\U0001f6e1 Proxy routing DISABLED", "system")
def _on_rotate_toggle(self):
self.proxy_manager.auto_rotate = self.proxy_rotate_var.get()
self._proxy_log(f"Auto-rotate: {'ON' if self.proxy_rotate_var.get() else 'OFF'}", "system")
def _update_proxy_display(self):
"""Refresh the proxy treeview from manager state."""
for item in self.proxy_tree.get_children():
self.proxy_tree.delete(item)
for p in self.proxy_manager.proxies:
tag = "alive" if p.alive else "dead"
alive_str = "\u2713" if p.alive else "\u2717"
latency_str = f"{p.latency:.0f}ms" if p.alive else "-"
self.proxy_tree.insert("", "end",
values=(p.host, str(p.port), p.protocol.upper(),
alive_str, latency_str, str(p.failures)),
tags=(tag,))
stats = self.proxy_manager.get_stats()
self.proxy_stats_label.config(
text=f"\U0001f4ca Total: {stats['total']} | "
f"\u2713 Working: {stats['working']} | "
f"\u2717 Failed: {stats['failed']} | "
f"Status: {'fetching...' if stats['fetching'] else 'testing...' if stats['testing'] else 'idle'}"
)
self.test_btn.config(state="normal" if stats['total'] > 0 else "disabled")
self.clear_btn.config(state="normal" if stats['total'] > 0 else "disabled")
def _fetch_proxies(self):
"""Fetch proxies from sources in a background thread."""
self.fetch_btn.config(state="disabled")
self.proxy_progress["mode"] = "indeterminate"
self.proxy_progress.start()
self._proxy_log("\U0001f4e1 Fetching proxy lists from sources...", "info")
def worker():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
count = loop.run_until_complete(
self.proxy_manager.fetch_from_sources(
progress_callback=lambda msg: self.root.after(0, lambda m=msg: self._proxy_log(m, "system"))
)
)
self.root.after(0, lambda: self._proxy_log(
f"\u2705 Fetched {count} unique proxies", "ok"))
self.root.after(0, self._update_proxy_display)
except Exception as e:
self.root.after(0, lambda: self._proxy_log(
f"\u274c Fetch error: {e}", "error"))
finally:
loop.close()
self.root.after(0, lambda: self.proxy_progress.stop())
self.root.after(0, lambda: self.proxy_progress.configure(mode="determinate"))
self.root.after(0, lambda: self.fetch_btn.config(state="normal"))
thread = threading.Thread(target=worker, daemon=True)
thread.start()
def _test_proxies(self):
"""Test all proxies in background thread."""
self.test_btn.config(state="disabled")
self.proxy_progress["value"] = 0
self.proxy_progress["maximum"] = 100
self._proxy_log("\U0001f50d Testing proxies...", "info")
def worker():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
def progress_cb(done, total):
pct = int((done / total) * 100) if total > 0 else 0
self.root.after(0, lambda v=pct: self.proxy_progress.configure(value=v))
working = loop.run_until_complete(
self.proxy_manager.test_all(
max_concurrent=100,
progress_callback=progress_cb
)
)
self.root.after(0, lambda: self._proxy_log(
f"\u2705 Testing complete: {working} working proxies", "ok"))
self.root.after(0, self._update_proxy_display)
except Exception as e:
self.root.after(0, lambda: self._proxy_log(
f"\u274c Test error: {e}", "error"))
finally:
loop.close()
self.root.after(0, lambda: self.test_btn.config(state="normal"))
self.root.after(0, lambda: self.proxy_progress.configure(value=0))
thread = threading.Thread(target=worker, daemon=True)
thread.start()
def _clear_proxies(self):
"""Clear all proxies."""
self.proxy_manager.proxies.clear()
self.proxy_manager._working.clear()
self.proxy_manager.working_count = 0
self.proxy_manager.failed_count = 0
self.proxy_manager.total_fetched = 0
self._update_proxy_display()
self._proxy_log("\U0001f5d1 Proxy list cleared", "system")
# ── TAB 4: SETTINGS ────────────────────────────────────
def _build_settings_tab(self):
editor_frame = ttk.LabelFrame(self.tab_settings, text="\U0001f4dd Wordlist Editor", padding=8)
editor_frame.pack(fill="both", expand=True, pady=(0, 5))
@@ -811,7 +1054,8 @@ class FastRDPGUI:
connect_timeout=timeout,
banner_check=False,
progress_callback=progress_cb,
live_callback=on_live_host
live_callback=on_live_host,
proxy_manager=self.proxy_manager
)
)
@@ -842,7 +1086,8 @@ class FastRDPGUI:
passwords=passwords,
max_concurrent=50,
timeout=5.0,
hit_callback=lambda i, p, u, pw: self._on_hit_found(i, p, u, pw)
hit_callback=lambda i, p, u, pw: self._on_hit_found(i, p, u, pw),
proxy_manager=self.proxy_manager
)
if not hits: