CVE-2026-2381

WooCommerce Stripe Payment Gateway <= 10.7.0 - Missing Authorization to Unauthenticated Order Status Manipulation via 'order' Parameter

mediumMissing Authorization
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
10.8.0
Patched in
1d
Time to patch

Description

The WooCommerce Stripe Payment Gateway plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the `ajax_pay_for_order()` function in all versions up to, and including, 10.7.0 This is due to a missing order ownership or order_key verification when processing payment for an order via the `wc_stripe_pay_for_order` WC-AJAX endpoint. The function only validates a nonce (which is publicly available on any WooCommerce page where Express Checkout is enabled), but does not verify that the requesting user owns the target order and is allowed to modify it. This makes it possible for unauthenticated attackers to force any pending order into a failed status by providing a fake payment method, causing a payment exception that updates the order status to "failed" via sequential order ID enumeration.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=10.7.0
PublishedJune 15, 2026
Last updatedJune 16, 2026

What Changed in the Fix

Changes introduced in v10.8.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-2381 ## 1. Vulnerability Summary The **WooCommerce Stripe Payment Gateway** plugin (<= 10.7.0) contains a missing authorization vulnerability in its AJAX handler for order payments. Specifically, the function `ajax_pay_for_order()` (registered via the `wc_stri…

Show full research plan

Exploitation Research Plan: CVE-2026-2381

1. Vulnerability Summary

The WooCommerce Stripe Payment Gateway plugin (<= 10.7.0) contains a missing authorization vulnerability in its AJAX handler for order payments. Specifically, the function ajax_pay_for_order() (registered via the wc_stripe_pay_for_order WC-AJAX endpoint) fails to verify order ownership or validate the order_key.

While the function checks a WordPress nonce, this nonce is exposed to unauthenticated users on any page where Stripe Express Checkout is enabled. Consequently, an unauthenticated attacker can supply an arbitrary Order ID and a malformed payment method. This triggers a payment exception within the plugin's logic, which the plugin handles by automatically updating the status of the targeted order to "failed".

2. Attack Vector Analysis

  • Endpoint: [TARGET_URL]/?wc-ajax=wc_stripe_pay_for_order (The wc-ajax query parameter triggers the WooCommerce AJAX handler).
  • Action Identifier: wc_stripe_pay_for_order
  • Vulnerable Parameter: order (Used to specify the target Order ID).
  • Authentication: None required (Unauthenticated).
  • Preconditions:
    1. The targeted order must be in a "Pending Payment" status.
    2. The "Payment Request Buttons" (Express Checkout) must be enabled in Stripe settings to expose the required nonce.

3. Code Flow

  1. Entry Point: The request hits admin-ajax.php or index.php with ?wc-ajax=wc_stripe_pay_for_order.
  2. Hook Registration: The plugin registers the AJAX handler (typically in a class like WC_Stripe_Payment_Gateway_Ajax or similar):
    add_action( 'wc_ajax_wc_stripe_pay_for_order', array( $this, 'ajax_pay_for_order' ) );
  3. Handler Execution: ajax_pay_for_order() is called.
  4. Nonce Verification: The function calls check_ajax_referer( 'wc_stripe_pay_for_order', '_wpnonce' ).
  5. Missing Check (The Vulnerability): The function retrieves the order using wc_get_order( $_POST['order'] ). Crucially, it does not verify that the current user is the owner of the order or that a valid order_key (secret token) was provided.
  6. Payment Processing: The function attempts to process payment using the provided payment_method.
  7. Exception Handling: If the payment_method is invalid (e.g., pi_invalid), the Stripe API client throws an exception.
  8. Sink: The catch block in ajax_pay_for_order() catches the exception and calls $order->update_status( 'failed', ... ), effectively modifying the order data without authorization.

4. Nonce Acquisition Strategy

The nonce for the wc_stripe_pay_for_order action is localized in the wc_stripe_params object when the Stripe scripts are enqueued.

  1. Identify Trigger: The nonce is enqueued on product pages or the cart page when "Payment Request Buttons" are active.
  2. Navigation: Use browser_navigate to visit a public product page.
  3. Extraction: Execute JavaScript to retrieve the nonce from the global parameters object defined in assets/js/stripe.js.
    • JS Variable: wc_stripe_params
    • Action Key: Based on the script localization logic, look for the nonce associated with the payment flow.
    • Command: browser_eval("wc_stripe_params.nonce.pay_for_order") (Note: The exact key inside wc_stripe_params may be _ajax_nonce or nested under a nonce key; verify by inspecting the wc_stripe_params object).

5. Exploitation Strategy

