CVE-2026-11358

Orbit Fox: Duplicate Page, Menu Icons, SVG Support, Cookie Notice, Custom Fonts & More <= 3.0.6 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'menu-item-icon' Parameter

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.4
CVSS Score
4.4
CVSS Score
medium
Severity
3.0.7
Patched in
1d
Time to patch

Description

The Orbit Fox: Duplicate Page, Menu Icons, SVG Support, Cookie Notice, Custom Fonts & More plugin for WordPress is vulnerable to Stored Cross-Site Scripting via admin settings in all versions up to, and including, 3.0.6 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level permissions and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. This only affects multi-site installations and installations where unfiltered_html has been disabled.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.0.6
PublishedJune 17, 2026
Last updatedJune 18, 2026
Affected pluginthemeisle-companion

What Changed in the Fix

Changes introduced in v3.0.7

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps required to demonstrate the Stored Cross-Site Scripting (XSS) vulnerability in the **Orbit Fox Companion** plugin (CVE-2026-11358). ## 1. Vulnerability Summary The Orbit Fox plugin is vulnerable to Stored XSS via the `menu-item-icon` parameter. The plugin provi…

Show full research plan

This research plan outlines the steps required to demonstrate the Stored Cross-Site Scripting (XSS) vulnerability in the Orbit Fox Companion plugin (CVE-2026-11358).

1. Vulnerability Summary

The Orbit Fox plugin is vulnerable to Stored XSS via the menu-item-icon parameter. The plugin provides a "Menu Icons" module that allows users to attach icons to navigation menu items. When a menu item is saved, the icon identifier is stored in the post meta. When the WordPress Navigation Menu editor (wp-admin/nav-menus.php) is loaded, the plugin uses a custom Walker class (Menu_Icons_OBFX_Walker) to render the menu item settings. This Walker fails to sanitize or escape the stored icon value before outputting it into a hidden HTML input field, allowing an attacker with menu-editing permissions (typically Administrator) to inject arbitrary scripts.

2. Attack Vector Analysis

  • Target Endpoint: wp-admin/nav-menus.php
  • Vulnerable Parameter: menu-item-icon[<MENU_ITEM_ID>]
  • Authentication Level: Authenticated Administrator (or any user with edit_theme_options capability).
  • Preconditions:
    1. The "Menu Icons" module must be active (active by default upon installation).
    2. The environment must have unfiltered_html disabled (standard for Multisite Administrators or if DISALLOW_UNFILTERED_HTML is true).

3. Code Flow

  1. Input (Storage): When a user saves a menu, WordPress triggers the wp_update_nav_menu_item hook. The plugin's Menu_Icons_OBFX_Module::save_fields (in obfx_modules/menu-icons/init.php) retrieves the icon value from $_POST['menu-item-icon'][$menu_item_db_id] and saves it to the database using update_post_meta($menu_item_db_id, 'obfx_menu_icon', $icon).
  2. Retrieval: When the Nav Menu editor is loaded, Menu_Icons_OBFX_Module::show_menu is called via the wp_setup_nav_menu_item filter. It retrieves the meta: $icon = get_post_meta( $menu->ID, 'obfx_menu_icon', true ); and assigns it to $menu->icon.
  3. Sink (Execution): The plugin registers Menu_Icons_OBFX_Walker for the menu editor. In obfx_modules/menu-icons/inc/class-menu-icons-obfx-walker.php, the start_el method renders the field:
    $icon    = isset( $item->icon ) ? $item->icon : '';
    $output .= sprintf( '<input type="hidden" name="menu-item-icon[%d]" id="menu-item-icon-%d" value="%s">', $item->ID, $item->ID, $icon );
    
    The $icon variable is injected into the value attribute using %s without any escaping (like esc_attr()), allowing an attacker to break out of the attribute and inject tags.

4. Nonce Acquisition Strategy

To update the menu, a valid WordPress nonce for the update-nav_menu action is required.

  1. Identify the Source: The nonce is generated by WordPress on the wp-admin/nav-menus.php page inside a hidden input field with the ID update-nav-menu-nonce.
  2. Extraction:
    • Use browser_navigate to go to wp-admin/nav-menus.php.
    • Use browser_eval to extract the nonce and the ID of an existing menu item:
      {
        nonce: document.querySelector('#update-nav-menu-nonce')?.value,
        menuId: document.querySelector('#nav-menu-meta-object-id')?.value,
        // Get the ID of the first menu item in the list
        firstMenuItemId: document.querySelector('.menu-item-settings')?.id?.split('-')?.pop()
      }
      

5. Exploitation Strategy

  1. Setup: Ensure at least one menu and one menu item exist.
  2. Payload: "><script>alert(document.domain)</script>
  3. Trigger Request: Send a POST request to wp-admin/nav-menus.php to save the menu item with the malicious icon value.
    • URL: http://<target>/wp-admin/nav-menus.php
    • Method: POST
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body Parameters:
      • action: update
      • nav-menu-data: (Standard menu structure string, can be captured from a legitimate save or left minimal)
      • update-nav-menu-nonce: <EXTRACTED_NONCE>
      • menu: <MENU_ID>
      • menu-item-icon[<ITEM_ID>]: "><script>alert(document.domain)</script>
  4. Execution: After the request completes, navigate back to wp-admin/nav-menus.php. The script will execute when the Walker renders the hidden input for that specific menu item.

