CVE-2026-3589

WooCommerce < 10.5.3 - Cross-Site Request Forgery

mediumCross-Site Request Forgery (CSRF)
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
10.5.3
Patched in
10d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<10.5.3
PublishedMarch 10, 2026
Last updatedMarch 19, 2026
Affected pluginwoocommerce

What Changed in the Fix

Changes introduced in v10.5.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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 namespace wc/store/v1 is inferred from standard WooCommerce Store API structure and the file path src/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

  1. Entry Point: The REST API server receives a POST request to /wc/store/v1/batch.
  2. Registration: Automattic\WooCommerce\StoreApi\Routes\V1\Batch::get_args() defines the route. Note the permission_callback:
    'permission_callback' => '__return_true',
    
  3. Processing: The request is handled by Batch::get_response( WP_REST_Request $request ).
  4. 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', ... );
        }
    }
    
  5. Internal Dispatch: The requests are passed to the WordPress core batch server:
    $response = rest_get_server()->serve_batch_request_v1( $request );
    
  6. Sink: serve_batch_request_v1 dispatches 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 in get_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 wcSettings global object.
  • Extraction:
    1. browser_navigate("/cart")
    2. browser_eval("window.wcSettings?.nonce") or browser_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.

  1. Step 1: Setup Data: Create a simple product to add to the cart.
  2. Step 2: Identify Endpoint: Target http://localhost:8080/wp-json/wc/store/v1/batch.
  3. Step 3: Craft Payload: Create a JSON payload for the requests parameter.
    {
      "requests": [
        {
          "method": "POST",
          "path": "/wc/store/v1/cart/add-item",
          "body": {
            "id": 123,
            "quantity": 1
          }
        }
      ]
    }
    
  4. Step 4: Execute Request: Use http_request to 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 (like X-WC-Store-API-Nonce).
  5. Step 5: Verify: Check the administrator's cart to see if the product was added.

6. Test Data Setup

  1. Create Product:
    wp product create --name="Exploit Product" --regular_price="100" --status="publish" --porcelain
    
    Note the returned Product ID (e.g., 123).
  2. 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/batch returns a 200 OK or 207 Multi-Status.
  • The response body contains a successful response for the nested add-item request.
  • The administrator's cart now contains the "Exploit Product".
  • Most importantly: The action succeeded despite the absence of the X-WC-Store-API-Nonce header which is mandatory for direct POST /wc/store/v1/cart/add-item requests.

8. Verification Steps

  1. 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());"
    
  2. 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 a key (cart item key) which can be retrieved via a preceding GET /wc/store/v1/cart in 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.

Research Findings
Static analysis — not yet PoC-verified

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

--- /home/deploy/wp-safety.org/data/plugin-versions/woocommerce/10.5.2/src/StoreApi/Routes/V1/Batch.php	2025-03-03 17:28:12.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/woocommerce/10.5.3/src/StoreApi/Routes/V1/Batch.php	2026-03-02 18:33:46.000000000 +0000
@@ -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.