Stock Ticker <= 3.26.1 - Authenticated (Administrator+) Stored Cross-Site Scripting via Template
Description
The Stock Ticker plugin for WordPress is vulnerable to Stored Cross-Site Scripting via admin settings in all versions up to, and including, 3.26.1 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level permissions and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. This only affects multi-site installations and installations where unfiltered_html has been disabled.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v3.26.2
Source Code
WordPress.org SVN# CVE-2026-2722 Exploitation Research Plan - Stock Ticker Stored XSS ## 1. Vulnerability Summary The **Stock Ticker** plugin (<= 3.26.1) is vulnerable to **Stored Cross-Site Scripting (XSS)** via its administrative settings. The plugin allows administrators to define custom templates for stock data…
Show full research plan
CVE-2026-2722 Exploitation Research Plan - Stock Ticker Stored XSS
1. Vulnerability Summary
The Stock Ticker plugin (<= 3.26.1) is vulnerable to Stored Cross-Site Scripting (XSS) via its administrative settings. The plugin allows administrators to define custom templates for stock data display (macros like %company%, %price%, etc.). The input for these template settings is not properly sanitized during saving and is not escaped when rendered on the frontend. While administrators usually have unfiltered_html capabilities, this vulnerability is critical in Multi-site environments or configurations where DISALLOW_UNFILTERED_HTML is defined, as it allows an administrator to bypass these restrictions and execute arbitrary JavaScript in the context of any user (including Super Admins) viewing the ticker.
2. Attack Vector Analysis
- Vulnerable Endpoint:
wp-admin/options.php(The standard WordPress settings handler). - Vulnerable Setting: The
templateortemplate_staticfields within thestockticker_defaultsoption array. - Authentication Level: Administrator (specifically users with
manage_optionscapability). - Preconditions:
- The plugin must be active.
- To demonstrate the security bypass,
unfiltered_htmlshould be disabled for the administrator (e.g., viadefine( 'DISALLOW_UNFILTERED_HTML', true );inwp-config.php), though the vulnerability exists regardless.
- Payload Transport: The payload is sent via a
POSTrequest towp-admin/options.php.
3. Code Flow
- Registration:
Wpau_Stock_Ticker_Settings::register_settings()(inclasses/class-wpau-stock-ticker-settings.php) registers the setting groupwpau_stock_tickerand the option namestockticker_defaults(stored in$this->option_name). - Storage: When an administrator saves the settings page (
options-general.php?page=stock-ticker), the data is sent tooptions.php. The plugin does not define asanitize_callbackfor thetemplatefield that would strip HTML or script tags. - Retrieval: The plugin retrieves these settings in
Wpau_Stock_Ticker::shortcode()orWpau_Stock_Ticker::ajax_stockticker_load()(inclasses/class-wpau-stock-ticker.php). - Rendering (The Sink):
- The plugin uses the stored
templatestring. - It performs string replacements for macros (e.g.,
str_replace('%company%', $name, $template)). - The resulting string, containing the injected
<script>tag, is echoed to the page or returned in an AJAX response without passing throughesc_html()orwp_kses().
- The plugin uses the stored
4. Nonce Acquisition Strategy
To update settings via options.php, a valid _wpnonce and the option_page identifier are required.
- Identify the Page: The settings page is located at
wp-admin/options-general.php?page=stock-ticker. - Navigation: Use
browser_navigateto go to the settings page as an authenticated Administrator. - Extraction: Use
browser_evalto extract the required form values:- Nonce:
document.querySelector('input[name="_wpnonce"]').value - Option Page:
document.querySelector('input[name="option_page"]').value(expected:wpau_stock_ticker) - Action:
document.querySelector('input[name="action"]').value(expected:update)
- Nonce:
5. Exploitation Strategy
The goal is to inject a stored XSS payload into the "Template" setting and verify its execution on the frontend.
Step 1: Inject the Payload
Send a POST request to wp-admin/options.php with the administrator's cookies.
- URL:
http://<target>/wp-admin/options.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Parameters:
_wpnonce: (Extracted in Section 4)_wp_http_referer:/wp-admin/options-general.php?page=stock-tickeroption_page:wpau_stock_tickeraction:updatestockticker_defaults[template]:<script>alert(document.domain)</script>%company% %price% %change% %changep%stockticker_defaults[all_symbols]:AAPL(Ensuring a symbol exists to trigger rendering)stockticker_defaults[avapikey]:TEST_KEY
Step 2: Trigger the XSS
Visit a page where the stock ticker is rendered. The ticker typically loads its data via AJAX.
- Navigate to the page created in the "Test Data Setup" section.
- The plugin will call
wp_ajax_stockticker_load. - The response will contain the HTML with the injected
<script>tag. - The browser will execute the
alert(document.domain).
6. Test Data Setup
- Configure Environment: Disable unfiltered HTML for admins to prove it's a plugin-level vulnerability.
wp config set DISALLOW_UNFILTERED_HTML true --raw
- Create Trigger Page: Create a public post containing the shortcode.
wp post create --post_type=page --post_title="Stock View" --post_status=publish --post_content='[stock_ticker symbols="AAPL"]'
- Verify Plugin Setup: Ensure the plugin has a default symbol and API key (even if fake) so it attempts to render the template.
wp option update stockticker_defaults '{"all_symbols":"AAPL","avapikey":"test"}' --format=json
7. Expected Results
- Successful Injection: The
options.phprequest returns a302 Redirectback to the settings page withsettings-updated=true. - Successful Storage: Running
wp option get stockticker_defaultsshows thetemplatekey containing the<script>tag. - Successful Execution: When visiting the "Stock View" page, a JavaScript alert appears.
8. Verification Steps
- Check DB State:
wp option get stockticker_defaults --format=json
Verify that thetemplatefield contains the raw payload. - Check Frontend Output:
Usehttp_requestto fetch the frontend page and grep for the raw payload:grep "<script>alert(document.domain)</script>" response_body - Check AJAX Response:
Trigger the AJAX loader directly:POST /wp-admin/admin-ajax.phpaction=stockticker_load&symbols=AAPL
Verify the JSON response contains the unescaped script tag.
9. Alternative Approaches
- Static Template Injection: If the site uses the "static" mode for the ticker, the vulnerable parameter is
stockticker_defaults[template_static]. - Shortcode Attribute Bypass: Check if the
templateparameter can be passed directly via the shortcode (e.g.,[stock_ticker template="<script>..."]). If so, this would allow Contributor+ level users to exploit the vulnerability. (Review ofWpau_Stock_Ticker::shortcode()inclass-wpau-stock-ticker.phpis needed to confirm iftemplateis an accepted attribute).
Summary
The Stock Ticker plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'template' and 'template_static' admin settings. Authenticated administrators can inject arbitrary web scripts into these settings, which then execute on any page where the stock ticker is rendered, bypassing 'unfiltered_html' restrictions in Multi-site or hardened environments.
Vulnerable Code
// classes/class-wpau-stock-ticker-settings.php around line 809 case 'template': $value = strip_tags( $value, '<span><em><strong>' ); break; --- // classes/class-wpau-stock-ticker.php around line 862 // No results were returned? if ( empty( $q ) ) { return "{$out_start}{$out_error_msg}{$out_end}"; } // Print ticker content if we have it. return "{$out_start}{$q}{$out_end}";
Security Fix
@@ -861,11 +873,30 @@ // No results were returned? if ( empty( $q ) ) { - return "{$out_start}{$out_error_msg}{$out_end}"; + $html = "{$out_start}{$out_error_msg}{$out_end}"; + } else { + $html = "{$out_start}{$q}{$out_end}"; } - // Print ticker content if we have it. - return "{$out_start}{$q}{$out_end}"; + // Filter output content. + $html = wp_kses( + $html, + array( + 'ul' => array( + 'class' => true, + ), + 'li' => array( + 'class' => true, + ), + 'span' => array( + 'class' => true, + 'title' => true, + ), + ) + ); + + // Return safe content. + return $html; } // END public function stock_ticker() @@ -807,7 +830,15 @@ } break; case 'template': - $value = strip_tags( $value, '<span><em><strong>' ); + $allowed_html = array( + 'span' => array( + 'class' => array(), + 'style' => array(), + ), + 'em' => array(), + 'strong' => array(), + ); + $value = wp_kses( $value, $allowed_html ); break; case 'cache_timeout': $value = (int) $value;
Exploit Outline
1. Authenticate to the WordPress admin panel with Administrator privileges. 2. Navigate to the Stock Ticker settings page (wp-admin/options-general.php?page=stock-ticker). 3. Extract the `_wpnonce` and `option_page` field values from the settings form. 4. Send a POST request to `wp-admin/options.php` with the payload `<script>alert(document.domain)</script>` inside the `stockticker_defaults[template]` parameter. 5. Ensure a valid stock symbol (e.g., AAPL) is present in `stockticker_defaults[all_symbols]` to trigger rendering. 6. Visit any public page where the `[stock_ticker]` shortcode is used. The injected script will execute when the ticker data loads.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.