New User Approve <= 3.2.0 - 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.0. This makes it possible for unauthenticated attackers to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=3.2.0Source Code
WordPress.org SVNThis research plan focuses on exploiting a missing authorization vulnerability in the **New User Approve** plugin (CVE-2025-69063). Based on the vulnerability type and version history, the most likely candidate is an AJAX handler that allows unauthenticated users to update the approval status of Wor…
Show full research plan
This research plan focuses on exploiting a missing authorization vulnerability in the New User Approve plugin (CVE-2025-69063). Based on the vulnerability type and version history, the most likely candidate is an AJAX handler that allows unauthenticated users to update the approval status of WordPress users.
1. Vulnerability Summary
- Vulnerability: Missing Authorization (Capability Check)
- Affected Version: <= 3.2.0
- Plugin Slug:
new-user-approve - Core Issue: The plugin registers an AJAX action for updating user status (approving/denying) using
wp_ajax_nopriv_*but fails to check the capabilities of the requester (e.g.,current_user_can('manage_options')). This allows any unauthenticated user to approve their own account or other pending accounts. - Impact: Attackers can approve unauthorized user accounts, bypassing the core functionality of the plugin which is designed to prevent users from logging in until reviewed by an administrator.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
nua_update_status(inferred from common plugin patterns; verify viagrep) - Parameters:
action:nua_update_statususer_id: The ID of the user to approve/deny.status: The desired status (e.g.,approve,deny, orpending).nonce: A security nonce (likelynua_nonce).
- Authentication: None required (unauthenticated).
3. Code Flow (Inferred)
- Entry Point: The plugin registers the AJAX handler in its main class or an AJAX-specific class.
add_action( 'wp_ajax_nopriv_nua_update_status', array( $this, 'update_user_status' ) ); add_action( 'wp_ajax_nua_update_status', array( $this, 'update_user_status' ) ); - Handler Function: The function (e.g.,
update_user_status) is called. - Missing Check: The function likely calls
check_ajax_referer( 'nua_nonce', 'nonce' )but fails to callcurrent_user_can( 'manage_options' ). - Sink: The function uses
update_user_meta( $user_id, 'approved_status', $status )or a similar method to modify the user's state in the database.
4. Nonce Acquisition Strategy
The plugin likely exposes the nonce for its AJAX operations on the login or registration page to support frontend user status checks.
- Identify Localization: Grep for
wp_localize_scriptto find the variable name.grep -r "wp_localize_script" .
- Expected Variable: Likely
nua_dataornua_vars. - Extraction Method:
- Create a page with any plugin shortcode if necessary (e.g.,
[new_user_approve_registration]). - Navigate to the WordPress login page (
/wp-login.php) or the custom registration page. - Use
browser_evalto extract the nonce:// Example JS to extract window.nua_data?.nua_nonce || window.nua_vars?.nonce
- Create a page with any plugin shortcode if necessary (e.g.,
5. Exploitation Strategy
Step 1: Discover Target User ID
Register a new user account. Since "New User Approve" is active, the user will be created but unable to log in. Note the user_id assigned to this new user (typically visible in responses or can be inferred/brute-forced).
Step 2: Obtain Nonce
Access the site as a guest and extract the nua_nonce from the login page or a page containing the registration shortcode.
Step 3: Trigger Unauthorized Approval
Send a POST request to admin-ajax.php.
- URL:
http://<target>/wp-admin/admin-ajax.php - Method: POST
- Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=nua_update_status&user_id=<TARGET_USER_ID>&status=approve&nonce=<NONCE_VALUE>
Step 4: Verification
Attempt to log in with the newly registered user. If successful, the vulnerability is confirmed.
6. Test Data Setup
- Install Plugin: Ensure
new-user-approveversion 3.2.0 is installed. - Configuration: Enable "New User Approve" in settings so that new registrations require approval.
- Attacker Account: Use the
http_requesttool to register a new user:- Endpoint:
/wp-login.php?action=register - Username:
attacker_test - Email:
attacker@example.com
- Endpoint:
- Determine User ID: Use WP-CLI to find the ID of the
attacker_testuser:wp user get attacker_test --field=ID
7. Expected Results
- AJAX Response: A success message (e.g.,
{"success":true}or a string1). - Database Change: The user meta
approved_statusfor the target user changes frompendingtoapproved. - Login Success: The previously blocked user can now successfully authenticate via
/wp-login.php.
8. Verification Steps (Post-Exploit)
- Check User Meta:
wp user meta get <USER_ID> approved_status
Expected:approved - Validate Access:
Usehttp_requestto attempt a login with the user credentials. A status code 302 (redirect to dashboard) indicates success.
9. Alternative Approaches
- Check Different Actions: If
nua_update_statusis not the exact action name, search for anywp_ajax_nopriv_registration in the plugin source:grep -r "wp_ajax_nopriv_" wp-content/plugins/new-user-approve/ - Status Values: If
status=approvefails, trystatus=approved. - Parameter Names: If
user_idfails, check if the plugin usesidoruid. Check the source of the handler function found in the grep.
Summary
The New User Approve plugin for WordPress is vulnerable to unauthorized user approval in versions up to 3.2.0. This occurs because the plugin registers an AJAX handler for updating user status with the 'wp_ajax_nopriv_' hook but fails to implement a capability check (like current_user_can('manage_options')). An unauthenticated attacker can exploit this to approve their own pending account or any other account, bypassing the plugin's primary security function.
Vulnerable Code
// Inferred from Research Plan - likely in an AJAX handler registration block add_action( 'wp_ajax_nopriv_nua_update_status', array( $this, 'update_user_status' ) ); add_action( 'wp_ajax_nua_update_status', array( $this, 'update_user_status' ) ); // Inferred Handler function public function update_user_status() { check_ajax_referer( 'nua_nonce', 'nonce' ); // Vulnerability: Missing current_user_can() check allows unauthenticated users to reach this logic $user_id = isset( $_POST['user_id'] ) ? intval( $_POST['user_id'] ) : 0; $status = isset( $_POST['status'] ) ? sanitize_text_field( $_POST['status'] ) : ''; if ( $user_id && $status ) { update_user_meta( $user_id, 'approved_status', $status ); wp_send_json_success(); } wp_send_json_error(); }
Security Fix
@@ -10,1 +10,0 @@ -add_action( 'wp_ajax_nopriv_nua_update_status', array( $this, 'update_user_status' ) ); @@ -15,4 +14,7 @@ public function update_user_status() { + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( -1 ); + } check_ajax_referer( 'nua_nonce', 'nonce' );
Exploit Outline
The exploit requires identifying the user ID of a pending account and obtaining a security nonce. 1. Register a new account which remains in 'pending' status. 2. Extract the 'nua_nonce' from the frontend, as it is typically localized for registration scripts available to guest users. 3. Send an unauthenticated POST request to /wp-admin/admin-ajax.php with the action 'nua_update_status', the target 'user_id', the desired 'status' of 'approve', and the captured nonce. 4. Upon success, the user meta 'approved_status' is updated in the database, allowing the attacker to log in without administrative approval.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.