CVE-2026-32499

WPBot – AI ChatBot for Live Support, Lead Generation, AI Services <= 7.7.9 - Unauthenticated SQL Injection

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

Description

The WPBot – AI ChatBot for Live Support, Lead Generation, AI Services plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 7.7.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 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<=7.7.9
PublishedMarch 20, 2026
Last updatedMarch 26, 2026
Affected pluginchatbot

What Changed in the Fix

Changes introduced in v7.8.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps required to exploit an unauthenticated SQL injection vulnerability in the WPBot plugin for WordPress. ### 1. Vulnerability Summary The **WPBot** plugin (versions <= 7.7.9) is vulnerable to an unauthenticated SQL injection. The vulnerability exists in the `wpbo_…

Show full research plan

This research plan outlines the steps required to exploit an unauthenticated SQL injection vulnerability in the WPBot plugin for WordPress.

1. Vulnerability Summary

The WPBot plugin (versions <= 7.7.9) is vulnerable to an unauthenticated SQL injection. The vulnerability exists in the wpbo_search_site function (found in qcld-wpwbot-search.php), specifically when the enable_wp_chatbot_post_content option is enabled. The plugin fails to properly escape the keyword parameter before using it in a SQL fragment within the wpbot_flexible_search_filter function, which is attached to the WordPress posts_search filter.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: wpbo_search_site (inferred from function name and standard plugin patterns)
  • Vulnerable Parameter: keyword
  • Authentication: Unauthenticated (the plugin registers wp_ajax_nopriv_wpbo_search_site)
  • Precondition: The WordPress option enable_wp_chatbot_post_content must be set to 1.

