CVE-2025-12934

Beaver Builder – WordPress Page Builder <= 2.9.4.1 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Post Update

highMissing Authorization
8.1
CVSS Score
8.1
CVSS Score
high
Severity
2.9.4.2
Patched in
1d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
None
Availability

Technical Details

Affected versions<=2.9.4.1
PublishedDecember 22, 2025
Last updatedDecember 23, 2025

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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 within classes/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_layout
    • post_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)

  1. Registration: The plugin registers the AJAX action: add_action( 'wp_ajax_duplicate_wpml_layout', array( 'FLBuilderAjax', 'duplicate_wpml_layout' ) );
  2. Entry Point: FLBuilderAjax::duplicate_wpml_layout() is called.
  3. Missing Check: The function likely validates the nonce but fails to perform a current_user_can( 'edit_post', $new_id ) check.
  4. Data Processing: The function retrieves metadata (prefixed with _fl_builder_) from post_id.
  5. Sink: It uses update_post_meta() or FLBuilderModel::duplicate_post() to apply that metadata to new_id.

4. Nonce Acquisition Strategy

Beaver Builder typically localizes nonces for its AJAX handlers.

  1. Requirement: The script fl-builder.js must be enqueued. This happens when the Beaver Builder UI is active or on pages where Beaver Builder content is rendered.
  2. Shortcode: Use the Beaver Builder "Saved Template" or "Global" shortcode if necessary, or simply navigate to a page created with Beaver Builder.
  3. Extraction:
    • The nonce is usually found in the FLBuilderConfig global JavaScript object.
    • Target Variable: FLBuilderConfig.nonce
  4. 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-urlencoded
  • Cookie: [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

  1. Target Plugin: Install Beaver Builder Lite version <= 2.9.4.1.
  2. Admin Setup:
    • Create a "Secret Post" (ID: S) with status private. Use Beaver Builder to add unique text: SECRET_DATA_CONTENT.
    • Create a "Public Post" (ID: P) with status publish. Use Beaver Builder to add placeholder text: OLD_PUBLIC_CONTENT.
  3. User Setup: Create a user attacker with the Subscriber role.
  4. 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_postmeta for post P should now contain _fl_builder_data and other layout meta matching post S.
  • Visual Confirmation: Navigating to the URL of Post P displays SECRET_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_layout only copies specific keys, test if it can be used to copy data from FL_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 under wp_ajax_nopriv_. The CVE specifically states "Subscriber+", so wp_ajax_ is the likely hook.
  • UI-based Acquisition: If FLBuilderConfig is not available, check wp_localize_script for FLBuilderAjax or FLBuilderFree prefixes in the page source.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/classes/class-fl-builder-ajax.php
+++ b/classes/class-fl-builder-ajax.php
@@ -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.