SECURITY ADVISORY / 01

CVE-2026-3178 Exploit & Vulnerability Analysis

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

cve_patchdiff:name-directory NVD ↗
Exploit PoC Vulnerability Patch Analysis

The Exploit

An unauthenticated attacker can inject arbitrary JavaScript into the WordPress admin dashboard by crafting a malicious directory entry with an HTML entity–encoded payload that decodes after sanitization.

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: target.local
Content-Type: application/x-www-form-urlencoded

action=name_directory_add_entry&name_directory_name=Test%20User&name_directory_description=<img%20src=x%20onerror=alert('XSS')>

The payload <img src=x> stores in the database and triggers JavaScript execution whenever any user (including WordPress administrators) views the directory listing. The attacker observes no immediate feedback — the AJAX request succeeds with a 200 response — but upon navigating to the directory page, the injected image tag decodes and fires the onerror handler in the admin or frontend context.

To verify, retrieve the stored entry:

GET /wp-admin/admin-ajax.php?action=name_directory_get_entry&id=1 HTTP/1.1
Host: target.local

The response includes the decoded payload ready to execute in the page DOM.


What the Patch Did

Before:

function name_directory_deep_sanitize_public_user_input($input, $allowed_tags = null) {
    $raw = trim( wp_unslash( (string)$input ) );

    if( ! is_array( $allowed_tags ) ) {
        $allowed_tags = array('p' => array(), 'br' => array(), 'strong'=>array(), 'em'=>array());
    }
    return wp_kses( $raw, $allowed_tags );
}

After:

function name_directory_deep_sanitize_public_user_input($input, $allowed_tags = null) {

    $raw = trim( wp_unslash( (string)$input ) );

    $decoded = html_entity_decode( $raw, ENT_QUOTES | ENT_HTML5, 'UTF-8' );

    if( ! is_array( $allowed_tags ) ) {
        $allowed_tags = array('p' => array(), 'br' => array(), 'strong'=>array(), 'em'=>array());
    }

    return wp_kses( $decoded, $allowed_tags );
}

The patch introduced html_entity_decode() to normalize HTML entities before applying wp_kses() whitelist filtering. The intent was to prevent attackers from bypassing the whitelist by submitting pre-encoded payloads; however, the implementation is backwards. The control added — html_entity_decode() — actually weakens security by converting encoded entities back into raw HTML before the sanitizer runs. A complementary patch to admin.php replaced unescaped output calls (html_entity_decode(stripslashes($name->name))) with esc_html(), shifting the defense to the output layer, but this does not prevent the root storage of malicious content.


Root Cause

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

The vulnerability stems from a logic inversion in the sanitization pipeline. When user input arrives in the name_directory_name and name_directory_description POST parameters via AJAX, it flows through name_directory_deep_sanitize_public_user_input(). The original code passed raw input directly to wp_kses(), which strips disallowed tags and attributes. By adding html_entity_decode() before wp_kses(), the patch unintentionally decodes HTML entities that an attacker may have supplied as &lt;img&gt;, converting them back to <img> before the whitelist filter evaluates them. Since img is not in the $allowed_tags array (only p, br, strong, em), wp_kses() removes the decoded tag—but only after the damage of having decoded it. Worse, if an attacker uses a double-encoded payload (&amp;lt;img&gt;), a single decode leaves &lt;img&gt;, which then passes wp_kses() and decodes again at output time when html_entity_decode() is called in admin.php line 930. This crosses the trust boundary of the database: unsanitized content is persisted, and later decoded during output, bypassing the whitelist.


Why It Works

The load-bearing line is html_entity_decode() itself — without it, pre-encoded payloads remain benign strings. If you removed the decode, an attacker's &lt;script&gt; would stay encoded in the database and render harmlessly as literal text in the browser. However, the engineer added html_entity_decode() with good intent: to catch double-encoding bypasses. The problem is order of operations. The correct sequence is sanitize → store → escape at output. The broken sequence is decode → sanitize → store → decode at output, which allows an attacker to craft a payload that survives the first decode-sanitize cycle (via double-encoding or tag alternatives) and then fires after the second decode at display time. The complementary change in admin.php from html_entity_decode() to esc_html() for output only papers over the deeper storage vulnerability — it prevents one specific attack vector but does not restore the integrity of the sanitization function itself.


Hardening Checklist

  • Apply wp_kses_post() or wp_kses() directly to user input at the point of storage, without prior decoding. Let wp_kses() be the canonical sanitizer; never decode before filtering.
  • Use sanitize_text_field() for GET/POST parameters that will not be embedded as HTML (e.g., $_GET['sub']), as the patch correctly added to admin.php lines 63, 116, 117.
  • Escape output using context-aware functions (esc_html() for HTML context, esc_attr() for attribute context, esc_url() for URLs) after retrieval from the database, never before storage.
  • Audit any call to html_entity_decode() in input-processing code. If it appears before a sanitization function, reverse the order. If it appears at output time, verify it does not introduce a second decode that converts already-escaped content.
  • Use a static analysis tool such as phpstan with a security ruleset to flag patterns like html_entity_decode() followed by incomplete sanitization (e.g., wp_kses() with a restrictive whitelist that does not include the tags being decoded).

References

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

Frequently asked questions about CVE-2026-3178

What is CVE-2026-3178?

CVE-2026-3178 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-3178?

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

How does CVE-2026-3178 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-3178?

CVE-2026-3178 — 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-3178?

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-3178?

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