GNTT Post Title Ticker <= 1.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
Description
The GNTT Post Title Ticker plugin for WordPress is vulnerable to Stored Cross-Site Scripting in version 1.0 via the `title-ticker-slide`, `title-ticker-fade`, and `title-ticker-typing` shortcodes. This is due to insufficient input sanitization and output escaping on shortcode attributes (notably `border`, `width`, `height`, `header_background`, `header_text_color`, and `id`) within the `gntt_title_ticker_slide()`, `gntt_title_ticker_fade()`, and `gntt_title_ticker_typing()` functions. None of these attribute values are passed through `esc_attr()` or any other escaping function before being concatenated into HTML output. 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
<=1.0This research plan outlines the steps required to demonstrate the Stored Cross-Site Scripting (XSS) vulnerability in the **GNTT Post Title Ticker** plugin. ### 1. Vulnerability Summary * **ID:** CVE-2026-8701 * **Vulnerability Type:** Stored Cross-Site Scripting (XSS) * **Component:** GNTT Po…
Show full research plan
This research plan outlines the steps required to demonstrate the Stored Cross-Site Scripting (XSS) vulnerability in the GNTT Post Title Ticker plugin.
1. Vulnerability Summary
- ID: CVE-2026-8701
- Vulnerability Type: Stored Cross-Site Scripting (XSS)
- Component: GNTT Post Title Ticker (<= 1.0)
- Vulnerable Functions:
gntt_title_ticker_slide(),gntt_title_ticker_fade(), andgntt_title_ticker_typing(). - Vulnerable Attributes:
border,width,height,header_background,header_text_color, andid. - Cause: Shortcode attributes are concatenated directly into HTML output without being passed through escaping functions like
esc_attr()oresc_html(). This allows a user with Contributor-level privileges (who can use shortcodes) to inject malicious scripts into pages.
2. Attack Vector Analysis
- Entry Point: Shortcode injection within a Post or Page content.
- Shortcodes:
[title-ticker-slide],[title-ticker-fade], or[title-ticker-typing]. - Vulnerable Parameter: Attributes within the shortcode (e.g.,
id,width). - Authentication: Requires a user with at least
Contributorprivileges (capabilityedit_posts). - Preconditions: The plugin must be active.
3. Code Flow (Inferred from Description)
- Registration: The plugin registers shortcodes via
add_shortcode()(likely in the main plugin file or aninithook). - Trigger: A user with
edit_postscapability creates a post containing a shortcode like[title-ticker-slide width='"><script>alert(1)</script>']. - Parsing: When the post is viewed, WordPress calls the associated callback function, e.g.,
gntt_title_ticker_slide($atts). - Processing: Inside the callback, the plugin extracts attributes. It likely uses
shortcode_atts()to merge user input with defaults. - Sink: The code constructs an HTML string for output. Because it omits
esc_attr(), it produces:<div width=""><script>alert(1)</script>" ... > - Execution: The unescaped payload is rendered in the frontend, executing the script in the context of any user viewing the page.
4. Nonce Acquisition Strategy
This vulnerability does not occur via an AJAX or REST endpoint that requires a custom plugin nonce. Instead, it is exploited through the standard WordPress post-saving mechanism.
- Injection Phase: To inject the shortcode via the API or automated requests, the agent will need a standard WordPress REST API or
admin-ajax.phpnonce for post creation/editing. - Extraction: If the agent performs the injection via the browser (Playwright), the browser session handles the
_wpnonceautomatically. - Verification: If there is a need to check for localized scripts that might contain nonces (though not required for this specific XSS), use:
browser_eval("window.gntt_vars?.nonce")(inferred)
5. Exploitation Strategy
The goal is to inject a stored XSS payload as a Contributor and trigger it as an Administrator.
- Step 1: Authenticate as Contributor: Log into the WordPress dashboard using Contributor credentials.
- Step 2: Create Malicious Post: Use the
http_requesttool to create a new post containing the payload.- URL:
http://[target]/wp-json/wp/v2/posts(or viaadmin-ajax.phpautosave). - Method:
POST - Body:
{ "title": "XSS Test", "content": "[title-ticker-slide id='\"><script>alert(document.domain)</script>']", "status": "publish" } - Note: Contributors' posts usually go to "Pending Review" unless they have
publish_posts. However, the XSS will still fire for the Administrator who previews or reviews the post.
- URL:
- Step 3: Trigger XSS: Log in as an Administrator and navigate to the newly created post's URL.
- Step 4: Confirm Execution: Use
browser_evalto check if a specific global variable (set by the payload) exists or if an alert was triggered.
6. Test Data Setup
- Plugin Setup: Ensure
gntt-post-title-tickerversion 1.0 is installed and activated. - User Setup:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Content Setup: (Optional) A page where the shortcode will be displayed.
7. Expected Results
- When the page is viewed, the HTML source should show the
id(orwidth/height) attribute of the ticker container being closed prematurely by the">characters. - The
<script>tag will follow immediately in the DOM. - The browser will execute
alert(document.domain).
8. Verification Steps
After the exploitation attempt, verify the storage and lack of escaping:
- Verify DB Storage:
wp post list --post_type=post --field=post_content | grep "script" - Verify Frontend Output:
Usehttp_request(GET) on the post's permalink and check if the response body contains the raw, unescaped payload:# Look for the exact unescaped string grep 'id=""><script>alert(document.domain)</script>"'
9. Alternative Approaches
If the id attribute is partially sanitized, attempt exploitation through other identified vulnerable attributes:
- Style Injection via
header_background:[title-ticker-slide header_background='";}</style><script>alert(1)</script>'] - Width/Height Breakout:
[title-ticker-fade width='100px" onmouseover="alert(1)" data-x="'] - Alternative Shortcodes:
Repeat the process using[title-ticker-fade]or[title-ticker-typing]callbacks, as they share the same vulnerable pattern.
Summary
The GNTT Post Title Ticker plugin for WordPress is vulnerable to Stored Cross-Site Scripting via shortcode attributes in version 1.0. This occurs because the plugin does not sanitize or escape user-provided attributes like 'id', 'width', and 'height' before rendering them in HTML. Authenticated users with contributor-level access or higher can inject malicious scripts into posts, which execute when the post is viewed by other users.
Vulnerable Code
// gntt-post-title-ticker.php (Inferred) function gntt_title_ticker_slide($atts) { extract(shortcode_atts(array( 'id' => 'gntt-ticker', 'width' => '100%', 'height' => '50px', 'border' => '1px solid #ccc', 'header_background' => '#eee', 'header_text_color' => '#000' ), $atts)); $output = '<div id="' . $id . '" style="width:' . $width . '; height:' . $height . '; border:' . $border . ';">'; // ... concatenation continues using $header_background and $header_text_color return $output; } --- function gntt_title_ticker_fade($atts) { // Similar logic using unescaped attributes like 'id' and 'width' extract(shortcode_atts(array('id' => 'fade-ticker', 'width' => '100%'), $atts)); return '<div id="' . $id . '" style="width:' . $width . ';">...'; } --- function gntt_title_ticker_typing($atts) { // Similar logic using unescaped attributes like 'id' and 'width' extract(shortcode_atts(array('id' => 'typing-ticker', 'width' => '100%'), $atts)); return '<div id="' . $id . '" style="width:' . $width . ';">...'; }
Security Fix
@@ -10,7 +10,12 @@ - $output = '<div id="' . $id . '" style="width:' . $width . '; height:' . $height . '; border:' . $border . ';">'; + $output = '<div id="' . esc_attr($id) . '" style="width:' . esc_attr($width) . '; height:' . esc_attr($height) . '; border:' . esc_attr($border) . ';">'; @@ -25,1 +30,1 @@ - return '<div id="' . $id . '" style="width:' . $width . ';">...'; + return '<div id="' . esc_attr($id) . '" style="width:' . esc_attr($width) . ';">...'; @@ -40,1 +45,1 @@ - return '<div id="' . $id . '" style="width:' . $width . ';">...'; + return '<div id="' . esc_attr($id) . '" style="width:' . esc_attr($width) . ';">...';
Exploit Outline
The exploit is performed by an authenticated attacker with Contributor-level privileges or higher who has the ability to create or edit posts. 1. The attacker logs into the WordPress dashboard and creates a new post or page. 2. In the post editor, the attacker inserts one of the vulnerable shortcodes (e.g., [title-ticker-slide]) and provides a malicious payload for an attribute that will be rendered within an HTML tag. 3. A payload such as [title-ticker-slide id='"><script>alert(document.domain)</script>'] is used to break out of the HTML attribute context and inject a script tag. 4. The attacker saves the post (or submits it for review). 5. When an administrator or any other user views the post on the frontend or via the 'Preview' function, the unescaped shortcode attribute is rendered directly into the page source, causing the browser to execute the injected JavaScript.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.