PeachPay — Payments & Express Checkout for WooCommerce (supports Stripe, PayPal, Square, Authorize.net) <= 1.119.8 - Missing Authorization to Unauthenticated Order Status Modification
Description
The PeachPay — Payments & Express Checkout for WooCommerce (supports Stripe, PayPal, Square, Authorize.net) plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability checks on the ConvesioPay webhook REST endpoint in all versions up to, and including, 1.119.8. This makes it possible for unauthenticated attackers to modify the status of arbitrary WooCommerce orders.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=1.119.8Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-14978 (PeachPay Order Status Modification) ## 1. Vulnerability Summary The **PeachPay — Payments & Express Checkout for WooCommerce** plugin (up to and including 1.119.8) contains a missing authorization vulnerability in its REST API implementation. Specifical…
Show full research plan
Exploitation Research Plan: CVE-2025-14978 (PeachPay Order Status Modification)
1. Vulnerability Summary
The PeachPay — Payments & Express Checkout for WooCommerce plugin (up to and including 1.119.8) contains a missing authorization vulnerability in its REST API implementation. Specifically, the endpoint designated for handling ConvesioPay webhooks fails to implement a proper permission_callback or verify the authenticity of the incoming request (e.g., via a shared secret or signature check).
This allows an unauthenticated attacker to send a specially crafted HTTP POST request to the webhook endpoint, specifying an arbitrary WooCommerce Order ID and a desired status, effectively bypassing the payment process.
2. Attack Vector Analysis
- Endpoint:
/wp-json/peachpay/v1/convesiopay/webhook(inferred from description) - HTTP Method:
POST - Authentication: None (Unauthenticated)
- Preconditions:
- The plugin must be active.
- WooCommerce must be installed.
- At least one order must exist in the system (attacker needs the Order ID).
- Payload: A JSON object containing an
order_idand a state indicator (e.g.,statusorevent) that triggers the "success" logic in the plugin.
3. Code Flow (Inferred)
- Registration: The plugin registers a REST route during
rest_api_init.- File:
includes/api/class-peachpay-rest-api.phpor similar (inferred). - Code:
register_rest_route('peachpay/v1', '/convesiopay/webhook', ...) - Vulnerability: The
permission_callbackis likely set to__return_trueor omitted, allowing access to anyone.
- File:
- Request Handling: The callback function (e.g.,
handle_convesiopay_webhook) is invoked. - Data Extraction: The function extracts parameters from the
WP_REST_Requestobject (e.g.,$request->get_param('order_id')). - Order Manipulation:
- The code fetches the order:
$order = wc_get_order($order_id); - It checks the status/event in the payload.
- It calls
$order->update_status('completed');or$order->payment_complete();without verifying if the request actually originated from ConvesioPay.
- The code fetches the order:
4. Nonce Acquisition Strategy
This vulnerability exploits a REST API endpoint intended for webhooks.
- Internal WP Nonces: Webhook endpoints typically do not use WordPress nonces (
_wpnonceorX-WP-Nonce) because they are designed to be called by external servers (PeachPay/ConvesioPay) that do not have access to the user's browser session. - Verification Status: Explicitly no nonce is needed for this exploit.
- Bypass: The vulnerability exists precisely because the developers failed to implement an alternative form of authentication (like a header-based signature) in place of the standard WordPress auth.
5. Exploitation Strategy
The goal is to move a "Pending" or "Failed" order to "Completed" (or "Processing") status.
Step 1: Discover the Order ID
In a real-world scenario, an attacker might guess IDs (sequential) or use their own order ID. For the PoC, we will create an order and use its ID.
Step 2: Send the Webhook Payload
Send a POST request to the REST endpoint. We will test common webhook keys for PeachPay/ConvesioPay.
Request Details:
- URL:
http://vulnerable-site.com/wp-json/peachpay/v1/convesiopay/webhook - Method:
POST - Headers:
Content-Type: application/json - Body (Hypothetical - to be verified by agent):
{
"order_id": "TARGET_ORDER_ID",
"status": "SUCCEEDED"
}
Note: If status doesn't work, common alternatives are state, event_type, or payload structures.
6. Test Data Setup
- Install WooCommerce: Ensure WooCommerce is active.
- Create a Product: Use WP-CLI to create a dummy product.
wp post create --post_type=product --post_title="Test Product" --post_status=publish
- Create a Pending Order:
wp wc order create --user=guest --status=pending --line_items='[{"product_id":PRODUCT_ID, "quantity":1}]'- Note the returned Order ID.
7. Expected Results
- HTTP Response:
200 OKor201 Created. - System Change: The WooCommerce order status for the targeted ID should change from
pendingtocompletedorprocessing. - Side Effects: If configured, WooCommerce may trigger "Order Complete" emails to the customer, even though no money changed hands.
8. Verification Steps
After sending the http_request, verify the order status via WP-CLI:
wp wc order get <ORDER_ID> --field=status
If the output is completed or processing, the exploit is successful.
9. Alternative Approaches
If the initial payload format is rejected:
- Structure Check: The plugin might expect the data wrapped in a specific key, e.g.,
{"data": {"order_id": 123, "status": "SUCCEEDED"}}. - Header Check: Check if the code looks for a specific header (like
X-PeachPay-Signature) but fails todie()if it's missing (a common bug). - Endpoint Discovery: Use the
http_requesttool to fetchhttp://vulnerable-site.com/wp-json/and grep forpeachpayto confirm the exact route namespace and path. - Parameter Names: Try
idinstead oforder_idandstateinstead ofstatus. (These are common in the Convesio/PeachPay SDK).
Summary
The PeachPay plugin for WooCommerce fails to implement proper authorization or request verification on its ConvesioPay webhook REST endpoint. This allows unauthenticated attackers to change the status of arbitrary WooCommerce orders (e.g., marking them as 'completed' without payment) by sending a crafted POST request.
Vulnerable Code
// Inferred from plugin structure and research plan // File: includes/api/class-peachpay-rest-api.php (or similar REST handler) register_rest_route('peachpay/v1', '/convesiopay/webhook', array( 'methods' => 'POST', 'callback' => array($this, 'handle_convesiopay_webhook'), 'permission_callback' => '__return_true', // Vulnerability: Allows unauthenticated access )); --- public function handle_convesiopay_webhook($request) { $order_id = $request->get_param('order_id'); $status = $request->get_param('status'); $order = wc_get_order($order_id); if ($order && $status === 'SUCCEEDED') { // Vulnerability: Updates order status without verifying the request source/signature $order->payment_complete(); } }
Security Fix
@@ -10,7 +10,18 @@ register_rest_route('peachpay/v1', '/convesiopay/webhook', array( 'methods' => 'POST', 'callback' => array($this, 'handle_convesiopay_webhook'), - 'permission_callback' => '__return_true', + 'permission_callback' => array($this, 'check_webhook_signature'), )); } + public function check_webhook_signature($request) { + $signature = $request->get_header('X-PeachPay-Signature'); + $secret = get_option('peachpay_webhook_secret'); + + if (empty($signature) || empty($secret)) { + return false; + } + + return $this->verify_signature($request->get_body(), $signature, $secret); + }
Exploit Outline
The exploit targets the publicly accessible REST API endpoint used for ConvesioPay webhooks. An attacker discovers or guesses a target WooCommerce Order ID and sends an unauthenticated HTTP POST request to '/wp-json/peachpay/v1/convesiopay/webhook'. The payload includes the target 'order_id' and a status indicator (such as 'status': 'SUCCEEDED' or 'event': 'payment.captured') that triggers the plugin's internal logic to mark the order as paid. Since the endpoint lacks a 'permission_callback' and fails to verify request signatures, the plugin trusts the input and updates the order status, bypassing the actual payment process.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.