SECURITY ADVISORY / 01

CVE-2026-96564 Exploit & Vulnerability Analysis

Complete CVE-2026-96564 security advisory with proof of concept (PoC), exploit details, and patch analysis.

cve_patchdiff:wp-seopress NVD ↗
Exploit PoC Vulnerability Patch Analysis

The Exploit

An unauthenticated attacker with the ability to publish public singular content (such as a bbPress forum topic) can inject arbitrary JavaScript into analytics tracking code by poisoning their author display name. The SEOPress plugin must have the 'Track Authors' custom dimension enabled in Google Analytics 4 or Matomo settings.

POST /forum/new-topic HTTP/1.1
Host: target.wordpress.local
Content-Type: application/x-www-form-urlencoded

bbp_forum_id=1&bbp_topic_title=Legit+Question&bbp_topic_content=Hello&user_login=attacker&[email protected]&user_url=&pass1=Password123&pass2=Password123&user_nicename=attacker&display_name=%27);alert('XSS');//

The attacker observes no immediate response error. When any user visits the newly created forum topic (or any page where the poisoned author's posts appear), the injected JavaScript executes in their browser. The Google Analytics 4 or Matomo tracking script on the page contains the unescaped display name:

gtag('event', 'Authors', {'cd_author': '');alert('XSS');//', 'non_interaction': true});

The payload breaks out of the string context and executes arbitrary code in the victim's browser session, stealing session cookies, CSRF tokens, or performing actions on the victim's behalf.

What the Patch Did

Before:

$seopress_google_analytics_event['cd_author'] = "gtag('event', '" . __( 'Authors', 'wp-seopress' ) . "', {'cd_author': '" . get_the_author() . "', 'non_interaction': true});";

After:

$seopress_google_analytics_event['cd_author'] = "gtag('event', " . seopress_js_string( __( 'Authors', 'wp-seopress' ) ) . ", {'cd_author': " . seopress_js_string( get_the_author() ) . ", 'non_interaction': true});";

The patch introduces output escaping at the point of use via a custom seopress_js_string() function. This function escapes values destined for JavaScript string literals—converting single quotes, double quotes, backslashes, and other metacharacters into their JavaScript escape sequences. The function treats the output as a complete JSON-safe string and wraps it appropriately, breaking the attacker's ability to close the string context and inject arbitrary code. Previously, the plugin directly concatenated get_the_author() (a user-controlled value from the WordPress author display name field) into a JavaScript string without any escaping, trusting instead on HTML context escaping (esc_html() applied downstream) which is ineffective inside a JavaScript execution context.

Root Cause

CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

The dataflow is straightforward: an attacker sets their WordPress user's display name (the display_name field in the wp_users table or author meta) to a value containing JavaScript metacharacters. This value is retrieved by get_the_author() in the seopress_google_analytics_footer_code() function (invoked during page render) and concatenated directly into a JavaScript event tracking call without any output encoding. Because the sink is a JavaScript string literal context—not HTML—the trust boundary crossed is between user-controlled metadata and executable script. The attacker-controlled input reaches the sink through the WordPress user object, bypassing standard HTML-escape defences.

Why It Works

The load-bearing line is seopress_js_string( get_the_author() ). Removing it restores the vulnerability; the attacker's payload executes again. The engineer added parallel escaping calls around other dynamic values (__() translation strings, category names, tag names, domain parsing results) because all of them share the same sink context—JavaScript string literals embedded in gtag() and _paq.push() calls. A single unescaped variable in any of these positions re-opens the XSS gate. The fix recognizes that output escaping is context-specific: HTML escaping (esc_html() or esc_attr()) is useless in JavaScript contexts because the JavaScript parser does not interpret HTML entities. Only JavaScript string escaping—converting quotes and backslashes into their \x or \' equivalents—provides a fence. The patch applies this principle consistently across all ten affected functions in both Google Analytics and Matomo modules.

Hardening Checklist

  • Audit all string concatenations into script blocks: Use wp_json_encode() or a dedicated JavaScript escaping function (like seopress_js_string()) for every dynamic value inserted into <script> tags or on*= event attributes. Do not use HTML escape functions in JavaScript contexts.

  • Require input validation and output escaping in the same diff: When a patch adds a new dynamic value to a script block, enforce a rule that the commit must also add an escaping call. Use pre-commit hooks or code review checklists to catch naked string concatenations.

  • Test XSS via WordPress author metadata: Create unit tests that poison wp_users.display_name, wp_posts.post_author, and user meta fields with payloads like ');alert(1);//, then verify these values do not appear unescaped in rendered script blocks.

  • Centralize escaping functions: Define a single seopress_js_string() equivalent in your plugin's core and document its use for all JavaScript sinks. Avoid ad-hoc escaping that may be incomplete.

  • Lint JavaScript output: Use static analysis tools (e.g., ESLint with a custom rule or grep-based checks in CI) to flag patterns like "' . $var . '" in PHP templates, alerting developers to likely XSS sinks before code review.

References

  • https://nvd.nist.gov/vuln/detail/CVE-2026-96564

Frequently asked questions about CVE-2026-96564

What is CVE-2026-96564?

CVE-2026-96564 is a security vulnerability. This security advisory provides detailed technical analysis of the vulnerability, exploit methodology, affected versions, and complete remediation guidance.

Is there a PoC (proof of concept) for CVE-2026-96564?

Yes. This writeup includes proof-of-concept details and a technical exploit breakdown for CVE-2026-96564. Review the analysis sections above for the PoC walkthrough and code examples.

How does CVE-2026-96564 get exploited?

The technical analysis section explains the vulnerability mechanics, attack vectors, and exploitation methodology. PatchLeaks publishes this information for defensive and educational purposes.

What products and versions are affected by CVE-2026-96564?

CVE-2026-96564 — check the affected-versions section of this advisory for specific version ranges, vulnerable configurations, and compatibility information.

How do I fix or patch CVE-2026-96564?

The patch analysis section provides guidance on updating to patched versions, applying workarounds, and implementing compensating controls.

What is the CVSS score for CVE-2026-96564?

The severity rating and CVSS scoring for CVE-2026-96564 is documented in the vulnerability details section. Refer to the NVD entry for the current authoritative score.