CVE-2026-8900

Simple SEO Slideshow <= 1.2.8 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes

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

Description

The Simple SEO Slideshow plugin for WordPress is vulnerable to Stored Cross-Site Scripting via Shortcode Attributes in all versions up to, and including, 1.2.8 due to insufficient input sanitization and output escaping. 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. WordPress KSES does not strip malicious shortcode attribute values on post save, allowing contributor-level users to persist payloads that execute for any visitor, including administrators reviewing the post.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.2.8
PublishedJune 5, 2026
Last updatedJune 5, 2026
Affected pluginsimple-seo-slideshow

What Changed in the Fix

Changes introduced in v1.2.9

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-8900 (Simple SEO Slideshow Stored XSS) ## 1. Vulnerability Summary The **Simple SEO Slideshow** plugin (versions <= 1.2.8) is vulnerable to **Authenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the shortcode handler for `[simple…

Show full research plan

Exploitation Research Plan: CVE-2026-8900 (Simple SEO Slideshow Stored XSS)

1. Vulnerability Summary

The Simple SEO Slideshow plugin (versions <= 1.2.8) is vulnerable to Authenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because the shortcode handler for [simpleslideshow] fails to properly sanitize or escape user-supplied attributes (such as sctitle, scexclude, etc.) before rendering them into the page HTML. A user with Contributor level permissions can embed malicious JavaScript within these attributes. When the post is saved, WordPress KSES does not filter attributes within shortcodes, and the plugin later echoes these values directly into the DOM.

2. Attack Vector Analysis

  • Endpoint: WordPress Post Editor (/wp-admin/post.php or /wp-admin/post-new.php).
  • Vulnerable Component: The [simpleslideshow] shortcode handler in simpleslideshow.php.
  • Payload Carrier: Shortcode attributes, specifically sctitle.
  • Authentication: Authenticated (Contributor+).
  • Preconditions: The plugin must be active, and the attacker must have permission to create or edit posts (Contributor role is sufficient).

3. Code Flow

  1. Entry Point: An attacker creates a post containing a shortcode: [simpleslideshow sctitle='"><script>alert(1)</script>'].
  2. Persistence: Upon saving, WordPress core stores the raw shortcode string in the post_content column of the wp_posts table.
  3. Processing: When a user (including an Admin) views the post, WordPress invokes the shortcode callback function registered via add_shortcode( 'simpleslideshow', ... ) in simpleslideshow.php.
  4. The Sink: The handler extracts the attributes and generates HTML. Based on js/simpleslideshow.js, the plugin likely renders hidden elements or attributes to pass settings to the frontend script:
    • imgtitle[index][0] = jQuery('img',this).eq(0).attr('title');
    • imgsubtitle[index][0] = jQuery('.slidedescr',this).html();
  5. Execution: If the PHP handler outputs $atts['sctitle'] into a title attribute or inside a div with class slidedescr without using esc_attr() or esc_html(), the browser interprets the payload. The JS then further propagates the XSS by reading this content and re-inserting it via .html().

4. Nonce Acquisition Strategy

This exploit involves saving a standard WordPress post. It does not rely on a plugin-specific AJAX nonce, but rather the core WordPress post-editor nonces.

  • Logic: To save a post as a Contributor, the agent must obtain the _wpnonce used by the editor.
  • Procedure:
    1. Login to the WordPress dashboard as a Contributor.
    2. Use browser_navigate to go to http://[target]/wp-admin/post-new.php.
    3. Use browser_eval to extract the nonce required for the post.php submission:
      // Common location for the post nonce in the Gutenberg or Classic editor
      document.querySelector('#_wpnonce')?.value || document.querySelector('[name="_wpnonce"]')?.value;
      
    4. The agent will use this nonce in the subsequent POST request to save the post content.

5. Exploitation Strategy

  1. Preparation: Identify the Contributor credentials and the target WordPress instance.
  2. Authentication: Authenticate the session using the http_request tool.
  3. Nonce Extraction: Navigate to the "New Post" page and extract the _wpnonce and the user_id.
  4. Payload Injection: Submit a request to save a new post containing the malicious shortcode.
    • URL: http://[target]/wp-admin/post.php
    • Method: POST
    • Content-Type: application/x-www-form-urlencoded
    • Parameters:
      • action: editpost
      • post_ID: [NEW_POST_ID]
      • _wpnonce: [EXTRACTED_NONCE]
      • post_title: Security Research Slideshow
      • content: [simpleslideshow sctitle='"><script>alert(document.domain)</script>']
      • post_status: publish (or pending if Contributor cannot publish)
  5. Trigger: Use browser_navigate to visit the permalink of the newly created post.

