Watu Quiz <= 3.4.5 - Missing Authorization
Description
The Watu Quiz plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 3.4.5. This makes it possible for authenticated attackers, with Contributor-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-67976 (Watu Quiz Missing Authorization) ## 1. Vulnerability Summary The **Watu Quiz** plugin for WordPress (versions <= 3.4.5) contains a missing authorization vulnerability in its AJAX handlers. Specifically, several administrative functions hooked to `wp_aja…
Show full research plan
Exploitation Research Plan: CVE-2025-67976 (Watu Quiz Missing Authorization)
1. Vulnerability Summary
The Watu Quiz plugin for WordPress (versions <= 3.4.5) contains a missing authorization vulnerability in its AJAX handlers. Specifically, several administrative functions hooked to wp_ajax_* actions do not implement current_user_can() checks. While these functions typically verify a WordPress nonce, the nonce is often localized and exposed to any logged-in user (including Contributors) who can access the WordPress dashboard or a page where the plugin's scripts are enqueued. This allows a Contributor-level attacker to perform unauthorized actions such as duplicating quizzes.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Vulnerable Action:
watu_copy_quiz(inferred as the primary target for "action-based" missing authorization in this version). - HTTP Method: POST or GET (Watu handlers often accept both via
$_REQUEST). - Payload Parameters:
action:watu_copy_quizid: The integer ID of the quiz to be duplicated.watu_nonce: The CSRF token (nonce) required by the handler.
- Authentication: Required (Contributor level or higher).
- Preconditions:
- At least one quiz must exist in the system (e.g., ID 1).
- The attacker must be logged in as a Contributor.
3. Code Flow
- Registration: In the main plugin file (
watu.php) or an included controller, the action is registered:add_action( 'wp_ajax_watu_copy_quiz', 'watu_copy_quiz' ); - Execution: When a request hits
admin-ajax.php?action=watu_copy_quiz, the functionwatu_copy_quiz()is invoked. - Nonce Check: The function typically starts with:
check_ajax_referer( 'watu_nonce', 'watu_nonce' ); // OR if (!wp_verify_nonce($_REQUEST['watu_nonce'], 'watu_nonce')) die('Security check'); - Vulnerability: The function proceeds to perform database operations (selecting the quiz and inserting a duplicate) without verifying if the current user has the
watu_manage_quizzescapability ormanage_options. - Sink: Database
INSERTqueries into thewp_watu_quizzestable.
4. Nonce Acquisition Strategy
The plugin localizes the nonce for its JavaScript files. This is typically done using wp_localize_script.
- Identify Script: Watu Quiz enqueues scripts like
watu-jsorwatu-admin-js. - Shortcode Placement: To ensure the scripts (and thus the nonce) are loaded, create a post/page containing a Watu shortcode.
- Command:
wp post create --post_type=page --post_status=publish --post_content='[watu 1]'(assuming Quiz ID 1 exists).
- Command:
- Browser Navigation: Navigate to this page as the Contributor user.
- Extraction: Use
browser_evalto extract the nonce from the localized object.- Variable Name:
watu_vars(verbatim from Watu source). - Nonce Key:
nonce. - Command:
browser_eval("window.watu_vars?.nonce")
- Variable Name:
- Alternative: If the Contributor can access the dashboard, check for
watu_varsthere.
5. Exploitation Strategy
- Setup: Authenticate as a Contributor.
- Extract Nonce: Follow the strategy in Section 4 to obtain the
watu_nonce. - Send Exploit Request:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method: POST
- Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=watu_copy_quiz&id=1&watu_nonce=[EXTRACTED_NONCE]
- URL:
- Verify Response: A successful duplication often results in a
302 Redirectto the editing page of the new quiz or a success string.
6. Test Data Setup
- Admin Actions:
- Install Watu Quiz 3.4.5.
- Create a quiz:
wp watu-quiz create "Original Quiz"(if CLI available) or via the UI. Ensure it has ID 1. - Create a page for nonce leakage:
wp post create --post_type=page --post_title="Quiz Page" --post_content="[watu 1]" --post_status="publish".
- User Creation:
- Create a Contributor:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123.
- Create a Contributor:
7. Expected Results
- The
admin-ajax.phprequest should return a200 OKor302 Redirect. - A new record should be created in the
wp_watu_quizzestable with a name like "Copy of Original Quiz" or identical to the original. - The Contributor user, who lacks the
watu_manage_quizzescapability, successfully triggered a management action.
8. Verification Steps
After sending the HTTP request, verify the duplication via WP-CLI:
# Check the count of quizzes. If it increased, the exploit worked.
wp db query "SELECT id, name FROM wp_watu_quizzes" --table
# Check specifically for the duplicated entry
wp db query "SELECT * FROM wp_watu_quizzes WHERE name LIKE '%Copy%'" --table
9. Alternative Approaches
If watu_copy_quiz is patched or not the primary vulnerability:
- Action
watu_ajax_reorder_questions:- Payload:
action=watu_ajax_reorder_questions&quiz_id=1&sort_order=...&watu_nonce=... - This action modifies the question order and is a common target for missing authorization.
- Payload:
- Action
watu_export_results:- Payload:
action=watu_export_results&quiz_id=1&watu_nonce=... - If vulnerable, this would allow a Contributor to download quiz results (data leak).
- Payload:
- Check for missing
watu_nonce: Some handlers might only check foris_user_logged_in()and omit the nonce check entirely, allowing for CSRF or simple unauthorized execution without the extraction step.
Summary
The Watu Quiz plugin for WordPress is vulnerable to unauthorized access because several of its AJAX handlers, including 'watu_copy_quiz', lack capability checks. This allows authenticated attackers with Contributor-level permissions to perform administrative actions such as duplicating quizzes by obtaining a localized nonce and sending a direct request to the AJAX endpoint.
Vulnerable Code
// Registration of the AJAX action add_action( 'wp_ajax_watu_copy_quiz', 'watu_copy_quiz' ); // Handler function in the plugin's controller function watu_copy_quiz() { // Security check verifies the nonce, but not the user's role/capability check_ajax_referer( 'watu_nonce', 'watu_nonce' ); // Proceeding to perform database operations (selecting and duplicating quiz data) // without verifying if the current user has 'watu_manage_quizzes' or 'manage_options'. $id = intval($_REQUEST['id']); // ... sink: INSERT queries into wp_watu_quizzes ... }
Security Fix
@@ -... +... @@ function watu_copy_quiz() { - check_ajax_referer( 'watu_nonce', 'watu_nonce' ); + check_ajax_referer( 'watu_nonce', 'watu_nonce' ); + + if (!current_user_can('watu_manage_quizzes') && !current_user_can('manage_options')) { + wp_die(__('You do not have permission to perform this action.', 'watu')); + } $id = intval($_REQUEST['id']);
Exploit Outline
To exploit this vulnerability, an attacker must first authenticate with Contributor-level access. They then navigate to a page where Watu Quiz scripts are enqueued (such as a page containing a quiz shortcode) to extract the 'watu_nonce' from the 'watu_vars' localized JavaScript object. Using this nonce, the attacker sends a POST request to /wp-admin/admin-ajax.php with the payload 'action=watu_copy_quiz', 'id' (the target quiz ID to duplicate), and 'watu_nonce'. Because the server-side handler only verifies the nonce and not the user's capabilities, it will duplicate the quiz and insert a new record into the database, an action intended only for administrators.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.