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:
251
gui.py
251
gui.py
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user