CVE-2025-69390

Business Template Blocks for WPBakery (Visual Composer) Page Builder <= 1.3.2 - Reflected Cross-Site Scripting

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.1
CVSS Score
6.1
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Business Template Blocks for WPBakery (Visual Composer) Page Builder plugin for WordPress is vulnerable to Reflected Cross-Site Scripting in versions up to, and including, 1.3.2 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.3.2
PublishedFebruary 10, 2026
Last updatedFebruary 16, 2026
Research Plan
Unverified

This plan outlines the research and exploitation strategy for **CVE-2025-69390**, a reflected cross-site scripting vulnerability in the "Business Template Blocks for WPBakery" plugin. --- ### 1. Vulnerability Summary The "Business Template Blocks for WPBakery" plugin fails to properly sanitize or …

Show full research plan

This plan outlines the research and exploitation strategy for CVE-2025-69390, a reflected cross-site scripting vulnerability in the "Business Template Blocks for WPBakery" plugin.


1. Vulnerability Summary

The "Business Template Blocks for WPBakery" plugin fails to properly sanitize or escape user-controlled input before reflecting it back in the browser. This occurs in one or more code paths accessible to unauthenticated users, likely within an AJAX handler or a frontend initialization hook. The vulnerability allows an attacker to execute arbitrary JavaScript in the context of the victim's session.

2. Attack Vector Analysis

  • Endpoint: Likely wp-admin/admin-ajax.php (via wp_ajax_nopriv_ actions) or a frontend page (via init or wp_loaded hooks).
  • Parameter: A GET or POST parameter (likely id, template, style, or class).
  • Authentication: Unauthenticated (PR:N).
  • Preconditions: The plugin must be active. If the vulnerability is in an AJAX handler, a nonce might be required.

3. Code Flow (Inferred)

  1. Entry Point: An unauthenticated user sends an HTTP request with a malicious payload in a query parameter.
  2. Hook Registration: The plugin registers a handler via add_action('wp_ajax_nopriv_[ACTION_NAME]', ...) or add_action('init', ...).
  3. Vulnerable Handler: The handler retrieves the parameter from $_GET, $_POST, or $_REQUEST.
  4. Sink: The handler echoes the parameter directly using echo, print, or printf without applying esc_html(), esc_attr(), or wp_kses().
  5. Output: The response contains the unescaped script, which executes in the browser.

4. Nonce Acquisition Strategy

If the vulnerability resides in an AJAX handler that enforces a nonce check (e.g., check_ajax_referer or wp_verify_nonce), follow these steps:

  1. Identify Action and Script: Search the plugin code for wp_localize_script to find the JavaScript object name and nonce key.
    • Search Command: grep -r "wp_localize_script" /var/www/html/wp-content/plugins/templates-and-addons-for-wpbakery-page-builder/
  2. Locate Trigger Shortcode: Determine if the script is only loaded when a specific shortcode is present.
    • Search Command: grep -r "add_shortcode" /var/www/html/wp-content/plugins/templates-and-addons-for-wpbakery-page-builder/
  3. Create Dummy Page: If a shortcode is required, create a page:
    • wp post create --post_type=page --post_status=publish --post_title="Exploit Page" --post_content='[SHORTCODE_NAME]'
  4. Extract Nonce:
    • Navigate to the page using browser_navigate.
    • Execute browser_eval("window.LOCALIZED_OBJECT?.nonce_key") to retrieve the valid nonce.
  5. Note: If the plugin calls check_ajax_referer with die=false and fails to check the return value, the nonce can be omitted or be invalid.

5. Exploitation Strategy

The goal is to find the specific parameter that reflects input.

  1. Discovery Phase:
    • Search for all wp_ajax_nopriv_ actions: grep -r "wp_ajax_nopriv_" .
    • Search for echo statements involving superglobals: grep -rP "echo\s+\\\$_(GET|POST|REQUEST)" .
  2. Candidate Actions (Inferred):
    • btb_load_template
    • btb_preview
    • get_template_blocks
  3. Payload Construction:
    • Primary: <script>alert(document.domain)</script>
    • Attribute Breakout (if reflected in an attribute): "><script>alert(1)</script>
  4. Execution:
    • Use the http_request tool to send a GET/POST request to the identified endpoint.
    • Example Request:
      GET /wp-admin/admin-ajax.php?action=[ACTION]&param=<script>alert(1)</script>&_wpnonce=[NONCE] HTTP/1.1
      Host: localhost:8080
      

6. Test Data Setup

  1. Install Plugin: Ensure templates-and-addons-for-wpbakery-page-builder is installed and activated.
  2. WPBakery dependency: This plugin usually requires WPBakery Page Builder to be active for its blocks to function. Ensure it is installed.
  3. Content Creation: Create a post containing a Business Template Block to ensure all frontend scripts/styles are loaded if the vulnerability is on the frontend.

7. Expected Results

  • The HTTP response should have a Content-Type of text/html.
  • The response body must contain the exact string <script>alert(1)</script> (or the broken-out version) without HTML encoding (e.g., no &lt;).
  • In a real browser, an alert box would appear.

8. Verification Steps

  1. Manual Verification: Use http_request to fetch the URL and check if the payload exists in the raw response body.
  2. Automated Verification: Use browser_navigate to the malicious URL and check for the presence of the alert using Playwright's page.on('dialog', ...) or by checking for a side effect injected by the script (e.g., window.pwned = 1).

9. Alternative Approaches

If no unauthenticated AJAX handlers are found:

  • Search for init hooks: Check if the plugin processes $_GET parameters on every page load to display "messages" or "errors".
    • Search Command: grep -rn "add_action\s*(\s*['\"]init['\"]" .
  • Check for CSS/JS files: Occasionally, plugins have PHP files that act as dynamic CSS/JS (style.php?color=...) which reflect input without proper headers or escaping.
  • WPBakery Admin XSS: Check if a logged-in Subscriber can use a shortcode that reflects attributes into the admin dashboard (Stored XSS escalated from Reflected).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Business Template Blocks for WPBakery (Visual Composer) Page Builder plugin for WordPress is vulnerable to Reflected Cross-Site Scripting (XSS) in versions up to 1.3.2. This vulnerability stems from the plugin's failure to sanitize or escape user-controlled input before reflecting it back in an HTTP response, allowing unauthenticated attackers to execute arbitrary scripts in a user's browser.

Exploit Outline

1. Identify an endpoint that reflects user input, likely an unauthenticated AJAX action such as 'btb_load_template', 'btb_preview', or 'get_template_blocks' registered via wp_ajax_nopriv_ hooks. 2. Determine which GET or POST parameter (e.g., 'id', 'template', 'style', or 'class') is echoed directly into the page output. 3. Construct a malicious URL targeting wp-admin/admin-ajax.php with the 'action' set to the vulnerable handler and the targeted parameter containing a JavaScript payload (e.g., <script>alert(document.domain)</script>). 4. If the endpoint requires a security nonce, extract the valid nonce from the localized JavaScript objects on a page where the plugin is active (search for wp_localize_script output). 5. Distribute the crafted link to a victim; when clicked, the injected script executes within the context of the victim's browser session.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.