Beaver Builder – WordPress Page Builder <= 2.9.4.1 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Post Update
Description
The Beaver Builder – WordPress Page Builder plugin for WordPress is vulnerable to unauthorized access and modification of data due to a missing capability check on the 'duplicate_wpml_layout' function in all versions up to, and including, 2.9.4.1. This makes it possible for authenticated attackers, with Subscriber-level access and above, to update arbitrary posts with the content of other existing posts, potentially exposing private and password-protected content and deleting any content that is not saved in revisions or backups. Posts must have been created with Beaver Builder to be copied or updated.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:NTechnical Details
<=2.9.4.1Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-12934 (Beaver Builder Arbitrary Post Update) This plan outlines the research steps to demonstrate the missing authorization vulnerability in Beaver Builder <= 2.9.4.1, which allows Subscriber-level users to overwrite post content. ## 1. Vulnerability Summary …
Show full research plan
Exploitation Research Plan: CVE-2025-12934 (Beaver Builder Arbitrary Post Update)
This plan outlines the research steps to demonstrate the missing authorization vulnerability in Beaver Builder <= 2.9.4.1, which allows Subscriber-level users to overwrite post content.
1. Vulnerability Summary
- Vulnerability: Missing Authorization (Insecure Direct Object Reference / IDOR).
- Vulnerable Function:
duplicate_wpml_layout(likely withinclasses/class-fl-builder-ajax.php). - Description: The function intended to support WPML (WordPress Multilingual Plugin) layout duplication fails to verify if the current user has the authority to edit the posts being manipulated. It accepts a source post ID and a destination post ID, then copies the Beaver Builder layout data from the source to the target.
- Impact: A Subscriber can overwrite a public post with the content of a private/password-protected post (Information Disclosure) or overwrite important pages with arbitrary content from other Beaver Builder posts (Data Integrity loss).
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
duplicate_wpml_layout(authenticated AJAX). - Required Authentication: Any authenticated user (Subscriber level and above).
- Payload Parameters:
action:duplicate_wpml_layoutpost_id: (Integer) The ID of the source post (the content to be copied).new_id: (Integer) The ID of the destination post (the post to be overwritten).nonce: (String) A valid Beaver Builder AJAX nonce.
3. Code Flow (Inferred from BB Architecture)
- Registration: The plugin registers the AJAX action:
add_action( 'wp_ajax_duplicate_wpml_layout', array( 'FLBuilderAjax', 'duplicate_wpml_layout' ) ); - Entry Point:
FLBuilderAjax::duplicate_wpml_layout()is called. - Missing Check: The function likely validates the nonce but fails to perform a
current_user_can( 'edit_post', $new_id )check. - Data Processing: The function retrieves metadata (prefixed with
_fl_builder_) frompost_id. - Sink: It uses
update_post_meta()orFLBuilderModel::duplicate_post()to apply that metadata tonew_id.
4. Nonce Acquisition Strategy
Beaver Builder typically localizes nonces for its AJAX handlers.
- Requirement: The script
fl-builder.jsmust be enqueued. This happens when the Beaver Builder UI is active or on pages where Beaver Builder content is rendered. - Shortcode: Use the Beaver Builder "Saved Template" or "Global" shortcode if necessary, or simply navigate to a page created with Beaver Builder.
- Extraction:
- The nonce is usually found in the
FLBuilderConfigglobal JavaScript object. - Target Variable:
FLBuilderConfig.nonce
- The nonce is usually found in the
- Method:
- Create a page as Admin and enable Beaver Builder on it.
- As the Subscriber user, view this page.
- Use
browser_eval("FLBuilderConfig.nonce")to retrieve the token.
5. Exploitation Strategy
Step 1: Discover Post IDs
The attacker identifies the post_id of a private post they want to read and a new_id of a public post they are allowed to view (but not edit).
Step 2: The Attack Request
Using the http_request tool, send the following POST request as the Subscriber:
URL: http://[target-ip]/wp-admin/admin-ajax.php
Method: POST
Headers:
Content-Type: application/x-www-form-urlencodedCookie: [Subscriber Cookies]
Body:
action=duplicate_wpml_layout&post_id=[SOURCE_ID]&new_id=[TARGET_ID]&nonce=[NONCE_VALUE]
Step 3: Trigger Content Rendering
The destination post (new_id) now contains the Beaver Builder metadata from the source post. Visit the URL of the destination post. WordPress and Beaver Builder will render the metadata of the destination post, which is now identical to the sensitive source post.
6. Test Data Setup
- Target Plugin: Install Beaver Builder Lite version <= 2.9.4.1.
- Admin Setup:
- Create a "Secret Post" (ID:
S) with statusprivate. Use Beaver Builder to add unique text:SECRET_DATA_CONTENT. - Create a "Public Post" (ID:
P) with statuspublish. Use Beaver Builder to add placeholder text:OLD_PUBLIC_CONTENT.
- Create a "Secret Post" (ID:
- User Setup: Create a user
attackerwith theSubscriberrole. - Nonce Environment: Ensure the Beaver Builder "Post Type" settings (under Settings > Beaver Builder) allow editing of Posts/Pages to ensure the script enqueues correctly.
7. Expected Results
- HTTP Response: The AJAX request should return a success status (often a JSON object
{"success": true}). - Behavior: The database table
wp_postmetafor postPshould now contain_fl_builder_dataand other layout meta matching postS. - Visual Confirmation: Navigating to the URL of Post
PdisplaysSECRET_DATA_CONTENT.
8. Verification Steps (WP-CLI)
Confirm the overwrite by comparing post metadata:
# Check source meta
wp post meta list [SOURCE_ID] --keys=_fl_builder_data
# Check target meta (should match source after exploit)
wp post meta list [TARGET_ID] --keys=_fl_builder_data
9. Alternative Approaches
- Direct Meta Manipulation: If
duplicate_wpml_layoutonly copies specific keys, test if it can be used to copy data fromFL_BUILDER_LAYOUT(global templates). - Nonce Check Bypass: Investigate if Beaver Builder allows
current_user_can( 'edit_posts' )generally, which Subscribers lack, or if the action is registered underwp_ajax_nopriv_. The CVE specifically states "Subscriber+", sowp_ajax_is the likely hook. - UI-based Acquisition: If
FLBuilderConfigis not available, checkwp_localize_scriptforFLBuilderAjaxorFLBuilderFreeprefixes in the page source.
Summary
The Beaver Builder plugin for WordPress (up to and including version 2.9.4.1) is vulnerable to an IDOR-based arbitrary post update due to a missing authorization check in the 'duplicate_wpml_layout' AJAX action. Authenticated attackers with Subscriber-level access can overwrite the content and layout of existing posts with data from other Beaver Builder-enabled posts, leading to the exposure of private or password-protected content and data integrity loss.
Vulnerable Code
// classes/class-fl-builder-ajax.php public static function duplicate_wpml_layout() { $post_id = (int) $_POST['post_id']; $new_id = (int) $_POST['new_id']; if ( ! wp_verify_nonce( $_POST['nonce'], 'fl_builder_ajax_nonce' ) ) { return; } // Vulnerability: Missing check to verify if the current user has permission to edit the destination post ($new_id) FLBuilderModel::duplicate_post( $post_id, $new_id ); }
Security Fix
@@ -100,6 +100,10 @@ if ( ! wp_verify_nonce( $_POST['nonce'], 'fl_builder_ajax_nonce' ) ) { return; } + + if ( ! current_user_can( 'edit_post', $new_id ) ) { + return; + } FLBuilderModel::duplicate_post( $post_id, $new_id );
Exploit Outline
The exploit involves an authenticated attacker with Subscriber-level permissions or higher targeting the 'duplicate_wpml_layout' AJAX endpoint. The attacker first extracts a valid Beaver Builder AJAX nonce from the 'FLBuilderConfig' JavaScript object, which is typically localized to pages where the builder or its templates are rendered. Using this nonce, the attacker sends a POST request to '/wp-admin/admin-ajax.php' with the action 'duplicate_wpml_layout'. The request includes a 'post_id' corresponding to a private or sensitive source post and a 'new_id' corresponding to a public post they wish to overwrite. Because the function fails to perform a capability check (e.g., current_user_can('edit_post', $new_id)), the plugin duplicates all Beaver Builder metadata from the source to the target, allowing the attacker to view private contents by visiting the target post's public URL.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.