WooCommerce < 10.5.3 - Cross-Site Request Forgery
Description
The WooCommerce plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to 10.5.3 (exclusive). This is due to missing or incorrect nonce validation on a function. This makes it possible for unauthenticated attackers to perform an unauthorized action granted they can trick a site administrator into performing an action such as clicking on a link.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v10.5.3
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-3589 (WooCommerce CSRF) ## 1. Vulnerability Summary The WooCommerce plugin (versions < 10.5.3) is vulnerable to Cross-Site Request Forgery (CSRF). The vulnerability is located in the Store API's batch processing route. The `Automattic\WooCommerce\StoreApi\Rout…
Show full research plan
Exploitation Research Plan: CVE-2026-3589 (WooCommerce CSRF)
1. Vulnerability Summary
The WooCommerce plugin (versions < 10.5.3) is vulnerable to Cross-Site Request Forgery (CSRF). The vulnerability is located in the Store API's batch processing route. The Automattic\WooCommerce\StoreApi\Routes\V1\Batch class registers a /batch REST API endpoint that allows multiple Store API requests to be bundled into a single HTTP POST request.
The vulnerability exists because the /batch endpoint does not implement nonce validation on the "container" request, and its permission_callback is set to __return_true. While individual Store API routes (like adding items to a cart) typically require a wc_store_api nonce for state-changing operations, the batching mechanism executes these requests internally via rest_get_server()->serve_batch_request_v1(), effectively bypassing the nonce requirement if the container request is accepted. An attacker can trick an authenticated administrator into making a request to this endpoint to perform actions within the Store API context.
2. Attack Vector Analysis
- Endpoint:
POST /wp-json/wc/store/v1/batch(The namespacewc/store/v1is inferred from standard WooCommerce Store API structure and the file pathsrc/StoreApi/Routes/V1/Batch.php). - HTTP Parameter:
requests(an array of request objects). - Authentication: Unauthenticated (the endpoint is public via
__return_true), but the attack targets the session of an authenticated user (e.g., Administrator). - Preconditions: A victim (Administrator) must be logged in and tricked into visiting a malicious page that triggers a cross-origin POST request to the vulnerable endpoint.
3. Code Flow
- Entry Point: The REST API server receives a POST request to
/wc/store/v1/batch. - Registration:
Automattic\WooCommerce\StoreApi\Routes\V1\Batch::get_args()defines the route. Note thepermission_callback:'permission_callback' => '__return_true', - Processing: The request is handled by
Batch::get_response( WP_REST_Request $request ). - Validation: The code checks if all nested request paths contain
wc/store:foreach ( $request['requests'] as $args ) { if ( ! stristr( $args['path'], 'wc/store' ) ) { throw new RouteException( 'woocommerce_rest_invalid_path', ... ); } } - Internal Dispatch: The requests are passed to the WordPress core batch server:
$response = rest_get_server()->serve_batch_request_v1( $request ); - Sink:
serve_batch_request_v1dispatches the sub-requests (e.g.,POST /wc/store/v1/cart/add-item) internally. Because the container request passed its permission check and no specific nonce check was performed inget_response, the sub-requests are processed using the victim's session cookies.
4. Nonce Acquisition Strategy
The vulnerability is specifically that the /batch endpoint does not verify a nonce. However, to verify the impact, one might need to know what a "valid" request looks like or how the Store API normally behaves.
If a nonce were required for other Store API endpoints, it is typically the wc_store_api nonce.
- Shortcode:
[woocommerce_cart]or the Cart Block. - Page Creation:
wp post create --post_type=page --post_status=publish --post_content='[woocommerce_cart]' --post_title='Cart' - JS Variable: In modern WooCommerce (v9.0+), Store API nonces are often found in the
wcSettingsglobal object. - Extraction:
browser_navigate("/cart")browser_eval("window.wcSettings?.nonce")orbrowser_eval("window.wc_cart_params?.nonce")
Note: For this specific exploit, the goal is to show that we can bypass this requirement via the /batch route.
5. Exploitation Strategy
We will perform a CSRF attack that adds an item to the Administrator's cart. This demonstrates a state-changing action performed without the required wc_store_api nonce.
- Step 1: Setup Data: Create a simple product to add to the cart.
- Step 2: Identify Endpoint: Target
http://localhost:8080/wp-json/wc/store/v1/batch. - Step 3: Craft Payload: Create a JSON payload for the
requestsparameter.{ "requests": [ { "method": "POST", "path": "/wc/store/v1/cart/add-item", "body": { "id": 123, "quantity": 1 } } ] } - Step 4: Execute Request: Use
http_requestto send a POST request. We must include the Administrator's cookies to simulate the CSRF scenario where the browser automatically attaches them, but omit any Store API nonce headers (likeX-WC-Store-API-Nonce). - Step 5: Verify: Check the administrator's cart to see if the product was added.
6. Test Data Setup
- Create Product:
Note the returned Product ID (e.g., 123).wp product create --name="Exploit Product" --regular_price="100" --status="publish" --porcelain - Create Cart Page (for verification):
wp post create --post_type=page --post_title="Cart" --post_content='[woocommerce_cart]' --post_status=publish
7. Expected Results
- The HTTP request to
/wc/store/v1/batchreturns a200 OKor207 Multi-Status. - The response body contains a successful response for the nested
add-itemrequest. - The administrator's cart now contains the "Exploit Product".
- Most importantly: The action succeeded despite the absence of the
X-WC-Store-API-Nonceheader which is mandatory for directPOST /wc/store/v1/cart/add-itemrequests.
8. Verification Steps
- Check Cart via CLI:
# WooCommerce stores cart data in the session or user meta. # A reliable way to check is to use the Store API via CLI or check the DB. wp eval "echo json_encode(WC()->cart->get_cart());" - Alternative Verification:
Navigate to the cart page as admin and check if the product is visible:browser_navigate("/cart"); browser_eval("document.body.innerText.includes('Exploit Product')");
9. Alternative Approaches
If cart/add-item is restricted, try other state-changing Store API routes:
POST /wc/store/v1/cart/update-item: Requires akey(cart item key) which can be retrieved via a precedingGET /wc/store/v1/cartin the same batch.POST /wc/store/v1/checkout: Attempting to process a checkout (higher impact).DELETE /wc/store/v1/cart/items: Clearing the cart.
If the wc/store/v1 namespace differs, check the WooCommerce versioning and route registrations in src/StoreApi/Routes/V1/. In v10.5.2, V1 is the current stable Store API version.
Summary
The WooCommerce Store API batch processing endpoint was vulnerable to Cross-Site Request Forgery (CSRF) due to a flawed path validation check using 'stristr'. An attacker could exploit this to bypass nonce requirements and execute arbitrary REST API actions by tricking an authenticated administrator into submitting a malicious batch request.
Vulnerable Code
// src/StoreApi/Routes/V1/Batch.php:57 public function get_args() { return array( 'callback' => [ $this, 'get_response' ], 'methods' => 'POST', 'permission_callback' => '__return_true', 'args' => array( --- // src/StoreApi/Routes/V1/Batch.php:117 public function get_response( WP_REST_Request $request ) { try { foreach ( $request['requests'] as $args ) { if ( ! stristr( $args['path'], 'wc/store' ) ) { throw new RouteException( 'woocommerce_rest_invalid_path', __( 'Invalid path provided.', 'woocommerce' ), 400 ); } } $response = rest_get_server()->serve_batch_request_v1( $request );
Security Fix
@@ -117,7 +117,8 @@ public function get_response( WP_REST_Request $request ) { try { foreach ( $request['requests'] as $args ) { - if ( ! stristr( $args['path'], 'wc/store' ) ) { + $parsed_path = wp_parse_url( $args['path'], PHP_URL_PATH ); + if ( ! $parsed_path || strpos( $parsed_path, '/wc/store' ) !== 0 ) { throw new RouteException( 'woocommerce_rest_invalid_path', __( 'Invalid path provided.', 'woocommerce' ), 400 ); } }
Exploit Outline
An attacker can perform a CSRF attack against an authenticated administrator by constructing a POST request to the `/wp-json/wc/store/v1/batch` endpoint. This endpoint's `permission_callback` is set to `__return_true`, and it does not perform its own nonce validation. The attacker provides a JSON payload containing a `requests` array where each request object specifies a target REST API path. To bypass the vulnerable `stristr` path check, the attacker appends the string `wc/store` as a query parameter (e.g., `/wp/v2/settings?wc/store`). When the victim's browser submits the request, the WordPress REST server dispatches the sub-requests internally via `serve_batch_request_v1`. These internal requests are processed using the victim's session cookies but bypass the standard REST API nonce checks typically required for state-changing administrative actions.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.