6. Test Data Setup

  • User: A user with the contributor role (e.g., username: researcher, pass: password123).
  • Environment: WordPress instance with simple-seo-slideshow version 1.2.8 installed and activated.
  • Shortcode: The exploit utilizes the default shortcode [simpleslideshow].

7. Expected Results

  • The POST request to post.php should return a 302 Found redirecting to the post editor with a success message.
  • The post_content in the database should contain the unescaped payload.
  • Upon visiting the post frontend, an alert box displaying the document.domain should appear, confirming the execution of the injected script.

8. Verification Steps

  1. Database Check: Verify the payload is stored correctly using WP-CLI:
    wp post list --post_type=post --format=ids | xargs -I % wp post get % --field=post_content
    
  2. Frontend Inspection: Check the rendered HTML source for the presence of the unescaped script:
    # Look for the sctitle value in the div or image tags
    curl -s http://[target]/path-to-post | grep "alert(document.domain)"
    
  3. Execution Check: Confirm that the script executes in the browser context using the browser_eval tool to check for a global side effect (e.g., window.xss_executed = true).

9. Alternative Approaches

  • Attribute Breakout: If sctitle is correctly escaped within HTML tags but used inside an attribute (e.g., value='...'), try breaking out with ' onmouseover='alert(1).
  • Other Attributes: If sctitle is sanitized, test scexclude, scdelay, or scheight. Although js/mce_dialog.js uses parseInt on scdelay and scheight, the PHP side might still echo them raw before the JS loads.
  • JS-based Trigger: Since simpleslideshow.js uses jQuery(...).html(), test if the payload can be stored in a way that bypasses initial PHP rendering but is executed when the JS processes the slideshow captions:
    • Payload: [simpleslideshow sctitle='<img src=x onerror=alert(1)>']
Research Findings
Static analysis — not yet PoC-verified

Summary

The Simple SEO Slideshow plugin for WordPress (versions up to 1.2.8) is vulnerable to Stored Cross-Site Scripting via shortcode attributes such as 'sctitle'. Authenticated attackers with Contributor-level permissions can inject malicious scripts into these attributes, which are then rendered on the page without proper sanitization and subsequently executed by the plugin's JavaScript logic.

Vulnerable Code

/* js/simpleslideshow.js lines 35-36 (v1.2.6) */
imgtitle[index][0] = jQuery('img',this).eq(0).attr('title');
imgsubtitle[index][0] = jQuery('.slidedescr',this).html();

---

/* js/simpleslideshow.js line 83 (v1.2.6) */
// The plugin extracts content from the DOM and re-inserts it using .html(), triggering execution
jQuery('.slidedescr',newthis).html(imgsubtitle[index][newci]);

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/simple-seo-slideshow/1.2.6/readme.txt /home/deploy/wp-safety.org/data/plugin-versions/simple-seo-slideshow/1.2.9/readme.txt
--- /home/deploy/wp-safety.org/data/plugin-versions/simple-seo-slideshow/1.2.6/readme.txt	2013-08-03 22:47:56.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/simple-seo-slideshow/1.2.9/readme.txt	2026-05-28 01:16:58.000000000 +0000
@@ -1,18 +1,18 @@
-=== Plugin Name ===
+=== Plugin Name ===
 Contributors: spyrosvl
 Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9QBE26DTURXJQ
 Tags: slideshow widget, page slideshow widget, post slideshow widget
-Requires at least: 3.2
-Tested up to: 3.5.2
-Stable tag: 1.2.6
+Requires at least: 5.4
+Tested up to: 7.0
+Stable tag: 1.2.9
 License: GPLv2 or later
 License URI: http://www.gnu.org/licenses/gpl-2.0.html
 
 == Upgrade Notice ==
 
+= 1.2.9 =
+
+* Resolved security issue (CVE-2026-8900)
+* Added auto calculation of slider height
+
 = 1.2.6 =
 
 * New navigation buttons

Exploit Outline

1. Log in to the WordPress dashboard as an authenticated user with at least Contributor-level permissions. 2. Create a new post or edit an existing one using the Post Editor. 3. Insert the [simpleslideshow] shortcode with a malicious payload inside one of its attributes, for example: [simpleslideshow sctitle='<script>alert(document.domain)</script>']. 4. Save the post. Since WordPress does not filter attributes inside shortcode strings on post save, the payload is persisted in the database. 5. View the post on the frontend. The plugin's PHP handler renders the shortcode attribute directly into the HTML source. 6. The plugin's JavaScript (js/simpleslideshow.js) reads the content of the affected element and re-injects it into the DOM using the jQuery .html() method, which triggers the execution of the stored script.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.