CVE-2026-9848

WP Ticket <= 6.0.4 - Unauthenticated SQL Injection via WordPress Search 's' Parameter

highImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
7.5
CVSS Score
7.5
CVSS Score
high
Severity
6.0.5
Patched in
1d
Time to patch

Description

The WP Ticket plugin for WordPress is vulnerable to SQL Injection via the WordPress search query parameter (`s`) in versions up to, and including, 6.0.4 The plugin hooks WordPress's `posts_request` filter with `wp_ticket_com_posts_request()`, which calls `emd_author_search_results()` when the current request is an unauthenticated front-end search. That function reads `$query->query_vars['s']` — already wp_unslash()'d by `WP_Query::parse_query()`, so wp_magic_quotes protection has been stripped — and concatenates the raw value into a SQL `LIKE` clause inside a UNION sub-SELECT appended to the main query, with no `$wpdb->prepare()` or escaping. This makes it possible for unauthenticated attackers 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:N/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=6.0.4
PublishedJune 12, 2026
Last updatedJune 13, 2026
Affected pluginwp-ticket

What Changed in the Fix

Changes introduced in v6.0.5

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

I cannot fulfill your request. I am prohibited from generating actionable exploitation plans, technical attack vectors, or specific payloads for vulnerabilities. This policy is in place to prevent the creation of content that could be used to facilitate real-world harm or cyberattacks. To learn mor…

Show full research plan

I cannot fulfill your request. I am prohibited from generating actionable exploitation plans, technical attack vectors, or specific payloads for vulnerabilities. This policy is in place to prevent the creation of content that could be used to facilitate real-world harm or cyberattacks.

To learn more about identifying and preventing SQL injection vulnerabilities in WordPress, I recommend searching for resources such as:

  • The WordPress Plugin Handbook section on "Database Security" and the proper usage of the $wpdb class.
  • OWASP’s "SQL Injection Prevention Cheat Sheet."
  • General security practices for using $wpdb->prepare() to handle user-supplied data in database queries.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Ticket plugin for WordPress is vulnerable to unauthenticated SQL Injection via the 's' search parameter in versions up to 6.0.4. This occurs because the plugin's emd_author_search_results function concatenates the unescaped search term into a UNION sub-SELECT query, allowing attackers to extract sensitive information from the database.

Vulnerable Code

// includes/common-functions.php line 174
$search = $query->query_vars['s'];
foreach (array_values($set_types) as $ptype) {
	$pids = apply_filters('emd_limit_by', $pids, $app, $ptype, 'frontend');
	$diff_pids = array_diff($pids,Array('0'));	
	if(empty($pids)){
		$input_add .= " UNION (SELECT * FROM " . $wpdb->posts . " WHERE " . $wpdb->posts . ".post_type ='" . $ptype . "' AND " . $wpdb->posts . ".post_status = 'publish' AND ";
		if($type == 'author'){
			$input_add .=  $wpdb->posts . ".post_author=" . $auth_id . ")";
		}
		elseif($type == 'search'){
			$input_add .=  "(" . $wpdb->posts . ".post_title LIKE '%" . $search . "%' OR " . $wpdb->posts . ".post_content LIKE '%" . $search . "%'))";
		}
	}
	elseif(!empty($diff_pids)) {
		$pids_arr = "(" . implode(",",$pids) . ")";
		$input_add .= " UNION (SELECT * FROM " . $wpdb->posts . " WHERE " . $wpdb->posts . ".ID IN " . $pids_arr . " AND ";
		if($type == 'author'){
			$input_add .= $wpdb->posts . ".post_author=" . $auth_id . ")";
		}
		elseif($type == 'search'){
			$input_add .=  "(" . $wpdb->posts . ".post_title LIKE '%" . $search . "%') OR (" . $wpdb->posts . ".post_content LIKE '%" . $search . "%'))";
		}
	}
}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/wp-ticket/6.0.4/includes/common-functions.php	2025-09-04 19:17:26.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-ticket/6.0.5/includes/common-functions.php	2026-06-08 19:31:34.000000000 +0000
@@ -164,24 +164,29 @@
 			$search = $query->query_vars['s'];
 			foreach (array_values($set_types) as $ptype) {
 				$pids = apply_filters('emd_limit_by', $pids, $app, $ptype, 'frontend');
-				$diff_pids = array_diff($pids,Array('0'));	
+				$diff_pids = array_diff($pids,Array('0'));
+				// Prepare wildcard searching safely
+				// esc_like() ensures literal '%' or '_' in user input doesn't break logic
+				$wildcard_search = '%' . $wpdb->esc_like($search) . '%';
+
 				if(empty($pids)){
-					$input_add .= " UNION (SELECT * FROM " . $wpdb->posts . " WHERE " . $wpdb->posts . ".post_type ='" . $ptype . "' AND " . $wpdb->posts . ".post_status = 'publish' AND ";
+					$input_add .= " UNION (SELECT * FROM " . $wpdb->posts . " WHERE " . $wpdb->posts . ".post_type ='" . esc_sql($ptype) . "' AND " . $wpdb->posts . ".post_status = 'publish' AND ";
 					if($type == 'author'){
 						$input_add .=  $wpdb->posts . ".post_author=" . $auth_id . ")";
 					}
 					elseif($type == 'search'){
-						$input_add .=  "(" . $wpdb->posts . ".post_title LIKE '%" . $search . "%' OR " . $wpdb->posts . ".post_content LIKE '%" . $search . "%'))";
+						$input_add .=  $wpdb->prepare("(" . $wpdb->posts . ".post_title LIKE %s OR " . $wpdb->posts . ".post_content LIKE %s))", $wildcard_search,$wildcard_search);
 					}
 				}
 				elseif(!empty($diff_pids)) {
-					$pids_arr = "(" . implode(",",$pids) . ")";
+					$pids_cleaned = array_map('intval', $pids);
+					$pids_arr = "(" . implode(",", $pids_cleaned) . ")";
 					$input_add .= " UNION (SELECT * FROM " . $wpdb->posts . " WHERE " . $wpdb->posts . ".ID IN " . $pids_arr . " AND ";
 					if($type == 'author'){
 						$input_add .= $wpdb->posts . ".post_author=" . $auth_id . ")";
 					}
 					elseif($type == 'search'){
-						$input_add .=  "(" . $wpdb->posts . ".post_title LIKE '%" . $search . "%') OR (" . $wpdb->posts . ".post_content LIKE '%" . $search . "%'))";
+						$input_add .=  $wpdb->prepare("(" . $wpdb->posts . ".post_title LIKE %s) OR (" . $wpdb->posts . ".post_content LIKE %s))",$wildcard_search,$wildcard_search);
 					}
 				}
 			}

Exploit Outline

The exploit involves an unauthenticated attacker submitting a specially crafted SQL payload via the standard WordPress search parameter 's' (e.g., /?s=payload). Because the plugin retrieves the search term from WP_Query variables (which have had magic quotes removed) and directly concatenates it into a LIKE clause within a UNION statement, the attacker can use a single quote to break out of the string literal. This allows them to append arbitrary SQL queries to extract data from any table in the WordPress database. No special authentication or complex payload shape beyond standard UNION-based SQL injection is required.

Check if your site is affected.

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