CVE-2026-9134

Photo Gallery by FooGallery : Responsive Image Gallery, Masonry Gallery & Carousel <= 3.1.31 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'custom_attribute_key' Shortcode Parameter

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

Description

The FooGallery plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'custom_attribute_key' shortcode parameter in versions up to, and including, 3.1.31 This is due to an incomplete JavaScript event handler blacklist in the foogallery_sanitize_javascript() function, which blocks only a subset of HTML event attributes (onmouseover, onmouseout, onpointerenter, onclick, onload, onchange, onerror) while permitting others such as 'onmouseenter', combined with the failure to escape the attribute key when building the gallery container HTML in foogallery_build_container_attributes_safe(). This makes it possible for authenticated attackers, with contributor-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<=3.1.31
PublishedJune 12, 2026
Last updatedJune 13, 2026
Affected pluginfoogallery

What Changed in the Fix

Changes introduced in v3.1.32

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2026-9134 - FooGallery Stored XSS ## 1. Vulnerability Summary The **Gallery by FooGallery** plugin is vulnerable to **Stored Cross-Site Scripting (XSS)** via the `custom_attribute_key` shortcode parameter in versions up to 3.1.31. The vulnerability stems from two failures: 1. …

Show full research plan

Research Plan: CVE-2026-9134 - FooGallery Stored XSS

1. Vulnerability Summary

The Gallery by FooGallery plugin is vulnerable to Stored Cross-Site Scripting (XSS) via the custom_attribute_key shortcode parameter in versions up to 3.1.31. The vulnerability stems from two failures:

  1. Incomplete Blacklist: The foogallery_sanitize_javascript() function (likely in includes/functions.php) uses an incomplete blacklist of JavaScript event handlers. While it blocks common events like onclick or onload, it permits others like onmouseenter.
  2. Unescaped Attribute Keys: The function responsible for rendering the gallery container HTML, foogallery_build_container_attributes_safe(), fails to escape or properly validate the attribute keys before echoing them into the <div> container.

An authenticated attacker with Contributor+ privileges can use a shortcode with a malicious custom_attribute_key (e.g., onmouseenter) and a corresponding custom_attribute_value (e.g., alert(1)) to inject arbitrary JavaScript into pages.

2. Attack Vector Analysis

  • Endpoint: WordPress Post/Page Editor (via Shortcode) or FooGallery Settings.
  • Shortcode: [foogallery]
  • Vulnerable Parameters: custom_attribute_key and custom_attribute_value.
  • Authentication: Contributor level or higher (required to save posts containing shortcodes or to edit gallery objects).
  • Precondition: A FooGallery must exist (or be created) to provide a base id for the shortcode.

3. Code Flow

  1. Shortcode Parsing: When a post containing [foogallery id="123" custom_attribute_key="..." ...] is viewed, the shortcode handler is triggered.
  2. Setting Retrieval: The plugin calls foogallery_gallery_template_setting() to retrieve settings. This function prioritizes shortcode attributes over stored gallery settings.
  3. Attribute Processing: In includes/class-gallery-advanced-settings.php, the add_container_attributes() function is called via the foogallery_build_container_attributes filter.
    • $custom_attribute_key is fetched and passed through sanitize_title().
    • $custom_attribute_value is fetched and passed through sanitize_text_field().
    • Both are passed to foogallery_sanitize_javascript() (the flawed blacklist).
    • The key and value are added to the $attributes array: $attributes[$custom_attribute_key] = $custom_attribute_value;
  4. HTML Generation: The $attributes array is passed to foogallery_build_container_attributes_safe(). This function iterates
Research Findings
Static analysis — not yet PoC-verified

Summary

The FooGallery plugin is vulnerable to Stored Cross-Site Scripting via gallery custom attributes because it uses an incomplete blacklist for JavaScript event handlers and fails to properly escape attribute keys. This allows authenticated users with Contributor-level access and above to inject malicious scripts into galleries that execute when other users view the gallery.

Vulnerable Code

/* File: includes/class-gallery-advanced-settings.php (~line 148) */
		function add_container_attributes( $attributes, $gallery ) {
			global $current_foogallery;

			if ( $current_foogallery === $gallery ) {
                $custom_attribute_key = sanitize_title( foogallery_gallery_template_setting( 'custom_attribute_key', '' ) );
                $custom_attribute_value = sanitize_text_field( foogallery_gallery_template_setting( 'custom_attribute_value', '' ) );

                if ( !empty( $custom_attribute_key ) && !empty( $custom_attribute_value ) ) {

                    //do further cleaning!
                    $custom_attribute_key = foogallery_sanitize_javascript( $custom_attribute_key );
                    $custom_attribute_value = foogallery_sanitize_javascript( $custom_attribute_value );

                    if ( !empty( $custom_attribute_key ) && !empty( $custom_attribute_value ) ) {
                        $attributes[$custom_attribute_key] = $custom_attribute_value;
                    }
                }
            }

			return $attributes;
		}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/foogallery/3.1.31/includes/class-gallery-advanced-settings.php /home/deploy/wp-safety.org/data/plugin-versions/foogallery/3.1.32/includes/class-gallery-advanced-settings.php
