CVE-2026-4379

LightPress Lightbox <= 2.3.4 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'group' Shortcode Attribute

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

Description

The LightPress Lightbox plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the `group` attribute in the `[gallery]` shortcode in all versions up to, and including, 2.3.4. This is due to the plugin modifying gallery shortcode output to include the `group` attribute value without proper escaping. 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<=2.3.4
PublishedApril 7, 2026
Last updatedApril 8, 2026
Affected pluginwp-jquery-lightbox

What Changed in the Fix

Changes introduced in v2.3.5

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the process for exploiting a Stored Cross-Site Scripting (XSS) vulnerability in the LightPress Lightbox plugin (versions <= 2.3.4). ## 1. Vulnerability Summary The **LightPress Lightbox** plugin is vulnerable to Stored XSS via the `group` attribute of the WordPress `[gal…

Show full research plan

This research plan outlines the process for exploiting a Stored Cross-Site Scripting (XSS) vulnerability in the LightPress Lightbox plugin (versions <= 2.3.4).

1. Vulnerability Summary

The LightPress Lightbox plugin is vulnerable to Stored XSS via the group attribute of the WordPress [gallery] shortcode. The plugin hooks into the post_gallery filter to capture the group attribute and subsequently modifies the HTML output (likely via the_content or by overriding the gallery output) to inject this value into the rel attribute of image links. Because the group value is concatenated into the HTML string without using esc_attr() or similar sanitization, an authenticated attacker with Contributor-level permissions can break out of the HTML attribute and inject arbitrary JavaScript.

2. Attack Vector Analysis

  • Target Endpoint: Post/Page creation/editor (/wp-admin/post-new.php or REST API /wp/v2/posts).
  • Vulnerable Component: The [gallery] shortcode processing logic.
  • Vulnerable Parameter: The group attribute within the shortcode.
  • Authentication Level: Contributor or higher (any role capable of using shortcodes in posts).
  • Preconditions: The plugin must be active. A post containing the malicious shortcode must be published or previewed.

3. Code Flow

  1. Entry Point: An authenticated user saves or previews a post containing: [gallery group='"><script>alert(1)</script>'].
  2. Shortcode Handling: When the post is rendered, WordPress triggers the post_gallery filter.
  3. Plugin Hook: In lightboxes/wp-jquery-lightbox/class-wp-jquery-lightbox.php, the constructor registers the filter:
    add_filter( 'post_gallery', array( $this, 'filter_groups' ), 10, 2 );
  4. State Storage: The filter_groups function (or jqlb_filter_groups in legacy mode) extracts the group attribute:
    // In wp-jquery-lightbox-legacy.php
    function jqlb_filter_groups($output, $attr) {
        global $jqlb_group;
        if(isset($attr['group'])){
            $jqlb_group = $attr['group']; // Raw input assigned to global
        }
        return $output;
    }
    
  5. Sink: The plugin later processes the content, likely in filter_content (registered at line 91 of class-wp-jquery-lightbox.php on the the_content hook). It searches for image links within the gallery and appends rel="lightbox[GROUP_VALUE]".
  6. XSS Trigger: Because GROUP_VALUE (the group attribute) is not escaped with esc_attr(), the payload "><script>alert(1)</script> closes the rel attribute and the <a> tag, allowing the script to execute.

4. Nonce Acquisition Strategy

This vulnerability does not require a plugin-specific nonce for the execution phase, as the XSS is stored and executes when the page is viewed. However, the injection phase (saving the post) requires standard WordPress authentication and nonces.

Strategy for Automated Agent:

  1. Use the wp_cli tool to create a post directly. This bypasses the need to manually extract CSRF nonces from the WordPress editor UI.
  2. Alternatively, if using the UI via browser_navigate:
    • The _wpnonce and _wp_http_referer are standard in the post.php form.
    • No specialized wp_localize_script nonce is needed because we are not targeting an AJAX endpoint, but rather the shortcode rendering engine.

5. Exploitation Strategy

The exploitation will involve creating a post as a Contributor and then verifying the XSS as an unauthenticated visitor.

  1. Login as Contributor: Authenticate as a user with the contributor role.
  2. Upload Media (Optional but Recommended): Ensure at least one image exists in the media library so the [gallery] shortcode renders actual <a> tags.
  3. Create Malicious Post: Use wp_cli to create a post containing the payload.
    • Payload: [gallery ids="1" group='"><script>alert(window.origin)</script>']
  4. View Post: Use the http_request tool to navigate to the frontend URL of the newly created post.
  5. Verify Payload Execution: Inspect the response body to confirm the presence of the unescaped script tag within the gallery HTML.

6. Test Data Setup

  • User: A user with the username contributor_user and role contributor.
  • Media: At least one attachment. We can use wp media import to add a placeholder image.
  • Shortcode: [gallery ids="ID_HERE" group='"><img src=x onerror=alert(document.domain)>']