3. Code Flow

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with action=wpbo_search_site.
  2. Handler: The function wpbo_search_site() in qcld-wpwbot-search.php is executed.
  3. Input Handling:
    • The code retrieves $keyword from $_POST['keyword'] and passes it through sanitize_text_field().
    • sanitize_text_field() strips HTML tags but does not escape SQL metacharacters like single quotes (').
  4. Word Variations: The function `_wpbot_generate
Research Findings
Static analysis — not yet PoC-verified

Summary

The WPBot plugin for WordPress is vulnerable to unauthenticated SQL injection through the wpbo_search_site_pagination AJAX action. This occurs due to the improper use of sanitize_text_field() instead of WordPress prepared statements, allowing attackers to inject SQL commands via the 'keyword' and 'type' parameters.

Vulnerable Code

// qcld-wpwbot-search.php line 320
function wpbo_search_site_pagination() {
	global $wpdb;

	$keyword           = sanitize_text_field( $_POST['keyword'] );// phpcs:ignore WordPress.Security.NonceVerification.Missing
	$post_type         = sanitize_text_field( $_POST['type'] );// phpcs:ignore WordPress.Security.NonceVerification.Missing
	$page              = sanitize_text_field( $_POST['page'] );// phpcs:ignore WordPress.Security.NonceVerification.Missing
	$enable_post_types = get_option( 'wppt_post_types' );

    // ... (logic for search settings)

	$searchkeyword = qcld_wpbot_modified_keyword( $keyword );

    // ...

	// qcld-wpwbot-search.php line 350
	if ( get_option( 'active_advance_query' ) != '1' ) {
		$sql   = 'SELECT * FROM ' . $wpdb->prefix . "posts where post_type in ('" . $post_type . "') and post_status='publish' and ((post_title LIKE '%" . $searchkeyword . "%')) order by ID DESC";
		$limit = ' Limit 0, ' . $searchlimit;
	} else {
		// advance query building
		$sql   = 'SELECT * FROM ' . $wpdb->prefix . "posts where post_type in ('" . $post_type . "') and post_status='publish' and ((post_title REGEXP '\\b" . $searchkeyword . "\\b') or (post_content REGEXP '\\b" . $searchkeyword . "\\b')) order by ID DESC";
		$limit = ' Limit 0, ' . $searchlimit;
	}
	$total_results = $wpdb->get_results( $sql );

	if ( ! empty( $total_results ) ) {
        // ...
		if ( $orderby != 'none' or $orderby != 'rand' ) {
			$sql .= " order by $orderby $order";
		}
		$limit = ' Limit ' . ( $total_items * $page ) . ", $total_items";

		$results = $wpdb->get_results( $sql . $limit );
	} else {

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/chatbot/7.7.9/js/qcld-wp-chatbot-plugin.js /home/deploy/wp-safety.org/data/plugin-versions/chatbot/7.8.0/js/qcld-wp-chatbot-plugin.js
--- /home/deploy/wp-safety.org/data/plugin-versions/chatbot/7.7.9/js/qcld-wp-chatbot-plugin.js	2026-02-13 12:31:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/chatbot/7.8.0/js/qcld-wp-chatbot-plugin.js	2026-02-17 08:42:16.000000000 +0000
@@ -2238,7 +2238,7 @@
                     var post_type = obj.attr('data-post_type');
                     var page = obj.attr('data-page');
                     obj.text('Loading...');
-                    var data = {'action':'wpbo_search_site_pagination','name':globalwpw.hasNameCookie,'keyword':keyword,'language': globalwpw.settings.obj.language,'type': post_type, 'page': page};
+                    var data = {'action':'wpbo_search_site_pagination','name':globalwpw.hasNameCookie,'keyword':keyword,'language': globalwpw.settings.obj.language,'type': post_type, 'page': page, 'nonce': qcld_chatbot_obj.nonce};
                     if($(globalwpw.settings.messageLastChild+' .wp-chatbot-comment-loader').length==0){
                         $(globalwpw.settings.messageContainer).append(wpwKits.botPreloader());
                     }
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/chatbot/7.7.9/qcld-wpwbot-search.php /home/deploy/wp-safety.org/data/plugin-versions/chatbot/7.8.0/qcld-wpwbot-search.php
--- /home/deploy/wp-safety.org/data/plugin-versions/chatbot/7.7.9/qcld-wpwbot-search.php	2026-02-13 12:31:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/chatbot/7.8.0/qcld-wpwbot-search.php	2026-02-17 08:42:16.000000000 +0000
@@ -320,9 +320,23 @@
 function wpbo_search_site_pagination() {
 	global $wpdb;
 
-	$keyword           = sanitize_text_field( $_POST['keyword'] );// phpcs:ignore WordPress.Security.NonceVerification.Missing
-	$post_type         = sanitize_text_field( $_POST['type'] );// phpcs:ignore WordPress.Security.NonceVerification.Missing
-	$page              = sanitize_text_field( $_POST['page'] );// phpcs:ignore WordPress.Security.NonceVerification.Missing
+	// Verify nonce for security
+	if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'wpbot_search_nonce' ) ) {
+		wp_send_json_error( array( 'message' => 'Security check failed' ) );
+		wp_die();
+	}
+
+	// Sanitize and validate inputs
+	$keyword           = isset( $_POST['keyword'] ) ? sanitize_text_field( $_POST['keyword'] ) : '';
+	$post_type         = isset( $_POST['type'] ) ? sanitize_text_field( $_POST['type'] ) : 'post';
+	$page              = isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 0;
+	
+	// Validate post type against allowed types
+	$allowed_post_types = array( 'post', 'page', 'product' );
+	if ( ! in_array( $post_type, $allowed_post_types, true ) ) {
+		$post_type = 'post';
+	}
+
 	$enable_post_types = get_option( 'wppt_post_types' );
 	$load_more         = maybe_unserialize( get_option( 'qlcd_wp_chatbot_load_more' ) );
 
@@ -332,14 +346,17 @@
 	if ( is_array( $load_more ) ) {
 		$load_more = $load_more[ array_rand( $load_more ) ];
 	}
-	$searchlimit = ( get_option( 'wppt_number_of_result' ) == '' ? '5' : get_option( 'wppt_number_of_result' ) );
+	$searchlimit = ( get_option( 'wppt_number_of_result' ) == '' ? 5 : absint( get_option( 'wppt_number_of_result' ) ) );
 	$orderby     = ( get_option( 'wppt_result_orderby' ) == '' ? 'none' : get_option( 'wppt_result_orderby' ) );
 	$order       = ( get_option( 'wppt_result_order' ) == '' ? 'ASC' : get_option( 'wppt_result_order' ) );
 	$thumb       = ( get_option( 'wpbot_search_image_size' ) ? get_option( 'wpbot_search_image_size' ) : 'thumbnail' );
 	// order by setup
 	$new_window = get_option( 'wpbot_search_result_new_window' );
 
-	$total_items = get_option( 'wppt_number_of_result' );
+	$total_items = absint( get_option( 'wppt_number_of_result' ) );
+	if ( $total_items < 1 ) {
+		$total_items = 5;
+	}
 
 	$searchkeyword = qcld_wpbot_modified_keyword( $keyword );
 
@@ -347,19 +364,42 @@
 	$response['status'] = 'fail';
 	$response['html']   = '';
 
-	// $sql = "SELECT * FROM ". $wpdb->prefix."posts where post_type in ('".$post_type."') and post_status='publish' and ((post_title REGEXP '\\b".$searchkeyword."\\b'))";
+	// Use prepared statements to prevent SQL injection
 	if ( get_option( 'active_advance_query' ) != '1' ) {
-		$sql   = 'SELECT * FROM ' . $wpdb->prefix . "posts where post_type in ('" . $post_type . "') and post_status='publish' and ((post_title LIKE '%" . $searchkeyword . "%')) order by ID DESC";
-		$limit = ' Limit 0, ' . $searchlimit;
+		// Simple query - search in post_title only
+		$sql = $wpdb->prepare(
+			"SELECT * FROM {$wpdb->prefix}posts 
+			WHERE post_type = %s 
+			AND post_status = 'publish' 
+			AND post_title LIKE %s 
+			ORDER BY ID DESC",
+			$post_type,
+			'%' . $wpdb->esc_like( $searchkeyword ) . '%'
+		);
 	} else {
-		// advance query building
-		$sql   = 'SELECT * FROM ' . $wpdb->prefix . "posts where post_type in ('" . $post_type . "') and post_status='publish' and ((post_title REGEXP '\\b" . $searchkeyword . "\\b') or (post_content REGEXP '\\b" . $searchkeyword . "\\b')) order by ID DESC";
-		$limit = ' Limit 0, ' . $searchlimit;
+		// Advanced query - search in both post_title and post_content
+		$sql = $wpdb->prepare(
+			"SELECT * FROM {$wpdb->prefix}posts 
+			WHERE post_type = %s 
+			AND post_status = 'publish' 
+			AND (post_title REGEXP %s OR post_content REGEXP %s) 
+			ORDER BY ID DESC",
+			$post_type,
+			'[[:<:]]' . $searchkeyword . '[[:>:]]',
+			'[[:<:]]' . $searchkeyword . '[[:>:]]'
+		);
 	}
+	
 	$total_results = $wpdb->get_results( $sql );

Exploit Outline

1. Endpoint: Send an unauthenticated POST request to /wp-admin/admin-ajax.php. 2. Action: Set the 'action' parameter to 'wpbo_search_site_pagination'. 3. Parameters: - 'keyword': Provide a keyword (e.g., 'test'). - 'type': Provide a SQL injection payload (e.g., "post') OR (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -"). This parameter is used directly in a SQL query's 'IN' clause. 4. Vulnerability mechanism: The plugin uses sanitize_text_field() on inputs but fails to use $wpdb->prepare(). This allows an attacker to break out of the single-quoted string context in the query and append malicious SQL syntax. 5. Preconditions: No specific options are strictly required for wpbo_search_site_pagination, although the plugin's search features must be active (default behavior).

Check if your site is affected.

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