WP Wand – Unlimited Content Generation using AI – for OpenAI, Claude, Openrouter and Deepseek <= 1.3.07 - Missing Authorization
Description
The WP Wand – Unlimited Content Generation using AI – for OpenAI, Claude, Openrouter and Deepseek plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 1.3.07. This makes it possible for authenticated attackers, with contributor-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
<=1.3.07As the source code for the plugin is not provided, this research plan is based on the vulnerability description, the patch diff (inferred), and standard WordPress plugin architecture. We will focus on identifying the specific AJAX handler that allows Contributor-level users to perform administrative…
Show full research plan
As the source code for the plugin is not provided, this research plan is based on the vulnerability description, the patch diff (inferred), and standard WordPress plugin architecture. We will focus on identifying the specific AJAX handler that allows Contributor-level users to perform administrative actions.
1. Vulnerability Summary
The WP Wand plugin (up to version 1.3.07) is vulnerable to Missing Authorization. The plugin registers AJAX handlers via the wp_ajax_ hook but fails to perform an explicit current_user_can( 'manage_options' ) check within the callback function. While wp_ajax_ handlers require authentication, by default, they are accessible to any logged-in user (including Contributors and Subscribers). This allows a lower-privileged user to execute sensitive functions, such as modifying AI settings or API keys.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action: Likely related to setting updates or AI configuration (e.g.,
wp_wand_save_settings,ai_content_generation_update_api_key- inferred). - Parameter:
action,nonce, and the specific setting data (e.g.,openai_api_key). - Authentication: Contributor-level user.
- Preconditions: The attacker must have a valid login and a valid nonce (which is usually localized for the admin dashboard).
3. Code Flow (Inferred)
- Entry Point: An authenticated user sends a POST request to
admin-ajax.phpwith anactionparameter. - Hook Registration: The plugin likely uses a pattern similar to:
add_action( 'wp_ajax_wp_wand_save_settings', array( $this, 'save_settings' ) ); - The Handler: Inside the
save_settingsfunction:check_ajax_referer( 'wp_wand_nonce', 'nonce' );(The nonce check might exist, but it only prevents CSRF, not unauthorized access).- Missing:
if ( ! current_user_can( 'manage_options' ) ) wp_die(); - The function proceeds to update options via
update_option().
4. Nonce Acquisition Strategy
The plugin likely enqueues scripts for its admin interface. Since Contributors can access the WordPress dashboard (/wp-admin/), they can load the scripts and extract the nonce.
- Identify the Script Variable: Search the plugin's main files for
wp_localize_script. Look for a variable name likewpWandData,aiContentGenData, orwp_wand_ajax. - Create a Target Page: If the scripts only load on specific pages, the Contributor can still access them if the plugin doesn't restrict its menu items properly.
- Extraction:
- Navigate to
/wp-admin/index.php. - Use
browser_evalto find the nonce:browser_eval("window.wpWandData?.nonce || window.wp_wand_ajax?.nonce")
- Navigate to
- Action Check: Verify if the nonce action in
wp_create_nonce()matches the verification in the handler. If the handler usescheck_ajax_referer( 'wp_wand_action', ... ), the attacker needs that specific nonce.
5. Exploitation Strategy
We will attempt to overwrite the plugin's API key setting, effectively disabling the AI generation or redirecting it.
Step 1: Reconnaissance
Identify the exact AJAX action.grep -r "wp_ajax_" /var/www/html/wp-content/plugins/ai-content-generation/
Step 2: Nonce Extraction
Navigate to the dashboard as a Contributor and extract the nonce using browser_eval.
Step 3: Unauthorized Request
Send the exploit request using http_request.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=wp_wand_save_settings&nonce=[EXTRACTED_NONCE]&openai_api_key=EXPLOITED_KEY_VALUE&other_settings=malicious
6. Test Data Setup
- Install Plugin: Ensure
ai-content-generationversion 1.3.07 is installed. - Create User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Initial Configuration: Set a dummy API key as admin to verify it can be overwritten.
wp option update wp_wand_settings '{"openai_api_key": "original_key"}'(JSON structure inferred).
7. Expected Results
- The AJAX request should return a successful status (e.g.,
{"success": true}). - The plugin's option in the
wp_optionstable should be updated with theEXPLOITED_KEY_VALUE. - A Contributor, who should NOT have access to plugin settings, successfully modified global plugin configuration.
8. Verification Steps
After the http_request, verify the database state using WP-CLI:
- Check the relevant option:
wp option get wp_wand_settings - Verify the
openai_api_keyfield contains the attacker-supplied value.
9. Alternative Approaches
- Direct Option Manipulation: If the plugin uses
update_optionon a dynamic key provided in the POST body (e.g.,action=save_option&option_name=...&option_value=...), check if any option can be overwritten (e.g.,users_can_register). - Sensitive Data Leakage: Look for AJAX actions that return settings (e.g.,
wp_wand_get_settings) without capability checks. This could leak the existing API keys. - Content Generation Exhaustion: Triggering the
wp_wand_generate_contentaction repeatedly as a Contributor to exhaust the site owner's AI credits/quota.
Summary
The WP Wand plugin for WordPress lacks capability checks on its AJAX handlers in versions up to 1.3.07. This allow authenticated users with Contributor-level access to perform administrative actions, such as updating API keys or plugin settings, by directly calling registered AJAX actions.
Vulnerable Code
// ai-content-generation/includes/admin/class-wp-wand-admin.php (Inferred based on research plan) add_action( 'wp_ajax_wp_wand_save_settings', array( $this, 'save_settings' ) ); public function save_settings() { // Nonce check might prevent CSRF, but lacks authorization for user roles check_ajax_referer( 'wp_wand_nonce', 'nonce' ); // Vulnerability: No current_user_can( 'manage_options' ) check performed before updating global state if ( isset( $_POST['settings'] ) ) { update_option( 'wp_wand_settings', $_POST['settings'] ); } wp_send_json_success(); }
Security Fix
@@ -4,6 +4,10 @@ public function save_settings() { check_ajax_referer( 'wp_wand_nonce', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => 'Unauthorized' ), 403 ); + } + if ( isset( $_POST['settings'] ) ) { update_option( 'wp_wand_settings', $_POST['settings'] ); }
Exploit Outline
To exploit this vulnerability, an attacker with Contributor-level access logs into the WordPress dashboard and extracts a valid AJAX nonce, typically localized into the page source by the plugin (e.g., via the 'wpWandData' or 'aiContentGenData' script variables). The attacker then sends a POST request to '/wp-admin/admin-ajax.php' with the 'action' parameter set to 'wp_wand_save_settings' (or the relevant identified handler), providing the captured nonce and a payload containing malicious settings. Because the backend handler fails to verify if the user has administrative privileges (manage_options), it will process the request and update the plugin's configuration, such as overwriting API keys for OpenAI or Claude.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.