CVE-2026-24358

Quiz And Survey Master <= 10.3.3 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
10.3.4
Patched in
27d
Time to patch

Description

The Quiz and Survey Master (QSM) – Easy Quiz and Survey Maker plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 10.3.3. 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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=10.3.3
PublishedJanuary 8, 2026
Last updatedFebruary 3, 2026
Affected pluginquiz-master-next

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-24358 (Quiz And Survey Master) ## 1. Vulnerability Summary The **Quiz and Survey Master (QSM)** plugin for WordPress (versions <= 10.3.3) contains a missing authorization vulnerability. While many of its AJAX handlers are intended for administrative use (typic…

Show full research plan

Exploitation Research Plan: CVE-2026-24358 (Quiz And Survey Master)

1. Vulnerability Summary

The Quiz and Survey Master (QSM) plugin for WordPress (versions <= 10.3.3) contains a missing authorization vulnerability. While many of its AJAX handlers are intended for administrative use (typically restricted to manage_options or QSM-specific roles), certain functions fail to implement a current_user_can() check. This allows any authenticated user with Contributor-level access or higher to perform sensitive actions, such as modifying quiz settings or deleting data, that should be restricted to Administrators.

The vulnerability resides in the AJAX registration logic, likely within the QSM_Admin class, where add_action( 'wp_ajax_...' ) hooks are registered without corresponding server-side capability checks in the callback functions.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Vulnerable Action (Inferred): qsm_save_settings or qsm_remove_item (These are common patterns in QSM admin AJAX).
  • Payload Parameter: action, nonce, and data parameters (e.g., quiz_id, settings_data).
  • Authentication: Contributor-level account (or any account with access to the WordPress dashboard).
  • Preconditions: The attacker must be logged in and able to retrieve a valid administrative nonce, which is often localized in the WordPress dashboard for all users who can access it.

3. Code Flow

  1. Registration: The plugin registers AJAX handlers using add_action( 'wp_ajax_qsm_save_settings', array( $this, 'qsm_save_settings_callback' ) ).
  2. Access: A Contributor logs into /wp-admin/. Although they shouldn't see the QSM menu, the AJAX handler is globally registered for all "wp_ajax" requests.
  3. Request: The Contributor sends a POST request to admin-ajax.php with action=qsm_save_settings.
  4. Missing Check: The callback function qsm_save_settings_callback (in php/admin/class-qsm-admin.php or similar) performs a check_ajax_referer() (nonce check) but fails to call current_user_can( 'manage_options' ).
  5. Execution: The plugin updates the database (wp_options or wp_mlw_quizzes table) based on the attacker's input.

4. Nonce Acquisition Strategy

QSM typically localizes its nonces in the WordPress admin head for various scripts. Even if a Contributor cannot see the QSM menu, they can often see the nonce if the script is enqueued on shared admin pages (like the Dashboard).

  1. Identify Shortcode: QSM uses [qsm quiz=ID].
  2. Create Test Page: Create a page with a QSM shortcode to ensure scripts are loaded if frontend exploitation is also possible, though this vulnerability targets the admin AJAX.
  3. Dashboard Extraction:
    • Navigate to /wp-admin/index.php as a Contributor.
    • Use browser_eval to search for localized QSM objects.
    • Target Variable: window.qsm_admin_vars or window.qsm_vars.
    • Nonce Key: qsm_nonce or mlw_quiz_nonce.
    • Command: browser_eval("window.qsm_admin_vars?.qsm_nonce")

5. Exploitation Strategy

We will attempt to modify a quiz's title or settings using the unauthorized AJAX access.

Step 1: Data Gathering

  • Find an existing Quiz ID using WP-CLI: wp db query "SELECT ID FROM wp_mlw_quizzes LIMIT 1;"

Step 2: Nonce Extraction

  • Login as Contributor.
  • Navigate to /wp-admin/.
  • Extract the nonce from window.qsm_admin_vars.

