CVE-2026-27984

Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets <= 4.1.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.0
Patched in
44d
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.1.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.1.3
PublishedMarch 2, 2026
Last updatedApril 14, 2026
Affected pluginwidget-options

What Changed in the Fix

Changes introduced in v4.2.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps to exploit **CVE-2026-27984**, an Authenticated (Contributor+) Remote Code Execution vulnerability in the **Widget Options** plugin. ### 1. Vulnerability Summary The "Widget Options" plugin provides a "Display Logic" module that allows users to control the visi…

Show full research plan

This research plan outlines the steps to exploit CVE-2026-27984, an Authenticated (Contributor+) Remote Code Execution vulnerability in the Widget Options plugin.

1. Vulnerability Summary

The "Widget Options" plugin provides a "Display Logic" module that allows users to control the visibility of Gutenberg blocks and widgets using PHP conditional tags. The plugin takes user-provided PHP code and executes it using eval(). While intended for administrators, the plugin fails to properly restrict access to this functionality in Gutenberg block settings, allowing any user with post-editing privileges (Contributor and above) to inject and execute arbitrary PHP code.

2. Attack Vector Analysis

  • Endpoint: WordPress REST API (/wp-json/wp/v2/posts) or the Plugin's AJAX handler (/wp-admin/admin-ajax.php).
  • Vulnerable Parameter: logic attribute within the widgetopts_visibility block attribute (or widgetopts_logic).
  • Authentication: Contributor-level session required.
  • Preconditions: The "Display Logic" module must be active. If not active, it can potentially be enabled by a Contributor via the vulnerable AJAX handler if capability checks are missing.

3. Code Flow

  1. Entry Point (Gutenberg): A Contributor edits a post and adds/modifies a block. The visibility settings are stored in the block's attributes (e.g., widgetopts_visibility).
  2. Persistence: When the post is saved, the attributes are stored in the post_content (block comments) or as post metadata.
  3. Execution Sink: When the post is rendered on the frontend (via render_block or the_content hooks), the plugin's visibility engine parses the block attributes.
  4. The Sink: The plugin calls a logic-checking function (referenced in includes/admin/settings/modules/logic.php as being "EVAL'd directly"). This function takes the string from the logic field and passes it to eval().

4. Nonce Acquisition Strategy

The exploit requires two nonces: one for the WordPress REST API and one for the plugin's custom AJAX actions.

  1. REST API Nonce: Required to update/create posts.
    • Variable: window.wpApiSettings.nonce
  2. Widget Options AJAX Nonce: Required for interacting with the plugin's settings (e.g., ensuring the Logic module is active).
    • Localization Key: widgetopts (from assets/js/settings.js)
    • Nonce Key: ajax_nonce

Action Plan:

  1. Navigate to the post editor: `browser_navigate("/wp-admin/post-new
Research Findings
Static analysis — not yet PoC-verified

Summary

The Widget Options plugin for WordPress is vulnerable to Remote Code Execution via its Display Logic feature, which uses the PHP eval() function to process visibility rules. While intended for administrators, the plugin allows users with Contributor-level access and above to define these rules for Gutenberg blocks, leading to arbitrary code execution when the blocks are rendered on the site.

Vulnerable Code

/* includes/admin/settings/modules/logic.php lines 48-50 */
<p>
    <?php _e( "<strong>Please note</strong> that the display logic you introduce is EVAL'd directly. Anyone who has access to edit widget appearance will have the right to add any code, including malicious and possibly destructive functions. There is an optional filter <code>widget_options_logic_override</code> which you can use to bypass the EVAL with your own code if needed.", 'widget-options' )?>
</p>

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/widget-options/4.1.3/assets/js/select2-settings.js /home/deploy/wp-safety.org/data/plugin-versions/widget-options/4.2.0/assets/js/select2-settings.js
--- /home/deploy/wp-safety.org/data/plugin-versions/widget-options/4.1.3/assets/js/select2-settings.js	2023-07-20 11:53:20.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/widget-options/4.2.0/assets/js/select2-settings.js	2026-03-12 16:37:26.000000000 +0000
@@ -4,7 +4,61 @@
 		_init: function()
 		{
 			$('select.widgetopts-select2').each(function() {
-				$(this).select2({
+				var $el = $(this);
+
+				// Use AJAX mode for snippet dropdown
+				if ($el.attr('name') === 'widgetopts_logic_snippet_id') {
+					$el.select2({
+						width: '100%',
+						allowClear: true,
+						placeholder: '— No Logic (Always Show) —',
+						minimumInputLength: 0,
+						ajax: {
+							url: ajaxurl,
+							type: 'POST',
+							dataType: 'json',
+							delay: 250,
+							data: function(params) {
+								return {
+									action: 'widgetopts_get_snippets_ajax',
+									search: params.term || ''
+								};
+							},
+							processResults: function(response) {
+								var results = [{id: '', text: '— No Logic (Always Show) —'}];
+								if (response.success && response.data && response.data.snippets) {
+									response.data.snippets.forEach(function(snippet) {
+										results.push({id: String(snippet.id), text: snippet.title, description: snippet.description || ''});
+									});
+								}
+								return { results: results };
+							},
+							cache: true
+						}
+					}).on('change', $.proxy(WidgetOptsSelect2._maybePreview, this.context));

Exploit Outline

1. Login to WordPress with Contributor-level credentials or higher. 2. Open the Gutenberg block editor (e.g., create a new post). 3. Add any block and modify its attributes via the REST API or editor interface to include visibility logic. Specifically, the 'widgetopts_visibility' block attribute (or the corresponding 'logic' field in the editor's block settings) should be populated with arbitrary PHP code (e.g., '<?php system("whoami"); ?>' or 'return true; eval("...");'). 4. Save or update the post. The malicious PHP code is now stored in the post content as part of the block's metadata. 5. View the post on the site's frontend or through the preview functionality. 6. During the rendering process, the plugin retrieves the 'logic' string from the block attributes and passes it to the PHP eval() function, executing the attacker's payload on the server.

Check if your site is affected.

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