Quiz Maker by AYS <= 6.7.1.29 - Unauthenticated Stored Cross-Site Scripting via 'rate_reason'
Description
The Quiz Maker by AYS plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'rate_reason' parameter in all versions up to, and including, 6.7.1.29 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v6.7.1.30
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-6817 ## 1. Vulnerability Summary The **Quiz Maker by AYS** plugin (<= 6.7.1.29) contains an unauthenticated stored cross-site scripting (XSS) vulnerability. The plugin allows users (including unauthenticated guests) to submit ratings and feedback ("reasons") …
Show full research plan
Exploitation Research Plan - CVE-2026-6817
1. Vulnerability Summary
The Quiz Maker by AYS plugin (<= 6.7.1.29) contains an unauthenticated stored cross-site scripting (XSS) vulnerability. The plugin allows users (including unauthenticated guests) to submit ratings and feedback ("reasons") upon completing a quiz. The rate_reason parameter is stored in the database without sufficient sanitization and is later rendered in the WordPress administrative dashboard (the "Reviews" list) without proper escaping.
An attacker can inject arbitrary JavaScript, which will execute in the context of an administrator viewing the quiz reviews, potentially leading to session hijacking or the creation of unauthorized administrative accounts.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - AJAX Action:
ays_finish_quiz(Commonly used for quiz completion and rating submission in this plugin). - Vulnerable Parameter:
rate_reason - Authentication: None required (Unauthenticated).
- Preconditions: At least one quiz must be published and accessible via a shortcode.
- Sink: The injected script is executed when an admin visits the "Reviews" page:
wp-admin/admin.php?page=ays_quiz_all_reviews_quiz_maker.
3. Code Flow
- Submission (Public):
- The
Quiz_Maker_Publicclass (inpublic/class-quiz-maker-public.php) registers AJAX handlers for quiz completion. - The handler (likely
ays_finish_quiz) retrieves$_POST['rate_reason']. - The data is stored in the
{$wpdb->prefix}aysquiz_reviewstable (or similar results table) via a database insert operation.
- The
- Display (Admin):
- An administrator navigates to the "Reviews" list.
- The
All_Reviews_List_Tableclass inincludes/lists/class-quiz-maker-all-reviews-list-table.phpis instantiated to display the records. - The
rate_reasonvalue is retrieved from the database. - The value is echoed into the table row, typically within a
column_defaultor specificcolumn_rate_reasonmethod (not fully shown in snippet, but implied by theWP_List_Tableimplementation), without usingesc_html()orwp_kses().
4. Nonce Acquisition Strategy
The plugin enqueues public scripts and localizes a nonce for AJAX operations.
- Identify Shortcode: The main shortcode is
[ays_quiz id='QUIZ_ID'](verified fromREADME.txt). - Creation: Use WP-CLI to create a test quiz and a page containing its shortcode.
- Extraction:
- Navigate to the page.
- The plugin localizes data into a JavaScript object. Based on the plugin's structure, the object is typically named
ays_quiz_ajax. - JS Variable:
window.ays_quiz_ajax - Nonce Key:
nonce - Action String: The nonce is generated via
wp_create_nonce('ays_quiz_ajax_nonce').
5. Exploitation Strategy
Step 1: Target Identification
Determine a valid Quiz ID. If no quiz exists, one must be created.
Step 2: Obtain Nonce
Access a page where the quiz is rendered and extract the ays_quiz_ajax.nonce value.
Step 3: Inject Payload
Send an unauthenticated POST request to admin-ajax.php.
Request Details:
- URL:
http://TARGET_URL/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body Parameters:
action:ays_finish_quizquiz_id:[ID_OF_CREATED_QUIZ]ays_quiz_questions_ids:[ID_OF_A_QUESTION](often required for valid completion)ays_quiz_nonce:[EXTRACTED_NONCE]ays_quiz_rate:5rate_reason:<script>alert(document.domain);</script>
Step 4: Trigger Execution
Log in as an Administrator and navigate to the Reviews page.
6. Test Data Setup
- Create Quiz:
# Create a simple quiz via WP-CLI (if possible) or use a known ID # Note: AYS Quiz Maker stores data in custom tables, so we use wp_eval to insert a dummy quiz wp eval "global \$wpdb; \$wpdb->insert(\"{\$wpdb->prefix}aysquiz_quizes\", array('title' => 'Exploit Test', 'status' => 'publish')); echo \$wpdb->insert_id;" - Create Page:
wp post create --post_type=page --post_title="Quiz Page" --post_status=publish --post_content="[ays_quiz id='1']"
7. Expected Results
- The AJAX submission should return a JSON success message (e.g.,
{"status":true,...}). - When the admin visits
wp-admin/admin.php?page=ays_quiz_all_reviews_quiz_maker, the browser will executealert(document.domain).
8. Verification Steps
- Check Database:
Confirm thewp db query "SELECT rate_reason FROM wp_aysquiz_reviews ORDER BY id DESC LIMIT 1;"rate_reasoncontains the raw<script>tag. - Check Admin UI (Manual/Automated):
Usebrowser_navigateas an admin to the reviews page and check for the presence of the alert or the unescaped string in the HTML source.
9. Alternative Approaches
If ays_finish_quiz is too complex or requires valid answers, attempt a direct rating action if available:
- Action:
ays_quiz_rate - Payload: Send
rate_reasonandquiz_idto this action. - JS Check: Inspect
public/js/quiz-maker-public.js(if accessible) to see exactly how the "Rating" submit button sends data toadmin-ajax.php. Look forjQuery.ajaxcalls involvingrate_reason.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.