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
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:NTechnical Details
What Changed in the Fix
Changes introduced in v3.1.32
Source Code
WordPress.org SVN# 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:
- Incomplete Blacklist: The
foogallery_sanitize_javascript()function (likely inincludes/functions.php) uses an incomplete blacklist of JavaScript event handlers. While it blocks common events likeonclickoronload, it permits others likeonmouseenter. - 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_keyandcustom_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
idfor the shortcode.
3. Code Flow
- Shortcode Parsing: When a post containing
[foogallery id="123" custom_attribute_key="..." ...]is viewed, the shortcode handler is triggered. - Setting Retrieval: The plugin calls
foogallery_gallery_template_setting()to retrieve settings. This function prioritizes shortcode attributes over stored gallery settings. - Attribute Processing: In
includes/class-gallery-advanced-settings.php, theadd_container_attributes()function is called via thefoogallery_build_container_attributesfilter.$custom_attribute_keyis fetched and passed throughsanitize_title().$custom_attribute_valueis fetched and passed throughsanitize_text_field().- Both are passed to
foogallery_sanitize_javascript()(the flawed blacklist). - The key and value are added to the
$attributesarray:$attributes[$custom_attribute_key] = $custom_attribute_value;
- HTML Generation: The
$attributesarray is passed tofoogallery_build_container_attributes_safe(). This function iterates
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
@@ -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.