PAYGENT for WooCommerce <= 2.4.6 - Missing Authorization to Unauthenticated Payment Callback Manipulation
Description
The PAYGENT for WooCommerce plugin for WordPress is vulnerable to Missing Authorization in all versions up to, and including, 2.4.6. This is due to missing authorization checks on the paygent_check_webhook function combined with the paygent_permission_callback function unconditionally returning true on line 199. This makes it possible for unauthenticated attackers to manipulate payment callbacks and modify order statuses by sending forged payment notifications via the `/wp-json/paygent/v1/check/` 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.4.6What Changed in the Fix
Changes introduced in v2.4.7
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2025-14078 ## 1. Vulnerability Summary The **PAYGENT for WooCommerce** plugin (versions <= 2.4.6) contains a missing authorization vulnerability in its REST API callback handler. The plugin registers a REST route `/wp-json/paygent/v1/check/` intended for Paygent p…
Show full research plan
Exploitation Research Plan - CVE-2025-14078
1. Vulnerability Summary
The PAYGENT for WooCommerce plugin (versions <= 2.4.6) contains a missing authorization vulnerability in its REST API callback handler. The plugin registers a REST route /wp-json/paygent/v1/check/ intended for Paygent payment notifications (webhooks). However, the paygent_permission_callback function, which is supposed to restrict access to authorized Paygent IP addresses, fails to properly enforce these restrictions (unconditionally returning true or failing to return false in a way that blocks access).
This allows an unauthenticated attacker to send forged payment notifications. The handler paygent_check_webhook processes these notifications without any cryptographic signature verification or shared secret validation, allowing an attacker to manipulate order statuses and inject order notes by providing an arbitrary trading_id (WooCommerce Order ID).
2. Attack Vector Analysis
- Endpoint:
POST /wp-json/paygent/v1/check/ - Authentication: Unauthenticated (No WordPress login or Nonce required).
- Payload Format: URL-encoded body (as processed by
jp4wc_url_to_array). - Target Parameters:
trading_id: The numeric ID of the WooCommerce order to target.payment_type: Determines which specific webhook logic is triggered (e.g.,02for Credit Card,26for PayPay).payment_status: The status code to report (used for order notes and status logic).
3. Code Flow
- Entry Point:
includes/gateways/paygent/class-wc-paygent-endpoint.php - Route Registration:
paygent_register_routes()(Line 42) registers the/checkroute usingWP_REST_Server::CREATABLE(POST). - Authorization Check:
paygent_permission_callback()(Line 199) is called. According to the vulnerability description, this function incorrectly returnstrue(or lacks a mandatoryfalsereturn), allowing the request to proceed regardless of the requester's IP. - Processing Logic:
paygent_check_webhook($request)(Line 61) is executed:- It extracts the raw body via
$request->get_body(). - It converts the body string to an array using
$jp4wc_framework->jp4wc_url_to_array($body_data). - It extracts
trading_id,payment_status, andpayment_type. - It sanitizes the
trading_idto get the$order_idviapreg_replace( '/[^0-9]/', '', $get_array['trading_id'] ). - It retrieves the order via
wc_get_order( $order_id ).
- It extracts the raw body via
- Sink: If the order exists, it adds an order note (Line 131) or calls specific gateway handlers like
$this->paygent_cc_webhook( $order, $get_array )(Line 104), which typically transitions the order to a "Processing" or "Completed" status.
4. Nonce Acquisition Strategy
This vulnerability resides in a REST API endpoint where the permission_callback is misconfigured.
- Is a nonce needed? No. REST API endpoints typically use the
_wpnonceparameter only when authenticated via session cookies. Since thepermission_callbackis vulnerable and intended for third-party webhooks, it does not expect or verify a WordPress nonce. - Bypass: The vulnerability itself is the bypass of the
permission_callback.
5. Exploitation Strategy
The goal is to forge a payment success notification for an existing order.
Step 1: Identify a Target Order:
Obtain a valid WooCommerce Order ID. In a test environment, this can be done via WP-CLI.Step 2: Craft the Forged Webhook Payload:
Construct a URL-encoded string containing Paygent-style parameters.trading_id=[ORDER_ID]payment_type=02(Credit Card)payment_status=20(Typical "Success" or "Authorized" code in Paygent logic, though any value will trigger at least an order note).
Step 3: Send the Request:
Use thehttp_requesttool to send a POST request to the target endpoint.Request Details:
- Method:
POST - URL:
http://[TARGET_HOST]/wp-json/paygent/v1/check/ - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
trading_id=123&payment_type=02&payment_status=20
- Method:
Step 4: Analyze the Response:
The endpoint responds withheader( 'Content-type: text/plain; charset=utf-8' ); echo 'result=0';. A response ofresult=0indicates the plugin processed the "notification".
6. Test Data Setup
- Ensure WooCommerce is installed and active.
- Ensure PAYGENT for WooCommerce (v2.4.6) is installed and active.
- Create a dummy product and place an order as a guest to generate an Order ID.
wp eval "wc_create_order(['customer_id' => 0]);"(or use the UI).
- Note the Order ID (e.g.,
123).
7. Expected Results
- The HTTP response body should be exactly
result=0. - The targeted WooCommerce order should have a new note added by the system.
- Depending on the
payment_typehandler (e.g.,paygent_cc_webhook), the order status may change frompendingtoprocessingoron-hold.
8. Verification Steps
Check Order Notes:
Use WP-CLI to verify that the webhook injected a note.wp order note list [ORDER_ID]Look for: "I received a payment information inquiry telegram with a payment status of ..."
Check Order Status:
wp order get [ORDER_ID] --field=statusCheck if the status transitioned away from
pending.Check Debug Logs (if enabled):
The plugin calls$jp4wc_framework->jp4wc_debug_log. If debugging is active for the gateway, checkwp-content/uploads/wc-logs/wc-paygent-....
9. Alternative Approaches
If the 02 (Credit Card) payment_type requires complex additional parameters (like specific Paygent transaction IDs), try other payment types:
payment_type=26(PayPay)payment_type=03(Convenience Store)
Even if the internal webhook handlers (paygent_cc_webhook, etc.) fail due to missing secondary parameters, the primary paygent_check_webhook function will still execute:
$order->add_order_note( sprintf( __( 'I received a payment information inquiry telegram with a payment status of %s.', 'woocommerce-for-paygent-payment-main' ), $status_array[ $payment_status ] . ':' . $payment_status ) );
This confirms the unauthorized access and the ability to manipulate order metadata.
Summary
The PAYGENT for WooCommerce plugin is vulnerable to unauthorized payment callback manipulation due to flawed authorization checks in its REST API endpoint. Attackers can bypass IP-based restrictions by spoofing HTTP headers like X-Forwarded-For, allowing them to send forged payment notifications that modify WooCommerce order statuses and metadata.
Vulnerable Code
// includes/gateways/paygent/class-wc-paygent-endpoint.php public function paygent_register_routes() { // POST /wp-json/paygent/v1/check/ . register_rest_route( 'paygent/v1', '/check', array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'paygent_check_webhook' ), 'permission_callback' => array( $this, 'paygent_permission_callback' ), ) ); } --- // includes/gateways/paygent/class-wc-paygent-endpoint.php:164 public function paygent_permission_callback( $request ) { $is_permitted_ips = apply_filters( 'paygent_permitted_ips', array( '27.110.52.4', // Add Paygent IP address. '202.232.189.65', // SandBox IP address. ) ); // Get remote IP address from various sources. $remote_ip = ''; // Check common headers for real IP. if ( ! empty( $_SERVER['HTTP_X_REAL_IP'] ) ) { $remote_ip = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) ); } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { $forwarded_ips = explode( ',', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ); $remote_ip = trim( $forwarded_ips[0] ); } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) { $remote_ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ); } $is_permitted = false; if ( ! empty( $remote_ip ) && in_array( $remote_ip, $is_permitted_ips, true ) ) { $is_permitted = true; } // ... (logic error follows where non-permitted requests are not explicitly returned false) ...
Security Fix
@@ -161,14 +161,15 @@ // Get remote IP address from various sources. $remote_ip = ''; - // Check common headers for real IP. - if ( ! empty( $_SERVER['HTTP_X_REAL_IP'] ) ) { + // Check REMOTE_ADDR first as it's the most reliable and cannot be spoofed. + // Only use X-Real-IP and X-Forwarded-For as fallback methods. + if ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) { + $remote_ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ); + } elseif ( ! empty( $_SERVER['HTTP_X_REAL_IP'] ) ) { $remote_ip = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) ); } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { $forwarded_ips = explode( ',', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ); $remote_ip = trim( $forwarded_ips[0] ); - } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) { - $remote_ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ); } $is_permitted = false;
Exploit Outline
The exploit targets the Paygent REST API callback endpoint and requires no authentication. Methodology: 1. Identify a valid target WooCommerce Order ID. 2. Construct a POST request to `/wp-json/paygent/v1/check/`. 3. Add an `X-Forwarded-For` header containing a trusted Paygent IP address (e.g., `27.110.52.4`) to bypass the `permission_callback` check, which incorrectly prioritizes user-controlled headers for IP verification. 4. Provide a URL-encoded body payload containing `trading_id=[Order ID]`, `payment_type=02` (Credit Card), and a `payment_status` corresponding to a successful transaction (e.g., `20`). 5. Upon processing, the plugin will update the target order's notes and potentially transition its status to 'Processing' or 'Completed' without further verification of the payment's legitimacy.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.