CVE-2026-2722

Stock Ticker <= 3.26.1 - Authenticated (Administrator+) Stored Cross-Site Scripting via Template

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

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
High
User Interaction
Required
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.26.1
PublishedMarch 6, 2026
Last updatedMarch 7, 2026
Affected pluginstock-ticker

What Changed in the Fix

Changes introduced in v3.26.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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 template or template_static fields within the stockticker_defaults option array.
  • Authentication Level: Administrator (specifically users with manage_options capability).
  • Preconditions:
    • The plugin must be active.
    • To demonstrate the security bypass, unfiltered_html should be disabled for the administrator (e.g., via define( 'DISALLOW_UNFILTERED_HTML', true ); in wp-config.php), though the vulnerability exists regardless.
  • Payload Transport: The payload is sent via a POST request to wp-admin/options.php.

3. Code Flow

  1. Registration: Wpau_Stock_Ticker_Settings::register_settings() (in classes/class-wpau-stock-ticker-settings.php) registers the setting group wpau_stock_ticker and the option name stockticker_defaults (stored in $this->option_name).
  2. Storage: When an administrator saves the settings page (options-general.php?page=stock-ticker), the data is sent to options.php. The plugin does not define a sanitize_callback for the template field that would strip HTML or script tags.
  3. Retrieval: The plugin retrieves these settings in Wpau_Stock_Ticker::shortcode() or Wpau_Stock_Ticker::ajax_stockticker_load() (in classes/class-wpau-stock-ticker.php).
  4. Rendering (The Sink):
    • The plugin uses the stored template string.
    • 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 through esc_html() or wp_kses().

4. Nonce Acquisition Strategy

To update settings via options.php, a valid _wpnonce and the option_page identifier are required.

  1. Identify the Page: The settings page is located at wp-admin/options-general.php?page=stock-ticker.
  2. Navigation: Use browser_navigate to go to the settings page as an authenticated Administrator.
  3. Extraction: Use browser_eval to 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)

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-ticker
    • option_page: wpau_stock_ticker
    • action: update
    • stockticker_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.

  1. Navigate to the page created in the "Test Data Setup" section.
  2. The plugin will call wp_ajax_stockticker_load.
  3. The response will contain the HTML with the injected <script> tag.
  4. The browser will execute the alert(document.domain).

6. Test Data Setup

  1. Configure Environment: Disable unfiltered HTML for admins to prove it's a plugin-level vulnerability.
    • wp config set DISALLOW_UNFILTERED_HTML true --raw
  2. 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"]'
  3. 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.php request returns a 302 Redirect back to the settings page with settings-updated=true.
  • Successful Storage: Running wp option get stockticker_defaults shows the template key containing the <script> tag.
  • Successful Execution: When visiting the "Stock View" page, a JavaScript alert appears.

8. Verification Steps

  1. Check DB State:
    wp option get stockticker_defaults --format=json
    Verify that the template field contains the raw payload.
  2. Check Frontend Output:
    Use http_request to fetch the frontend page and grep for the raw payload:
    grep "<script>alert(document.domain)</script>" response_body
  3. Check AJAX Response:
    Trigger the AJAX loader directly:
    POST /wp-admin/admin-ajax.php
    action=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 template parameter 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 of Wpau_Stock_Ticker::shortcode() in class-wpau-stock-ticker.php is needed to confirm if template is an accepted attribute).
Research Findings
Static analysis — not yet PoC-verified

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

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/stock-ticker/3.24.6/classes/class-wpau-stock-ticker.php /home/deploy/wp-safety.org/data/plugin-versions/stock-ticker/3.26.2/classes/class-wpau-stock-ticker.php
--- /home/deploy/wp-safety.org/data/plugin-versions/stock-ticker/3.24.6/classes/class-wpau-stock-ticker.php	2024-06-27 23:07:30.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/stock-ticker/3.26.2/classes/class-wpau-stock-ticker.php	2026-03-04 07:26:52.000000000 +0000
@@ -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()
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/stock-ticker/3.24.6/classes/class-wpau-stock-ticker-settings.php /home/deploy/wp-safety.org/data/plugin-versions/stock-ticker/3.26.2/classes/class-wpau-stock-ticker-settings.php
--- /home/deploy/wp-safety.org/data/plugin-versions/stock-ticker/3.24.6/classes/class-wpau-stock-ticker-settings.php	2024-06-27 23:07:30.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/stock-ticker/3.26.2/classes/class-wpau-stock-ticker-settings.php	2026-03-04 07:26:52.000000000 +0000
@@ -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.