WC Builder <= 1.2.0 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The WC Builder plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.2.0 due to insufficient input sanitization and output escaping. 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.2.0Source Code
WordPress.org SVNThis plan outlines the research and exploitation steps for **CVE-2025-68533**, a Stored Cross-Site Scripting (XSS) vulnerability in the **WC Builder – WooCommerce Page Builder for WPBakery** plugin (versions <= 1.2.0). --- ### 1. Vulnerability Summary The "WC Builder" plugin extends WPBakery Page …
Show full research plan
This plan outlines the research and exploitation steps for CVE-2025-68533, a Stored Cross-Site Scripting (XSS) vulnerability in the WC Builder – WooCommerce Page Builder for WPBakery plugin (versions <= 1.2.0).
1. Vulnerability Summary
The "WC Builder" plugin extends WPBakery Page Builder with WooCommerce-specific elements. The vulnerability exists because the plugin registers several shortcodes/WPBakery elements that process user-supplied attributes but fail to sanitize them before storage or escape them before output. Specifically, a user with Contributor permissions can create a post containing a malicious shortcode attribute, which will execute arbitrary JavaScript in the context of any user (including Administrators) who views the post.
2. Attack Vector Analysis
- Endpoint:
wp-admin/post.php(Standard WordPress post editing) orwp-admin/post-new.php. - HTTP Parameter:
content(Post content containing the malicious shortcode). - Authentication: Authenticated, Contributor-level access or higher.
- Preconditions:
- WPBakery Page Builder must be active.
- The "WC Builder" plugin (<= 1.2.0) must be active.
- The attacker must have permissions to create or edit posts (Contributor role is sufficient).
3. Code Flow (Inferred)
- Registration: The plugin uses
add_shortcode()to register WooCommerce elements (e.g., Product Grid, Product Slider). - Input: A Contributor saves a post. The shortcode and its attributes (e.g.,
title="<script>alert(1)</script>") are stored in thewp_poststable in thepost_contentcolumn. - Rendering: When a user views the post, the
do_shortcode()function is called. - Processing: The plugin's shortcode callback function (likely found in
includes/orelements/) retrieves the attributes usingshortcode_atts(). - Sink: The callback function concatenates the attribute into an HTML string (e.g., an
<h2>tag or aclassattribute) and returns it orechos it without usingesc_html(),esc_attr(), orwp_kses().
4. Nonce Acquisition Strategy
Since a Contributor is performing a standard post-update, they need the standard WordPress post nonce (_wpnonce).
- Login: Authenticate as a Contributor.
- Access Editor: Navigate to
wp-admin/post-new.php. - Extraction:
- Use
browser_navigateto load the page. - Use
browser_evalto extract the nonce from the hidden input field:browser_eval("document.querySelector('#_wpnonce')?.value") - Also, extract the
user_idandpost_ID(if it's an existing draft).
- Use
5. Exploitation Strategy
Step 1: Identify Vulnerable Shortcodes
Search the plugin directory for registered shortcodes to find the exact names and parameters:
grep -rn "add_shortcode" /var/www/html/wp-content/plugins/wc-builder/
Look for callbacks that handle text-based attributes like title, sub_title, extra_class, or label.
Step 2: Craft the Payload
Construct a shortcode payload. Assuming a shortcode named [wcb_product_grid] with a title attribute:[wcb_product_grid title='"><script>alert(document.domain)</script>']
Step 3: Inject via HTTP Request
Perform an http_request as the Contributor to save a new post.
- URL:
http://localhost:8080/wp-admin/post.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body Parameters:
action:editpostpost_ID:[NEW_POST_ID]_wpnonce:[EXTRACTED_NONCE]post_title:XSS Testcontent:[wcb_product_grid title='"><script>alert(1)</script>']post_status:publish(Note: Contributors can only set topending, but the XSS will still fire in the preview or when an Admin views the pending post).
Step 4: Trigger the XSS
Login as an Administrator and navigate to the newly created post (or its preview/edit page).
6. Test Data Setup
- Plugin Setup: Ensure
wc-builderv1.2.0 is installed and active. - User Setup: Create a user with the
contributorrole. - Target Post: As Contributor, initiate a new post to generate a
post_IDand the necessary nonce.
7. Expected Results
- The POST request to
post.phpshould return a302 Redirect. - When the post is viewed (frontend or via
wp-admin), the HTML source should reveal the payload reflected without escaping:<div class="wcb-grid-title">"><script>alert(1)</script></div> - The browser should execute the
alert(1)script.
8. Verification Steps
- Database Check: Use WP-CLI to verify the content is stored:
wp post get [POST_ID] --field=post_content - Frontend Verification: Navigate to the post URL as an unauthenticated user or Admin and check for the script execution in the DOM.
9. Alternative Approaches
- WPBakery AJAX: If the plugin uses a specific AJAX action for saving element data, use
grep -r "wp_ajax_"to find the handler. - Attribute Breakout: If the payload is reflected inside an HTML attribute (e.g.,
class="..."), use a breakout payload:class='x" onmouseover="alert(1)" data-x="' - Shortcode in Meta: Some builders store element data in Post Meta (e.g.,
_vc_post_settings). Check if the plugin saves attributes to meta and then renders them unescaped in the frontend template.
Summary
The WC Builder plugin for WordPress is vulnerable to Stored Cross-Site Scripting via WPBakery element shortcodes in versions up to 1.2.0. The plugin fails to sanitize or escape user-supplied attributes like 'title' or 'extra_class' before outputting them in HTML, allowing authenticated users with Contributor-level access to inject malicious JavaScript into pages.
Vulnerable Code
// Inferred from plugin shortcode registration and rendering logic // File path: includes/elements/product-grid.php public function render($atts, $content = null) { $atts = shortcode_atts(array( 'title' => '', 'extra_class' => '' ), $atts); $output = '<div class="wc-builder-element ' . $atts['extra_class'] . '">'; if ($atts['title']) { $output .= '<h3 class="title">' . $atts['title'] . '</h3>'; } // ... return $output; }
Security Fix
@@ -10,1 +10,1 @@ - $output = '<div class="wc-builder-element ' . $atts['extra_class'] . '">'; + $output = '<div class="wc-builder-element ' . esc_attr($atts['extra_class']) . '">'; @@ -12,1 +12,1 @@ - $output .= '<h3 class="title">' . $atts['title'] . '</h3>'; + $output .= '<h3 class="title">' . esc_html($atts['title']) . '</h3>';
Exploit Outline
The vulnerability is exploited by an authenticated Contributor who can create or edit posts. The attacker first captures a valid WordPress post nonce and a post ID. They then submit a POST request to the 'wp-admin/post.php' endpoint with the 'action' set to 'editpost'. In the 'content' field, the attacker includes a plugin-specific shortcode and crafts a malicious payload within its attributes, such as title='<script>alert(1)</script>'. When a site administrator views the post (either in the editor or on the frontend), the unsanitized attribute is rendered directly into the page, triggering the script execution.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.