6. Test Data Setup

  1. Create an administrator user.
  2. Activate the themeisle-companion plugin.
  3. Use wp-cli to ensure a menu exists:
    wp menu create "Test Menu"
    wp menu item add-custom "Test Menu" "Home" "http://localhost/"
    
  4. Verify the "Menu Icons" module is active in Orbit Fox settings (it is active_default = true in init.php).

7. Expected Results

  • The POST request should return a 302 Redirect back to nav-menus.php.
  • Upon viewing nav-menus.php, the HTML source for the specific menu item will contain:
    <input type="hidden" name="menu-item-icon[ID]" ... value=""><script>alert(document.domain)</script>">
  • An alert box showing the domain will appear in the browser.

8. Verification Steps

After performing the exploit via http_request, verify the payload is stored in the database:

# Replace <ITEM_ID> with the actual ID used
wp post meta get <ITEM_ID> obfx_menu_icon

Confirm the output exactly matches the payload: "><script>alert(document.domain)</script>.

9. Alternative Approaches

If the nav-menus.php POST structure is too complex, the payload can also be injected via the wp_ajax_obfx_update_module_options AJAX action if the specific module exposes the icon settings there. However, the nav-menus.php vector is the most direct path as it leverages the primary functionality of the Menu Icons module.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Orbit Fox plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'menu-item-icon' parameter in the Menu Icons module. This occurs because the plugin fails to sanitize or escape the icon identifier when rendering the navigation menu editor, allowing authenticated administrators to inject arbitrary scripts that execute when a user views the menu settings page.

Vulnerable Code

// obfx_modules/menu-icons/inc/class-menu-icons-obfx-walker.php line 22
public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
	parent::start_el( $output, $item, $depth, $args, $id );
	$icon    = isset( $item->icon ) ? $item->icon : '';
	$output .= sprintf( '<input type="hidden" name="menu-item-icon[%d]" id="menu-item-icon-%d" value="%s">', $item->ID, $item->ID, $icon );
}

---

// obfx_modules/menu-icons/init.php line 107
public function show_menu( $menu ) {
	$icon = get_post_meta( $menu->ID, 'obfx_menu_icon', true );
	if ( ! empty( $icon ) ) {
		$menu->icon = $icon;
		if ( ! is_admin() ) {
			// ...
			$menu->title = sprintf( '<i class="obfx-menu-icon %s %s"></i>%s', $prefix, $icon, $menu->title );
		}
	}
	return $menu;
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/themeisle-companion/3.0.6/obfx_modules/menu-icons/inc/class-menu-icons-obfx-walker.php /home/deploy/wp-safety.org/data/plugin-versions/themeisle-companion/3.0.7/obfx_modules/menu-icons/inc/class-menu-icons-obfx-walker.php
--- /home/deploy/wp-safety.org/data/plugin-versions/themeisle-companion/3.0.6/obfx_modules/menu-icons/inc/class-menu-icons-obfx-walker.php	2020-02-03 12:45:00.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/themeisle-companion/3.0.7/obfx_modules/menu-icons/inc/class-menu-icons-obfx-walker.php	2026-06-16 09:54:24.000000000 +0000
@@ -22,6 +22,6 @@
 	public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
 		parent::start_el( $output, $item, $depth, $args, $id );
 		$icon    = isset( $item->icon ) ? $item->icon : '';
-		$output .= sprintf( '<input type="hidden" name="menu-item-icon[%d]" id="menu-item-icon-%d" value="%s">', $item->ID, $item->ID, $icon );
+		$output .= sprintf( '<input type="hidden" name="menu-item-icon[%d]" id="menu-item-icon-%d" value="%s">', $item->ID, $item->ID, esc_attr( $icon ) );
 	}
 }
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/themeisle-companion/3.0.6/obfx_modules/menu-icons/init.php /home/deploy/wp-safety.org/data/plugin-versions/themeisle-companion/3.0.7/obfx_modules/menu-icons/init.php
--- /home/deploy/wp-safety.org/data/plugin-versions/themeisle-companion/3.0.6/obfx_modules/menu-icons/init.php	2025-08-14 08:53:54.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/themeisle-companion/3.0.7/obfx_modules/menu-icons/init.php	2026-06-16 09:54:24.000000000 +0000
@@ -115,7 +115,7 @@
 				$array       = explode( '-', $icon );
 				$prefix      = reset( $array );
 				$prefix      = apply_filters( 'obfx_menu_icons_icon_class', $prefix, $icon );
-				$menu->title = sprintf( '<i class="obfx-menu-icon %s %s"></i>%s', $prefix, $icon, $menu->title );
+				$menu->title = sprintf( '<i class="obfx-menu-icon %s %s"></i>%s', esc_attr( $prefix ), esc_attr( $icon ), $menu->title );
 			}
 		}
 		return $menu;

Exploit Outline

The exploit targets the navigation menu editor in the WordPress admin panel. An authenticated attacker with at least Administrator privileges (or permissions to manage menus) can inject a payload into the 'menu-item-icon' field. To execute this, the attacker first navigates to wp-admin/nav-menus.php to retrieve a valid 'update-nav-menu-nonce'. They then submit a POST request to update a specific menu item, setting the 'menu-item-icon[ITEM_ID]' parameter to a payload designed to break out of an HTML attribute (e.g., "><script>alert(1)</script>"). The payload is stored in the database. When the menu editor is subsequently reloaded, the vulnerable custom Walker (Menu_Icons_OBFX_Walker) renders the raw payload into the 'value' attribute of a hidden input field, triggering the script execution.

Check if your site is affected.

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