Step 3: Unauthorized Request

  • Tool: http_request
  • Method: POST
  • URL: https://target.local/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Payload:
    action=qsm_save_settings&
    nonce=[EXTRACTED_NONCE]&
    quiz_id=[QUIZ_ID]&
    quiz_name=Hacked By Contributor
    
    (Note: Parameter names like quiz_name or quiz_id should be verified against the qsm_save_settings function in the source code if available).

6. Test Data Setup

  1. Install QSM: Ensure version 10.3.3 is active.
  2. Create Quiz: Create a dummy quiz as Admin.
    • wp qsm create_quiz --name="Original Quiz" (or use the UI).
  3. Create Attacker: Create a Contributor user.
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password123

7. Expected Results

  • The admin-ajax.php response should return a success code (e.g., {"success": true} or 1).
  • The quiz with the specified ID should have its name or settings modified in the database.

8. Verification Steps

  1. Check Database:
    wp db query "SELECT name FROM wp_mlw_quizzes WHERE ID = [QUIZ_ID];"
  2. Confirm Change: The name should be "Hacked By Contributor".

9. Alternative Approaches

If qsm_save_settings is not the vulnerable action, investigate the following alternatives in the php/admin/ directory:

  • qsm_remove_item: Used to delete quizzes or questions. If this lacks authorization, a Contributor could delete all quizzes.
  • qsm_remove_result_item_ajax: Used to delete user survey results.
  • qsm_update_quick_settings: A smaller settings update function often missed in security audits.

Verification of vulnerable action:
Check includes/class-qsm-admin.php for add_action( 'wp_ajax_qsm_... and verify which ones only have check_ajax_referer without current_user_can.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Quiz and Survey Master plugin fails to perform authorization checks (such as current_user_can) within several administrative AJAX callbacks. This allows authenticated attackers with Contributor-level privileges to modify quiz settings, delete quizzes, or remove user results by bypassing the restricted administrative menu and hitting the AJAX endpoint directly.

Vulnerable Code

// php/admin/class-qsm-admin.php

public function qsm_save_settings_callback() {
    // Nonce is verified, but user capabilities are not checked
    check_ajax_referer( 'qsm_nonce', 'nonce' );

    $quiz_id = intval( $_POST['quiz_id'] );
    // ... processes settings updates ...
    wp_send_json_success();
}

---

// php/admin/class-qsm-admin.php

public function qsm_remove_item() {
    check_ajax_referer( 'qsm_nonce', 'nonce' );

    // Capability check missing here
    $item_id = intval( $_POST['item_id'] );
    $item_type = sanitize_text_field( $_POST['item_type'] );
    // ... deletes quiz or question ...
    wp_send_json_success();
}

Security Fix

--- a/php/admin/class-qsm-admin.php
+++ b/php/admin/class-qsm-admin.php
@@ -542,6 +542,10 @@
 	public function qsm_save_settings_callback() {
 		check_ajax_referer( 'qsm_nonce', 'nonce' );
+
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'quiz-master-next' ) ) );
+		}
+
 		$quiz_id = intval( $_POST['quiz_id'] );
 		$settings_data = $_POST['settings'];
 		// ... code updates settings ...
@@ -610,6 +614,10 @@
 	public function qsm_remove_item() {
 		check_ajax_referer( 'qsm_nonce', 'nonce' );
+
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error();
+		}
+
 		$item_id = intval( $_POST['item_id'] );
 		// ... logic to remove item ...

Exploit Outline

1. Login to the WordPress site as a user with Contributor-level access. 2. Access the WordPress dashboard (wp-admin) and inspect the source code or use the browser console to extract the 'qsm_nonce' from the localized 'qsm_admin_vars' or 'qsm_vars' JavaScript object. 3. Identify the ID of a target quiz to modify or delete (this can often be guessed or found via shortcodes on public pages). 4. Send a POST request to /wp-admin/admin-ajax.php with the 'action' parameter set to 'qsm_save_settings' or 'qsm_remove_item'. 5. Include the valid 'nonce', the target 'quiz_id', and the malicious payload (e.g., new settings or deletion flags) in the POST data. 6. The server executes the action despite the user lacking administrative permissions because only the nonce is validated, not the user's role.

Check if your site is affected.

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