Complianz – GDPR/CCPA Cookie Consent <= 7.4.5 - Missing Authorization to Unauthenticated Private Post Content Disclosure via Consent Area REST Endpoint
Description
The Complianz – GDPR/CCPA Cookie Consent plugin for WordPress is vulnerable to unauthorized data access in all versions up to, and including, 7.4.5 This is due to the REST API endpoint at /wp-json/complianz/v1/consent-area/{post_id}/{block_id} using __return_true as the permission_callback, allowing any unauthenticated user to access it. The cmplz_rest_consented_content() function retrieves a post by ID via get_post() and returns the consentedContent attribute of any complianz/consent-area block found in it, without checking if the post is published or if the user has permission to read it. This makes it possible for unauthenticated attackers to read the consent area block content from private, draft, or unpublished posts.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
<=7.4.5What Changed in the Fix
Changes introduced in v7.4.6
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-4019 (Complianz Private Post Disclosure) ## 1. Vulnerability Summary The **Complianz – GDPR/CCPA Cookie Consent** plugin (<= 7.4.5) contains a missing authorization vulnerability in its REST API implementation. Specifically, the endpoint `/wp-json/complianz/v1…
Show full research plan
Exploitation Research Plan: CVE-2026-4019 (Complianz Private Post Disclosure)
1. Vulnerability Summary
The Complianz – GDPR/CCPA Cookie Consent plugin (<= 7.4.5) contains a missing authorization vulnerability in its REST API implementation. Specifically, the endpoint /wp-json/complianz/v1/consent-area/{post_id}/{block_id} uses __return_true as its permission_callback, effectively allowing unauthenticated access.
The underlying function cmplz_rest_consented_content() retrieves a WordPress post using get_post($post_id) but fails to verify if the post is published, private, or draft, and does not check if the requesting user has the read_post capability for that specific post. If the post contains a complianz/consent-area block, the function extracts and returns the consentedContent attribute, leading to unauthorized disclosure of sensitive information hidden within unpublished or private posts.
2. Attack Vector Analysis
- Endpoint:
GET /wp-json/complianz/v1/consent-area/{post_id}/{block_id} - Authentication: None (Unauthenticated)
- Parameters:
post_id(URL Path): The ID of the target post (Draft, Private, or Scheduled).block_id(URL Path): The unique identifier assigned to the specific "Consent Area" block within that post.
- Vulnerable Component:
cmplz_rest_consented_content()(inferred location:includes/class-rest-api.phpor similar logic file, though not provided in the source snippets). - Preconditions: A post must exist (regardless of status) that contains a Gutenberg block of type
complianz/consent-areawhich has theconsentedContentattribute populated.
3. Code Flow (Inferred from Description)
- Registration: The plugin registers a REST route using
register_rest_route('complianz/v1', '/consent-area/(?P<post_id>\d+)/(?P<block_id>[a-zA-Z0-9\-]+)'). - Authorization: The
permission_callbackis set to__return_true, bypassing WordPress's default REST API authorization checks. - Execution: When a request is made,
cmplz_rest_consented_content(WP_REST_Request $request)is invoked. - Retrieval:
$post_id = $request->get_param('post_id')$post = get_post($post_id)
- Vulnerability Sink: The code proceeds to parse
$post->post_contentwithout checking$post->post_status. It looks for blocks matchingcomplianz/consent-area. - Data Extraction: If a block matches the provided
block_id, the value of theconsentedContentattribute is returned in the JSON response.
4. Nonce Acquisition Strategy
According to the vulnerability description, the permission_callback is __return_true. This indicates that no WordPress nonce is required for this endpoint when accessed via a direct GET request.
If for any reason the environment requires a REST nonce (e.g., due to global security headers), it can be obtained from the homepage:
- Navigate to the homepage using
browser_navigate. - Execute
browser_eval("window.wpApiSettings?.nonce"). - Note: This is likely unnecessary given the
__return_truecallback.
5. Exploitation Strategy
The goal is to retrieve content from a private post that contains a complianz/consent-area block.
Step 1: Discover Post IDs (Brute Force)
Since post IDs are incremental, an attacker can iterate through IDs. For the PoC, we will create a known post ID.
Step 2: Request the Content
- Send a GET request to the target endpoint.
- Tool:
http_request - URL:
http://localhost:8080/wp-json/complianz/v1/consent-area/{post_id}/{block_id} - Headers:
Accept: application/json
6. Test Data Setup
To verify the vulnerability, we must create a private post containing the specific Gutenberg block structure used by Complianz.
WP-CLI Command:
# Create a private post with a Consent Area block
# Note: The blockId and consentedContent attributes are key.
wp post create --post_type=post \
--post_status=private \
--post_title="Secret Disclosure Test" \
--post_content='<!-- wp:complianz/consent-area {"blockId":"exploit-id-123","consentedContent":"CENSORED_PRIVATE_TOKEN_9999"} --> <div class="cmplz-consent-area">Visible placeholder</div> <!-- /wp:complianz/consent-area -->'
Note the resulting ID from the command output.
7. Expected Results
- Success: The REST API returns a
200 OKresponse with a JSON body containing theconsentedContent. - Payload Response Example:
{
"consentedContent": "CENSORED_PRIVATE_TOKEN_9999"
}
- Failure (Patched): The API returns a
403 Forbiddenor401 Unauthorizedif the post is private and the user is unauthenticated.
8. Verification Steps
- Identify ID: Run
wp post list --post_status=privateto get the ID of the post created in Step 6. - Execute Exploit: Use
http_requestto fetch the REST endpoint. - Compare: Verify the string returned in the JSON matches the
consentedContentstring defined in the WP-CLI setup. - Check Permissions: Verify that visiting the post URL directly (
/?p=ID) as an unauthenticated user results in a 404 or a login prompt, proving the post is indeed private and otherwise inaccessible.
9. Alternative Approaches
If the block_id used in the URL is not the blockId attribute but rather a numeric index or a different hash:
- Brute Force/Inference: Use the
browser_evaltool on the private post (while logged in as admin) to inspect the Gutenberg block attributes:wp.data.select('core/block-editor').getBlocks(). - Block ID discovery: Check if the plugin leaks block IDs in any other public endpoints or if they are predictable (e.g., based on post ID). However, for a PoC, the manually defined
blockIdin the Gutenberg comment is the standard target.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.