7. Expected Results

  • The HTTP response for the post's frontend page should contain the string:
    rel="lightbox["><img src=x onerror=alert(document.domain)>]"
  • The browser (if rendered) would trigger the alert.
  • The raw HTML will show the rel attribute being prematurely closed by the "> characters in our payload.

8. Verification Steps

  1. Check Post Content:
    wp post get <POST_ID> --field=post_content
  2. Verify HTML Output:
    Use http_request to fetch the post and grep for the payload:
    grep -P 'rel="lightbox\["><script'
  3. Confirm Lack of Escaping:
    Verify that the " and > characters are NOT converted to &quot; and &gt;.

9. Alternative Approaches

  • Legacy Mode: If the plugin is configured to use the legacy file (JQLB_LEGACY constant set to true), target the jqlb_filter_groups function in lightboxes/wp-jquery-lightbox/wp-jquery-lightbox-legacy.php.
  • Attribute Variations: If group is not reflected in rel, check for it in data-lightbox-group or other custom data attributes the plugin might use to categorize images.
  • Comments XSS: The plugin also has a lightbox_comment filter (line 94 of class-wp-jquery-lightbox.php). If this filter uses the same lightbox_group logic, XSS might be possible via comment text if the plugin attempts to "lightbox" links within comments.
Research Findings
Static analysis — not yet PoC-verified

Summary

The LightPress Lightbox plugin is vulnerable to Stored Cross-Site Scripting via the 'group' attribute in the WordPress [gallery] shortcode. This occurs because the plugin fails to escape the group value before injecting it into the 'rel' attribute of image links, allowing attackers with Contributor-level permissions or higher to execute arbitrary JavaScript in the context of a user's browser.

Vulnerable Code

// File: lightboxes/wp-jquery-lightbox/class-wp-jquery-lightbox.php (around line 395)
		// Grouping.
		return str_replace( '<a', '<a rel="lightbox[' . $this->lightbox_group . ']"', $html );

---

// File: lightboxes/wp-jquery-lightbox/wp-jquery-lightbox-legacy.php (around line 329)
function jqlb_lightbox_gallery_links($html){ //honors our custom group-attribute of the gallery shortcode.
	global $jqlb_group;
	if(!isset($jqlb_group) || $jqlb_group == -1){return $html;}
    return str_replace('<a','<a rel="lightbox['.$jqlb_group.']"', $html);
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-jquery-lightbox/2.3.4/lightboxes/wp-jquery-lightbox/class-wp-jquery-lightbox.php /home/deploy/wp-safety.org/data/plugin-versions/wp-jquery-lightbox/2.3.5/lightboxes/wp-jquery-lightbox/class-wp-jquery-lightbox.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-jquery-lightbox/2.3.4/lightboxes/wp-jquery-lightbox/class-wp-jquery-lightbox.php	2024-10-20 23:54:22.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-jquery-lightbox/2.3.5/lightboxes/wp-jquery-lightbox/class-wp-jquery-lightbox.php	2026-03-25 23:14:32.000000000 +0000
@@ -392,7 +392,7 @@
 		}
 
 		// Grouping.
-		return str_replace( '<a', '<a rel="lightbox[' . $this->lightbox_group . ']"', $html );
+		return str_replace( '<a', '<a rel="lightbox[' . esc_attr( $this->lightbox_group ) . ']"', $html );
 	}
 
 	/**
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-jquery-lightbox/2.3.4/lightboxes/wp-jquery-lightbox/wp-jquery-lightbox-legacy.php /home/deploy/wp-safety.org/data/plugin-versions/wp-jquery-lightbox/2.3.5/lightboxes/wp-jquery-lightbox/wp-jquery-lightbox-legacy.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-jquery-lightbox/2.3.4/lightboxes/wp-jquery-lightbox/wp-jquery-lightbox-legacy.php	2024-06-19 16:37:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-jquery-lightbox/2.3.5/lightboxes/wp-jquery-lightbox/wp-jquery-lightbox-legacy.php	2026-03-25 23:14:32.000000000 +0000
@@ -326,7 +326,7 @@
 function jqlb_lightbox_gallery_links($html){ //honors our custom group-attribute of the gallery shortcode.
 	global $jqlb_group;
 	if(!isset($jqlb_group) || $jqlb_group == -1){return $html;}
-    return str_replace('<a','<a rel="lightbox['.$jqlb_group.']"', $html);
+    return str_replace('<a','<a rel="lightbox['.esc_attr($jqlb_group).']"', $html);
 }

Exploit Outline

1. Authenticate as a user with at least Contributor-level access (capable of creating and editing posts). 2. Create a new post or edit an existing one. 3. Insert a [gallery] shortcode using the 'group' attribute. The payload should use a closing quote and bracket to break out of the HTML attribute. 4. Payload example: `[gallery group='"><script>alert(document.domain)</script>']`. 5. Publish or preview the post. 6. When the gallery is rendered on the frontend, the plugin's `post_gallery` filter captures the group value and its content filter injects it directly into the `<a>` tag's `rel` attribute. The payload executes because the value is not passed through `esc_attr()`.

Check if your site is affected.

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