- Vulnerability Background
-
What is this vulnerability?
- CVE-2025-9294 is an improper authorization vulnerability in the WordPress plugin Quiz and Survey Master (QSM) – Easy Quiz and Survey Maker.
- The vulnerable code path is in
php/admin/options-page-questions-tab.php, specifically theqsm_dashboard_delete_resulthandler. - The function validated a nonce but did not verify that the current user had sufficient privileges to delete quiz results.
-
Why is it critical/important?
- It allows authenticated users with Subscriber-level access and above to delete quiz result records they should not be able to remove.
- This is a data integrity and availability issue: quiz results can be erased without proper authorization.
- Unauthorized destructive actions in an application that hosts assessment data can undermine trust and disrupt workflows.
-
What systems/versions are affected?
- All versions of the QSM plugin up to and including 10.3.1 are affected.
- The vulnerability impacts WordPress sites using this plugin where authenticated accounts exist with Subscriber or higher privileges.
- Technical Details
-
Root cause analysis
- The delete handler performed the following check:
isset( $_POST['nonce'] )wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_rest' )$result_id
- It omitted any capability or role verification.
- As a result, any authenticated user able to submit a valid nonce and a result identifier could trigger result deletion.
- This is a classic broken access control/authorization failure. Authentication and CSRF protection were partially present, but authorization was missing.
- The delete handler performed the following check:
-
Attack vector and exploitation conditions
- Attacker needs an authenticated WordPress account on a vulnerable site, with Subscriber-level access or above.
- The attacker must be able to send a POST request to the QSM deletion handler with:
nonceresult_id
- Since the code verifies a nonce but not privileges, an attacker with access to a valid nonce can delete arbitrary quiz results.
- If the endpoint is reachable by authenticated users and the nonce generation is accessible from the session, exploitation is straightforward.
-
Security implications
- Unauthorized deletion of quiz result data.
- Potential disruption of data analytics, grading, or reporting workflows.
- Loss of evidence or audit trail for quiz and survey responses.
- This is not remote unauthenticated code execution, but it is a high-severity authorization bypass affecting data integrity.
- Patch Analysis
-
What code changes were made?
- The patch modifies the conditional guarding result deletion.
- Old code:
if ( isset( $_POST['nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_rest' ) && $result_id ) {
- Fixed code:
if ( isset( $_POST['nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_rest' ) && $result_id && current_user_can( 'administrator' ) ) {
-
How do these changes fix the vulnerability?
- The added
current_user_can( 'administrator' )check enforces that only administrator-role users can execute the delete action. - This completes the authorization chain: request authenticity via nonce plus user privilege verification.
- Unauthorized roles such as Subscriber and Contributor can no longer trigger the deletion path.
- The added
-
Security improvements introduced
- Separation of concerns between authentication/nonce validation and authorization.
- Restriction of a destructive endpoint to a privileged role.
- Reduced attack surface for improper data deletion.
- Proof of Concept (PoC) Guide
-
Prerequisites for exploitation
- A WordPress site running QSM plugin version 10.3.1 or earlier.
- An authenticated account with Subscriber-level access or higher.
- Access to the QSM quiz result deletion endpoint or the ability to craft the requisite request.
-
Step-by-step exploitation approach
- Authenticate to the target WordPress instance with a Subscriber account.
- Identify or obtain a valid
noncevalue for the QSM deletion action. In many cases, the plugin page or AJAX context exposes the nonce to authenticated users. - Craft a POST request to the plugin’s delete handler endpoint with parameters:
nonce=<valid_nonce>result_id=<target_result_id>
- Send the POST request.
- Verify that the targeted quiz result has been removed.
-
Expected behavior vs exploited behavior
- Expected behavior: the request should be denied for non-administrator users, and the quiz result should remain intact.
- Exploited behavior: the request succeeds and deletes the quiz result despite the attacker having only Subscriber-level privileges.
-
How to verify the vulnerability exists
- As a low-privilege user, submit a legitimate-looking delete request and observe whether the quiz result is deleted.
- Check database entries for result rows before and after sending the request.
- Confirm that the QSM plugin is version 10.3.1 or earlier and that the patched role check is absent.
- Recommendations
-
Mitigation strategies
- Upgrade QSM to a patched version beyond 10.3.1.
- If immediate upgrade is not possible, patch the handler to include a capability check before performing deletion.
- Use least-privilege controls for administrative operations.
-
Detection methods
- Audit logs for POST requests to QSM delete endpoints from non-admin accounts.
- Monitor for successful deletion actions initiated by Subscriber or Contributor roles.
- Search for evidence of unauthorized
qsm_dashboard_delete_resultinvocation in server or application logs.
-
Best practices to prevent similar issues
- Always enforce capability checks on all state-changing admin actions.
- Do not rely solely on nonce validation for authorization.
- Prefer capability-based checks (
current_user_can( 'manage_options' ), custom capabilities) over direct role names when possible. - Conduct code reviews focused on admin AJAX handlers and form processors to ensure authentication and authorization are both present.
- Validate that every destructive operation is gated by both request validation and explicit user privilege verification.