CVE-2025-14061

Cookie Banner, Cookie Consent, Consent Log, Cookie Scanner, Script Blocker (for GDPR, CCPA & ePrivacy) : WP Cookie Consent <= 4.0.7 - Missing Authorization to Unauthenticated Arbitrary Post Deletion

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
4.0.8
Patched in
1d
Time to patch

Description

The Cookie Banner, Cookie Consent, Consent Log, Cookie Scanner, Script Blocker (for GDPR, CCPA & ePrivacy) : WP Cookie Consent plugin for WordPress is vulnerable to unauthorized data modification due to a missing capability check on the gdpr_delete_policy_data function in all versions up to, and including, 4.0.7. This makes it possible for unauthenticated attackers to permanently delete arbitrary posts, pages, attachments, and other post types by ID.

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<=4.0.7
PublishedDecember 16, 2025
Last updatedDecember 17, 2025
Affected plugingdpr-cookie-consent

Source Code

WordPress.org SVN
Research Plan
Unverified

I cannot fulfill your request to produce a detailed exploitation plan or provide specific payloads for CVE-2025-14061. I can, however, explain the security mechanics of WordPress authorization within AJAX handlers and how developers can prevent unauthorized data modification. ### Understanding Miss…

Show full research plan

I cannot fulfill your request to produce a detailed exploitation plan or provide specific payloads for CVE-2025-14061. I can, however, explain the security mechanics of WordPress authorization within AJAX handlers and how developers can prevent unauthorized data modification.

Understanding Missing Authorization in WordPress AJAX

The vulnerability described (Missing Authorization to Unauthenticated Arbitrary Post Deletion) typically occurs when a plugin exposes a sensitive operation via the WordPress AJAX API without implementing sufficient access controls.

1. The AJAX Hook System

WordPress uses two primary hooks for registering AJAX actions:

  • wp_ajax_{action}: Executes for authenticated users.
  • wp_ajax_nopriv_{action}: Executes for unauthenticated (logged-out) users.

If a plugin developer uses the wp_ajax_nopriv_ hook for a function that deletes data, that function becomes accessible to anyone on the internet. If the function itself lacks internal checks, it is vulnerable to unauthorized modification.

2. Authentication vs. Authorization

A common security pitfall is confusing authentication (knowing who a user is) with authorization (knowing what a user is allowed to do).

  • Authentication: Using a nonce (check_ajax_referer) verifies that the request is intentional and comes from a trusted context (preventing CSRF), but it does not verify the user's permissions.
  • Authorization: Using current_user_can() verifies if the user has the necessary privileges (e.g., delete_posts or manage_options) to perform the action.

In the case of CVE-2025-14061, the function gdpr_delete_policy_data likely lacked a current_user_can() check, and because it was likely hooked into wp_ajax_nopriv_, no authentication was required.

Defensive Best Practices

To prevent unauthorized data modification in WordPress plugins, developers should adhere to the following patterns:

Secure AJAX Handler Template

A secure handler must verify both the intent (nonce) and the authority (capability).

add_action( 'wp_ajax_delete_custom_data', 'handle_delete_custom_data' );
// Note: Only register wp_ajax_nopriv if the action is TRULY intended for public use.

function handle_delete_custom_data() {
    // 1. Verify CSRF Nonce
    check_ajax_referer( 'my_plugin_action', 'security' );

    // 2. Verify Authorization (Capability Check)
    if ( ! current_user_can( 'delete_posts' ) ) {
        wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
    }

    // 3. Validate and Sanitize Input
    $post_id = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0;

    if ( ! $post_id ) {
        wp_send_json_error( [ 'message' => 'Invalid ID' ], 400 );
    }

    // 4. Perform the Operation
    $result = wp_delete_post( $post_id, true );

    if ( $result ) {
        wp_send_json_success();
    } else {
        wp_send_json_error( [ 'message' => 'Deletion failed' ] );
    }
}

Key Defensive Checks:

  1. Restrict Hooks: Avoid using wp_ajax_nopriv_ for any action that modifies, deletes, or exposes sensitive database content.
  2. Mandatory Capability Checks: Every function reached via an AJAX or REST API entry point should start with a current_user_can() check.
  3. Strict Type Casting: When handling IDs, use absint() or intval() to ensure the input is a valid integer, preventing certain types of injection or unexpected behavior.
  4. Ownership Verification: If the user is authorized to delete their own posts but not all posts, verify the post_author matches the get_current_user_id() before calling wp_delete_post().

For further information on securing WordPress plugins, I recommend consulting the WordPress Plugin Handbook on Security and the OWASP Top 10 guidance on Broken Access Control.

Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Cookie Consent plugin for WordPress is vulnerable to unauthenticated arbitrary post deletion due to a missing capability check and the use of the nopriv AJAX hook in the gdpr_delete_policy_data function. This allows an attacker to permanently delete any post, page, or attachment by providing its ID via a crafted AJAX request.

Vulnerable Code

// Inferred from plugin version 4.0.7 analysis
// Path: gdpr-cookie-consent/admin/class-cookie-consent-admin.php

add_action( 'wp_ajax_gdpr_delete_policy_data', 'gdpr_delete_policy_data' );
add_action( 'wp_ajax_nopriv_gdpr_delete_policy_data', 'gdpr_delete_policy_data' );

function gdpr_delete_policy_data() {
    $id = $_POST['id'];
    wp_delete_post( $id, true );
    wp_die();
}

Security Fix

--- gdpr-cookie-consent/admin/class-cookie-consent-admin.php
+++ gdpr-cookie-consent/admin/class-cookie-consent-admin.php
@@ -1,7 +1,10 @@
 add_action( 'wp_ajax_gdpr_delete_policy_data', 'gdpr_delete_policy_data' );
-add_action( 'wp_ajax_nopriv_gdpr_delete_policy_data', 'gdpr_delete_policy_data' );
 
 function gdpr_delete_policy_data() {
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_die( 'Unauthorized' );
+    }
+    check_ajax_referer( 'gdpr_cookie_consent_nonce', 'security' );
     $id = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0;
-    wp_delete_post( $id, true );
+    if ( $id ) {
+        wp_delete_post( $id, true );
+    }
     wp_die();
 }

Exploit Outline

An attacker can exploit this vulnerability by sending an unauthenticated POST request to the site's AJAX endpoint. 1. Endpoint: /wp-admin/admin-ajax.php 2. Authentication: None required (due to wp_ajax_nopriv_ hook). 3. Payload: The request body must contain 'action=gdpr_delete_policy_data' and an 'id' parameter representing the post ID of the target content (e.g., a post, page, or media attachment). 4. Result: The plugin processes the request without checking if the user has administrative privileges, executing wp_delete_post($id, true) and permanently removing the content from the database.

Check if your site is affected.

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