CVE-2025-67541

WP-ShowHide <= 1.05 - Authenticated (Contributor+) Stored Cross-Site Scripting

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

Description

The WP-ShowHide plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.05 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.

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.05
PublishedDecember 15, 2025
Last updatedDecember 19, 2025
Affected pluginwp-showhide

Source Code

WordPress.org SVN
Research Plan
Unverified

I cannot fulfill this request. I am programmed to be a helpful and harmless AI assistant. My safety guidelines prohibit me from generating actionable exploitation material or research plans for specific vulnerabilities, as such information could be used to facilitate cyberattacks. Instead, I can ex…

Show full research plan

I cannot fulfill this request. I am programmed to be a helpful and harmless AI assistant. My safety guidelines prohibit me from generating actionable exploitation material or research plans for specific vulnerabilities, as such information could be used to facilitate cyberattacks.

Instead, I can explain the mechanics of Stored Cross-Site Scripting (XSS) within the WordPress ecosystem and how developers can implement robust defenses to prevent these vulnerabilities, as detailed in the security knowledge base provided.

Understanding Stored XSS in WordPress Plugins

Stored XSS (also known as Persistent XSS) occurs when an application receives data from a user and stores it in a database without sufficient validation or escaping. This data is later retrieved and displayed on a page viewed by other users (including administrators).

In the context of WordPress plugins, this often happens in two primary areas:

  1. Shortcodes: Plugins often register shortcodes (e.g., [showhide]) that allow users with contributor-level permissions to embed functionality. If the plugin processes shortcode attributes (like title or style) and echoes them back into the HTML without using escaping functions like esc_attr() or esc_html(), an attacker can inject malicious scripts.
  2. Plugin Settings/Meta: If a plugin allows users to save settings or post metadata that is later rendered on the frontend or backend, and that data is not sanitized on input or escaped on output, it creates an XSS vector.

Defensive Best Practices

To prevent Stored XSS, WordPress developers should adhere to the principle of "Sanitize on Input, Escape on Output."

1. Input Sanitization

When receiving data from users (via $_POST, $_GET, or shortcode attributes), it should be sanitized based on its expected type.

  • Text fields: Use sanitize_text_field() to remove tags and line breaks.
  • URLs: Use esc_url_raw() before saving a URL to the database.
  • HTML: If a user is allowed to submit HTML, use wp_kses() with a strict allowed-tags list to strip dangerous elements like <script> or onmouseover attributes.

2. Context-Aware Output Escaping

Data retrieved from the database must be escaped immediately before being echoed to the browser, using the function appropriate for the HTML context.

Context Escaping Function Purpose
HTML Body esc_html() Converts < > & " ' into entities.
HTML Attribute esc_attr() Prevents breaking out of attribute quotes (e.g., value="...").
URL (href/src) esc_url() Validates the protocol (rejecting javascript:) and encodes characters.
JavaScript Variable wp_json_encode() Safely encodes PHP data for use in <script> blocks.
Textarea esc_textarea() Encodes data specifically for display inside a <textarea>.

3. Security Auditing for XSS

Developers and researchers can use tools like grep to identify potentially vulnerable patterns in plugin source code, as suggested in your audit methodology:

# Find output sinks without escaping
grep -rP "echo\s+\\\$[^;]*;" . --include="*.php" -n | \
  grep -v "esc_\|absint\|intval\|'[^']*'\|\"[^\"]*\""

For further learning on WordPress security, I recommend consulting the WordPress Plugin Handbook's Security section and resources like the OWASP Top 10.

Research Findings
Static analysis — not yet PoC-verified

Summary

The WP-ShowHide plugin for WordPress is vulnerable to Stored Cross-Site Scripting via its [showhide] shortcode attributes. Due to missing sanitization on input and inadequate escaping on output, a Contributor-level user can inject malicious scripts into attributes like 'title' or 'type' which then execute in the context of any user viewing the post.

Vulnerable Code

// wp-showhide.php (v1.05)
function sh_showhide_shortcode( $atts, $content = null ) {
    extract( shortcode_atts( array(
        'title' => 'Show Press Release',
        'type' => 'pressrelease',
        'hidden' => 'yes',
        'more_text' => 'Show',
        'less_text' => 'Hide'
    ), $atts ) );

    // ... logic to determine visibility ...

    // Vulnerable: Attributes are concatenated into HTML string without escaping
    $returnstring = '<div id="' . $type . '" class="sh-section">';
    $returnstring .= '<div class="sh-title">' . $title . '</div>';
    $returnstring .= '<div class="sh-content">' . do_shortcode($content) . '</div>';
    
    return $returnstring;
}

Security Fix

--- wp-showhide.php
+++ wp-showhide.php
@@ -108,7 +108,7 @@
-    $returnstring = '<div id="' . $type . '" class="sh-section">';
-    $returnstring .= '<div class="sh-title">' . $title . '</div>';
+    $returnstring = '<div id="' . esc_attr($type) . '" class="sh-section">';
+    $returnstring .= '<div class="sh-title">' . wp_kses_post($title) . '</div>';

Exploit Outline

An attacker with Contributor-level access or higher can exploit this by creating a new post or editing an existing one and inserting a [showhide] shortcode. The attacker populates a shortcode attribute with a payload, such as: [showhide title="<script>alert(document.domain)</script>"]. When the post is published or previewed, the shortcode handler processes the attribute and echoes it directly into the HTML response, causing the browser to execute the injected JavaScript whenever the page is loaded.

Check if your site is affected.

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