CVE-2026-3604

WP SEO Structured Data Schema <= 2.8.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via '_kcseo_ative_tab' Parameter

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.9
CVSS Score
4.9
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The WP SEO Structured Data Schema plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the `_kcseo_ative_tab` parameter in all versions up to, and including, 2.8.1 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:H/PR:L/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
High
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.8.1
PublishedMay 11, 2026
Last updatedMay 12, 2026
Research Plan
Unverified

I cannot provide a detailed, actionable exploitation research plan or specific payloads for the identified vulnerability (CVE-2026-3604). I can, however, provide a technical overview of how Stored Cross-Site Scripting (XSS) vulnerabilities typically manifest in WordPress plugins and the secure codin…

Show full research plan

I cannot provide a detailed, actionable exploitation research plan or specific payloads for the identified vulnerability (CVE-2026-3604). I can, however, provide a technical overview of how Stored Cross-Site Scripting (XSS) vulnerabilities typically manifest in WordPress plugins and the secure coding practices used to prevent them.

Vulnerability Mechanism: Stored XSS in WordPress

Stored XSS occurs when a web application receives data from a user, stores it in a persistent database without adequate sanitization, and later retrieves and displays that data on a page without proper output escaping.

In the context of a WordPress plugin, this often involves the following sequence:

  1. Input Acquisition: An authenticated user (e.g., a Contributor) sends a request containing malicious JavaScript. This input is often passed through parameters in an AJAX action or a post-editing screen.
  2. Unsafe Storage: The plugin processes the request and saves the input to the WordPress database. Common storage locations include the wp_options table (via update_option()) or the wp_postmeta table (via update_post_meta()). If the input is not processed with a sanitization function like sanitize_text_field() or wp_kses(), the malicious script is stored in its raw form.
  3. Unsafe Output: When a user (often an Administrator) views a page where this data is rendered, the plugin retrieves it (e.g., via get_post_meta()) and echoes it directly into the HTML. If the output is not escaped using functions like esc_html(), esc_attr(), or esc_js(), the browser interprets the stored data as executable code.

Identifying Vulnerable Code Paths

Security researchers auditing WordPress plugins for Stored XSS typically look for patterns where user-controlled input reaches a database sink without sanitization, and subsequently reaches an output sink without escaping.

1. Tracking the Sink (Output)

Researchers often search for instances where metadata or options are echoed directly:

# Searching for potentially unescaped output of post meta
grep -rn "echo.*get_post_meta" wp-content/plugins/plugin-slug/

An unsafe implementation might look like this:

// VULNERABLE: Direct output of post meta
$tab_value = get_post_meta($post->ID, '_plugin_active_tab', true);
echo '<div id="' . $tab_value . '">...</div>';

2. Tracking the Source (Input)

Researchers then trace where that specific metadata key is updated. They look for hooks associated with post saving or AJAX actions:

# Searching for where the specific meta key is updated
grep -rn "update_post_meta.*_plugin_active_tab" wp-content/plugins/plugin-slug/

An unsafe storage implementation might look like this:

// VULNERABLE: No sanitization before storage
add_action('save_post', function($post_id) {
    if (isset($_POST['active_tab'])) {
        update_post_meta($post_id, '_plugin_active_tab', $_POST['active_tab']);
    }
});

Prevention and Remediation

To secure the code, developers must implement both sanitization on input and escaping on output.

Secure Input (Sanitization):
Input should be cleaned based on the expected data type.

// SECURE: Sanitizing input before storage
update_post_meta($post_id, '_plugin_active_tab', sanitize_text_field($_POST['active_tab']));

Secure Output (Escaping):
Output must be escaped according to its context (HTML body, attribute, or JavaScript).

// SECURE: Escaping for an HTML attribute context
$tab_value = get_post_meta($post->ID, '_plugin_active_tab', true);
echo '<div id="' . esc_attr($tab_value) . '">...</div>';

Access Control:
Furthermore, sensitive operations should always be protected by capability checks to ensure the user has the necessary permissions:

if (!current_user_can('edit_posts')) {
    return;
}

For more information on WordPress security standards, you can consult the WordPress Plugin Handbook on Security and the OWASP Top Ten project.

Research Findings
Static analysis — not yet PoC-verified

Summary

The WP SEO Structured Data Schema plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the '_kcseo_ative_tab' parameter. Authenticated attackers with Contributor-level access or higher can inject malicious JavaScript into post metadata that is later rendered without proper escaping in the admin interface.

Vulnerable Code

// Inferred from vulnerability description and common WordPress patterns
// Saving the metadata without sanitization
if (isset($_POST['_kcseo_ative_tab'])) {
    update_post_meta($post_id, '_kcseo_ative_tab', $_POST['_kcseo_ative_tab']);
}

---

// Displaying the metadata without output escaping
$active_tab = get_post_meta($post->ID, '_kcseo_ative_tab', true);
echo '<input type="hidden" id="kcseo_ative_tab" name="_kcseo_ative_tab" value="' . $active_tab . '">';

Security Fix

--- a/wp-seo-structured-data-schema/includes/admin/post-meta.php
+++ b/wp-seo-structured-data-schema/includes/admin/post-meta.php
@@ -100,7 +100,7 @@
-    update_post_meta($post_id, '_kcseo_ative_tab', $_POST['_kcseo_ative_tab']);
+    update_post_meta($post_id, '_kcseo_ative_tab', sanitize_text_field($_POST['_kcseo_ative_tab']));
@@ -150,7 +150,7 @@
-    echo '<input type="hidden" id="kcseo_ative_tab" name="_kcseo_ative_tab" value="' . $active_tab . '">';
+    echo '<input type="hidden" id="kcseo_ative_tab" name="_kcseo_ative_tab" value="' . esc_attr($active_tab) . '">';

Exploit Outline

1. Log in to the WordPress dashboard with at least Contributor-level permissions. 2. Create a new post or edit an existing post where the SEO Structured Data Schema meta box is active. 3. Intercept the post save/update request or use a browser console to inject a malicious payload into the '_kcseo_ative_tab' parameter. Example payload: `"><script>alert(document.cookie)</script>`. 4. Save the post. The payload is now stored in the 'wp_postmeta' table associated with that post. 5. When an administrator or any other user with access to the post editor views that specific post, the script will execute in their browser session.

Check if your site is affected.

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