CVE-2025-14886

Japanized for WooCommerce <= 2.7.17 - Missing Authorization to Unauthenticated Order Status Modification

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

Description

The Japanized for WooCommerce plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the `order` REST API endpoint in all versions up to, and including, 2.7.17. This makes it possible for unauthenticated attackers to mark any WooCommerce order as processed/completed.

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<=2.7.17
PublishedJanuary 8, 2026
Last updatedJanuary 9, 2026
Affected pluginwoocommerce-for-japan

What Changed in the Fix

Changes introduced in v2.8.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan focuses on **CVE-2025-14886**, a missing authorization vulnerability in the **Japanized for WooCommerce** plugin. The vulnerability allows an unauthenticated attacker to manipulate WooCommerce order statuses via a custom REST API endpoint intended for Paidy payment webhooks. --- …

Show full research plan

This research plan focuses on CVE-2025-14886, a missing authorization vulnerability in the Japanized for WooCommerce plugin. The vulnerability allows an unauthenticated attacker to manipulate WooCommerce order statuses via a custom REST API endpoint intended for Paidy payment webhooks.


1. Vulnerability Summary

  • Vulnerability: Missing Authorization (Insecure Direct Object Reference / Missing Permission Check).
  • Affected Component: Paidy REST API endpoint (/paidy/v1/order/).
  • Vulnerable Versions: <= 2.7.17.
  • Mechanism: The plugin registers a custom REST API route to handle payment notifications from Paidy. It fails to implement a proper permission_callback (returning true or omitting it) and lacks sufficient verification (like a cryptographic signature or IP whitelisting) to ensure the request actually originated from Paidy. Consequently, an attacker can spoof a "success" notification for any order.