The goal is to force a pending order into a "failed" status.

  1. Target Identification: Identify a target Order ID (e.g., via sequential enumeration starting from a known recent order).
  2. Request Construction:
    • Method: POST
    • URL: https://[TARGET]/index.php?wc-ajax=wc_stripe_pay_for_order
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body Parameters:
      • order: [TARGET_ORDER_ID]
      • payment_method: pi_12345_fake (Any string that causes a Stripe API failure).
      • _wpnonce: [EXTRACTED_NONCE]
  3. Execution: Send the request using http_request.
  4. Observation: The server will likely return a JSON response containing an error message related to the failed payment, which confirms the catch block was reached.

6. Test Data Setup

  1. Configure Plugin:
    • Enable WooCommerce and Stripe Gateway.
    • Enable "Payment Request Buttons" in the Stripe settings (Checkout -> Stripe -> Settings).
  2. Create Content:
    • Create a simple product.
  3. Create Target Order:
    • As a guest user, add the product to the cart and proceed to checkout.
    • Place an order using a "Manual" or "Cheque" method first, or simply leave the Stripe payment session unfinished to keep the order in pending status.
    • Record the Order ID (e.g., 123).

7. Expected Results

  • Response: The AJAX endpoint returns a JSON success or failure message (e.g., {"result":"failure","messages":"..."}).
  • State Change: The targeted order's status in the WooCommerce database changes from pending (Pending Payment) to failed.
  • Audit Trail: An order note is added stating that the payment failed, often including the fake payment ID or error message.

8. Verification Steps

  1. Check Status via CLI:
    wp wc order get [ORDER_ID] --field=status --user=[ADMIN_USER]
    • If the result is failed, the exploit was successful.
  2. Check Order Notes:
    wp wc order_note list [ORDER_ID] --user=[ADMIN_USER]
    • Look for notes indicating a payment failure triggered via AJAX.

9. Alternative Approaches

  • Order Enumeration: If the specific Order ID is unknown, the agent should attempt the request across a range of IDs (e.g., [LATEST_ID] - 10 to [LATEST_ID]).
  • Different Nonce Sources: If the product page does not yield the nonce, check the Cart or Checkout pages using browser_navigate.
  • Varying Payment Methods: If pi_fake is rejected by client-side validation (though this is a server-side check), try other invalid Stripe tokens like tok_chargeDeclined.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WooCommerce Stripe Payment Gateway plugin suffers from a missing authorization vulnerability in its `ajax_pay_for_order()` function. An unauthenticated attacker can manipulate the status of pending orders to 'failed' by providing an arbitrary order ID and a malformed payment method to the `wc_stripe_pay_for_order` AJAX endpoint.

Vulnerable Code

// includes/class-wc-stripe-ajax.php (approximate line 130)

public function ajax_pay_for_order() {
    check_ajax_referer( 'wc_stripe_pay_for_order', '_wpnonce' );

    try {
        $order_id = isset( $_POST['order'] ) ? absint( $_POST['order'] ) : 0;
        $order    = wc_get_order( $order_id );

        if ( ! $order ) {
            throw new Exception( __( 'Invalid order.', 'woocommerce-gateway-stripe' ) );
        }

        // ... processing payment method from $_POST['payment_method'] ...

    } catch ( Exception $e ) {
        // Vulnerability: The status is updated based on an invalid order ID / payment method without verifying ownership
        $order->update_status( 'failed', sprintf( __( 'Stripe payment failed: %s', 'woocommerce-gateway-stripe' ), $e->getMessage() ) );
        wp_send_json_error( array( 'message' => $e->getMessage() ) );
    }
}

Security Fix

--- includes/class-wc-stripe-ajax.php
+++ includes/class-wc-stripe-ajax.php
@@ -134,7 +134,9 @@
 		try {
 			$order_id = isset( $_POST['order'] ) ? absint( $_POST['order'] ) : 0;
 			$order    = wc_get_order( $order_id );
+			$order_key = isset( $_POST['order_key'] ) ? wc_clean( wp_unslash( $_POST['order_key'] ) ) : '';
 
-			if ( ! $order ) {
+			if ( ! $order || ! $order->key_is_valid( $order_key ) ) {
 				throw new Exception( __( 'Invalid order.', 'woocommerce-gateway-stripe' ) );
 			}

Exploit Outline

To exploit this vulnerability, an attacker first obtains a valid AJAX nonce for the `wc_stripe_pay_for_order` action, which is exposed in the `wc_stripe_params` JavaScript object on any product or cart page where Stripe Express Checkout is enabled. The attacker then sends a POST request to the `/?wc-ajax=wc_stripe_pay_for_order` endpoint. The payload must include the target `order` ID, the extracted `_wpnonce`, and an invalid or malformed `payment_method` string (e.g., 'pi_fake'). Because the server-side handler fails to verify if the requester owns the order or has a valid order key, it proceeds to process the payment. The invalid payment method triggers a Stripe API exception, and the plugin's error handler automatically updates the targeted order's status to 'failed', allowing for unauthenticated status manipulation via sequential order ID enumeration.

Check if your site is affected.

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