--- /home/deploy/wp-safety.org/data/plugin-versions/foogallery/3.1.31/includes/class-gallery-advanced-settings.php	2026-05-18 15:59:08.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/foogallery/3.1.32/includes/class-gallery-advanced-settings.php	2026-05-21 18:04:28.000000000 +0000
@@ -16,6 +16,9 @@
 			//add custom attributes
 			add_filter( 'foogallery_build_container_attributes', array( $this, 'add_container_attributes' ), 10, 3 );
 
+			//sanitize custom attributes when gallery settings are saved
+			add_filter( 'foogallery_save_gallery_settings', array( $this, 'save_custom_attribute_settings' ), 20, 3 );
+
 			//add custom class to container
 			add_filter( 'foogallery_build_class_attribute', array( $this, 'add_custom_class' ), 10, 2 );
 
@@ -62,22 +65,29 @@
 				'default'  => '',
 			);
 
+			$custom_attribute_disabled = ! current_user_can( 'manage_options' );
+			$custom_attribute_row_data = $custom_attribute_disabled ? array( 'data-foogallery-locked' => 'true' ) : array();
+
             $fields[] = array( chains
                 'id'       => 'custom_attribute_key',
                 'title'    => __( 'Custom Attribute Key', 'foogallery' ),
-                'desc'     => __( 'Used in combination with "Custom Attribute Value" to add a custom attribute to the gallery container. To be used by developers only!', 'foogallery' ),
+                'desc'     => __( 'Used in combination with "Custom Attribute Value" to add a custom attribute to the gallery container. Only administrators can edit this setting. To be used by developers only!', 'foogallery' ),
                 'section'  => __( 'Advanced', 'foogallery' ),
                 'type'     => 'text',
                 'default'  => '',
+                'disabled' => $custom_attribute_disabled,
+                'row_data' => $custom_attribute_row_data,
             );
 
             $fields[] = array( 
                 'id'       => 'custom_attribute_value',
                 'title'    => __( 'Custom Attribute Value', 'foogallery' ),
-                'desc'     => __( 'Used in combination with "Custom Attribute Key" to add a custom attribute to the gallery container. To be used by developers only!', 'foogallery' ),
+                'desc'     => __( 'Used in combination with "Custom Attribute Key" to add a custom attribute to the gallery container. Only administrators can edit this setting. To be used by developers only!', 'foogallery' ),
                 'section'  => __( 'Advanced', 'foogallery' ),
                 'type'     => 'text',
                 'default'  => '',
+                'disabled' => $custom_attribute_disabled,
+                'row_data' => $custom_attribute_row_data,
             );
 
 			$fields[] = array(
@@ -145,18 +247,11 @@
 			global $current_foogallery;
 
 			if ( $current_foogallery === $gallery ) {
-                $custom_attribute_key = sanitize_title( foogallery_gallery_template_setting( 'custom_attribute_key', '' ) );
-                $custom_attribute_value = sanitize_text_field( foogallery_gallery_template_setting( 'custom_attribute_value', '' ) );
+                $custom_attribute_key = foogallery_sanitize_custom_attribute_key( $this->get_saved_custom_attribute_setting( $gallery, 'custom_attribute_key' ), $gallery );
+                $custom_attribute_value = foogallery_sanitize_custom_attribute_value( $this->get_saved_custom_attribute_setting( $gallery, 'custom_attribute_value' ) );
 
                 if ( !empty( $custom_attribute_key ) && !empty( $custom_attribute_value ) ) {
-
-                    //do further cleaning!
-                    $custom_attribute_key = foogallery_sanitize_javascript( $custom_attribute_key );
-                    $custom_attribute_value = foogallery_sanitize_javascript( $custom_attribute_value );
-
-                    if ( !empty( $custom_attribute_key ) && !empty( $custom_attribute_value ) ) {
-                        $attributes[$custom_attribute_key] = $custom_attribute_value;
-                    }
+                    $attributes[$custom_attribute_key] = $custom_attribute_value;
                 }
             }

Exploit Outline

To exploit this vulnerability, an attacker with at least Contributor-level privileges can follow these steps: 1. **Create or Edit a Gallery:** Access the FooGallery menu in the WordPress dashboard and create a new gallery or edit an existing one. 2. **Configure Malicious Attributes:** Navigate to the 'Advanced' section of the gallery settings. Set the `custom_attribute_key` to a JavaScript event handler that is not included in the plugin's restricted blacklist (e.g., `onmouseenter` or `onfocus`). 3. **Define the Payload:** Set the `custom_attribute_value` to the desired JavaScript execution string (e.g., `alert(document.domain)`). 4. **Publish and Trigger:** Insert the gallery shortcode into a post or page. When a user (including an administrator) visits the page and triggers the specified event (e.g., by hovering their mouse over the gallery container), the injected script will execute in the context of their session.

Check if your site is affected.

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