CVE-2025-14978

PeachPay — Payments & Express Checkout for WooCommerce (supports Stripe, PayPal, Square, Authorize.net) <= 1.119.8 - Missing Authorization to Unauthenticated Order Status Modification

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
1.119.9
Patched in
1d
Time to patch

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

Technical Details

Affected versions<=1.119.8
PublishedJanuary 19, 2026
Last updatedJanuary 20, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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_id and a state indicator (e.g., status or event) that triggers the "success" logic in the plugin.

3. Code Flow (Inferred)

  1. Registration: The plugin registers a REST route during rest_api_init.
    • File: includes/api/class-peachpay-rest-api.php or similar (inferred).
    • Code: register_rest_route('peachpay/v1', '/convesiopay/webhook', ...)
    • Vulnerability: The permission_callback is likely set to __return_true or omitted, allowing access to anyone.
  2. Request Handling: The callback function (e.g., handle_convesiopay_webhook) is invoked.
  3. Data Extraction: The function extracts parameters from the WP_REST_Request object (e.g., $request->get_param('order_id')).
  4. 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.

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 (_wpnonce or X-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

  1. Install WooCommerce: Ensure WooCommerce is active.
  2. Create a Product: Use WP-CLI to create a dummy product.
    • wp post create --post_type=product --post_title="Test Product" --post_status=publish
  3. 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 OK or 201 Created.
  • System Change: The WooCommerce order status for the targeted ID should change from pending to completed or processing.
  • 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:

  1. Structure Check: The plugin might expect the data wrapped in a specific key, e.g., {"data": {"order_id": 123, "status": "SUCCEEDED"}}.
  2. Header Check: Check if the code looks for a specific header (like X-PeachPay-Signature) but fails to die() if it's missing (a common bug).
  3. Endpoint Discovery: Use the http_request tool to fetch http://vulnerable-site.com/wp-json/ and grep for peachpay to confirm the exact route namespace and path.
  4. Parameter Names: Try id instead of order_id and state instead of status. (These are common in the Convesio/PeachPay SDK).
Research Findings
Static analysis — not yet PoC-verified

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

--- a/includes/api/class-peachpay-rest-api.php
+++ b/includes/api/class-peachpay-rest-api.php
@@ -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.