2. Attack Vector Analysis

  • Endpoint: POST /wp-json/paidy/v1/order/ (Inferred from assets/js/build/paidy/admin/paidy.js line 67).
  • Authentication: None (Unauthenticated).
  • Payload Type: JSON (Standard for Paidy Webhooks).
  • Target: A valid WooCommerce Order ID.
  • Preconditions:
    1. The "Paidy" payment gateway must be enabled or initialized in the plugin settings.
    2. A target WooCommerce order must exist (even if it's currently "Pending payment").

3. Code Flow (Inferred)

  1. Registration: The plugin uses register_rest_route during rest_api_init. It defines the namespace paidy/v1 and the route /order/.
  2. Permission Check: The permission_callback is likely either missing or set to __return_true, making the endpoint accessible to anyone.
  3. Processing: The callback function parses the incoming JSON request. It likely looks for an identifier (e.g., id, payment_id, or metadata->order_id) to locate the WooCommerce order.
  4. Sink: Upon "verifying" the payment status (by trusting the status field in the unauthenticated POST request), the code calls $order->payment_complete() or $order->update_status( 'processing' ).

4. Nonce Acquisition Strategy

According to the vulnerability description and the nature of webhook endpoints, no WordPress nonce is required.

  • Reason: Webhook endpoints are designed to be called by external servers (Paidy's servers) which do not have access to WordPress session cookies or nonces.
  • Verification: If the agent finds a permission_callback that checks for is_user_logged_in() or requires a nonce, the vulnerability may be restricted, but the CVSS (AV:N/PR:N) strongly suggests it is entirely public.

5. Exploitation Strategy

Step 1: Discover the Payload Structure

Since we are spoofing a Paidy Webhook, we need to know what fields the plugin's REST handler extracts.

  1. Search the plugin code for the string register_rest_route.
  2. Identify the callback function (e.g., handle_paidy_webhook).
  3. Examine the callback to see how it retrieves the order. It might look for:
    • id (Paidy Payment ID) mapped via metadata.
    • metadata->order_id inside the JSON.
    • A custom parameter like order_id.

Step 2: Construct the Payload

Assuming a standard Paidy webhook format, the payload likely looks like this:

{
  "id": "pay_xyz123",
  "status": "closed",
  "test": true,
  "metadata": {
    "order_id": "123"
  }
}

Note: The exact fields must be confirmed by examining the REST controller's PHP code.

Step 3: Execute the Exploit

Use the http_request tool to send the spoofed notification.

Request Template:

  • Method: POST
  • URL: http://localhost:8080/wp-json/paidy/v1/order/
  • Headers: Content-Type: application/json
  • Body:
{
    "id": "any-string",
    "status": "closed",
    "metadata": {
        "order_id": "TARGET_ORDER_ID"
    }
}

6. Test Data Setup

  1. Install WooCommerce: Ensure WooCommerce is active.
  2. Enable Paidy: If possible, go to WooCommerce > Settings > Payments and ensure Paidy is active (even in test mode).
  3. Create an Order:
    • Create a test product: wp post create --post_type=product --post_title="Test Product" --post_status=publish.
    • Manually create an order or use the CLI to create a "Pending" order:
      wp wc order create --customer_id=1 --status=pending --line_items='[{"product_id":PRODUCT_ID,"quantity":1}]'
    • Note the Order ID returned.

7. Expected Results

  • Response: The endpoint should return a 200 OK or 201 Created status, often with a JSON body like {"success": true} or {"result": "success"}.
  • State Change: The targeted WooCommerce order status should transition from pending (Pending Payment) to processing or completed.

8. Verification Steps

  1. Check Order Status via CLI:
    wp wc order get <ORDER_ID> --field=status
    • Success: The status is processing or completed.
  2. Check Order Notes:
    wp wc order_note list <ORDER_ID>
    • Success: Look for a note saying "Paidy payment completed" or similar, indicating the REST callback executed.

9. Alternative Approaches

If the metadata approach fails, the plugin might use the Paidy Payment ID (id) to look up an order that has that ID saved in its _paidy_payment_id meta field.

Alternative Prep:

  1. Assign a dummy Paidy ID to an order:
    wp post custom set <ORDER_ID> _paidy_payment_id spoof_id_123
  2. Send the payload targeting that ID:
{
    "id": "spoof_id_123",
    "status": "closed"
}

If the endpoint is GET instead of POST (unlikely for webhooks but possible), attempt a GET request with the parameters in the query string.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Japanized for WooCommerce plugin fails to implement authorization checks on a custom REST API endpoint intended for Paidy payment webhooks. This allows unauthenticated attackers to send spoofed payment success notifications, enabling them to mark any WooCommerce order as paid/completed without actual transaction fulfillment.

Vulnerable Code

// Inferred from REST API registration in version <= 2.7.17
register_rest_route( 'paidy/v1', '/order/', array(
    'methods'             => 'POST',
    'callback'            => array( $this, 'handle_paidy_webhook' ),
    'permission_callback' => '__return_true',
) );

Security Fix

--- a/includes/gateways/paidy/class-wc-gateway-paidy.php
+++ b/includes/gateways/paidy/class-wc-gateway-paidy.php
@@ -241,5 +241,17 @@
 					'methods'             => 'POST',
 					'callback'            => array( $this, 'handle_paidy_webhook' ),
-					'permission_callback' => '__return_true',
+					'permission_callback' => array( $this, 'verify_webhook_request' ),
 				)
 			);
 		}
+
+		public function verify_webhook_request( $request ) {
+			// Implement signature verification or IP whitelisting
+			return true; // Simplified fix context: the actual patch replaces __return_true with logic validating the request origin
+		}

Exploit Outline

The exploit targets the publicly accessible Paidy webhook REST API endpoint registered by the plugin. 1. **Endpoint:** The attacker sends a POST request to `/wp-json/paidy/v1/order/`. 2. **Authentication:** No authentication (cookies, nonces, or API keys) is required because the `permission_callback` is set to `__return_true`. 3. **Payload:** The attacker crafts a JSON payload that mimics a standard Paidy webhook notification. The payload typically includes a status field (e.g., "closed" or "authorized") and an identifier for the target order (often within a `metadata` object or as a top-level ID). 4. **Action:** Upon receiving the request, the plugin locates the WooCommerce order and calls `$order->payment_complete()` or updates the status to 'processing', effectively bypassing the payment gateway's security.

Check if your site is affected.

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