CVE-2025-67976

Watu Quiz <= 3.4.5 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.4.5.1
Patched in
6d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.4.5
PublishedDecember 15, 2025
Last updatedDecember 20, 2025
Affected pluginwatu

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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_quiz
    • id: 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

  1. 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' );
    
  2. Execution: When a request hits admin-ajax.php?action=watu_copy_quiz, the function watu_copy_quiz() is invoked.
  3. 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');
    
  4. 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_quizzes capability or manage_options.
  5. Sink: Database INSERT queries into the wp_watu_quizzes table.

4. Nonce Acquisition Strategy

The plugin localizes the nonce for its JavaScript files. This is typically done using wp_localize_script.

  1. Identify Script: Watu Quiz enqueues scripts like watu-js or watu-admin-js.
  2. 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).
  3. Browser Navigation: Navigate to this page as the Contributor user.
  4. Extraction: Use browser_eval to 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")
  5. Alternative: If the Contributor can access the dashboard, check for watu_vars there.

5. Exploitation Strategy

  1. Setup: Authenticate as a Contributor.
  2. Extract Nonce: Follow the strategy in Section 4 to obtain the watu_nonce.
  3. 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]
      
  4. Verify Response: A successful duplication often results in a 302 Redirect to the editing page of the new quiz or a success string.

6. Test Data Setup

  1. 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".
  2. User Creation:
    • Create a Contributor: wp user create attacker attacker@example.com --role=contributor --user_pass=password123.

7. Expected Results

  • The admin-ajax.php request should return a 200 OK or 302 Redirect.
  • A new record should be created in the wp_watu_quizzes table with a name like "Copy of Original Quiz" or identical to the original.
  • The Contributor user, who lacks the watu_manage_quizzes capability, 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.
  • 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).
  • Check for missing watu_nonce: Some handlers might only check for is_user_logged_in() and omit the nonce check entirely, allowing for CSRF or simple unauthorized execution without the extraction step.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/wp-content/plugins/watu/lib/controllers/quiz-controller.php
+++ b/wp-content/plugins/watu/lib/controllers/quiz-controller.php
@@ -... +... @@
 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.