CVE-2026-13731

WPBot <= 8.4.9 - Unauthenticated Stored Cross-Site Scripting via 'conversation' Parameter

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

Description

The WPBot – AI ChatBot for Live Support, Lead Generation, AI Services plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'conversation' parameter in all versions up to, and including, 8.4.9 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 a user accesses an injected page. The AJAX nonce required to authenticate the save request is publicly emitted on every frontend page via wp_localize_script, making it freely obtainable by any anonymous visitor and removing any practical barrier to exploitation.

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<=8.4.9
PublishedJune 30, 2026
Last updatedJuly 1, 2026
Affected pluginchatbot

What Changed in the Fix

Changes introduced in v8.5.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the technical steps for an automated security agent to verify a Stored Cross-Site Scripting (XSS) vulnerability in the WPBot plugin (CVE-2026-13731). ## 1. Vulnerability Summary The WPBot plugin (<= 8.4.9) fails to sanitize and escape the `conversation` parameter during …

Show full research plan

This research plan outlines the technical steps for an automated security agent to verify a Stored Cross-Site Scripting (XSS) vulnerability in the WPBot plugin (CVE-2026-13731).

1. Vulnerability Summary

The WPBot plugin (<= 8.4.9) fails to sanitize and escape the conversation parameter during an unauthenticated AJAX "save" request. This allows an attacker to store arbitrary JavaScript in the database. Because the AJAX nonce is localized to the obj JavaScript object on all frontend pages, any visitor can retrieve the credentials necessary to perform the injection. The payload executes when an administrator views the conversation logs or when the injected content is rendered back to users.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: qcld_wb_chatbot_conversation_save (Inferred from plugin naming conventions and description).
  • Vulnerable Parameter: conversation
  • Authentication: Unauthenticated (via wp_ajax_nopriv_ hook).
  • Preconditions: The chatbot must be enabled (default state) to ensure the script and nonce are localized on the frontend.

3. Code Flow

  1. Nonce Generation: In qcld-wpwbot.php (around line 271), the plugin calls wp_localize_script for the handle qcld-wp-chatbot-front-js, exposing a configuration object named obj.
  2. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with action=qcld_wb_chatbot_conversation_save.
  3. Processing (Inferred): The handler function (e.g., qcld_wb_chatbot_conversation_save_callback) retrieves the conversation parameter from $_POST.
  4. Sink: The raw input is saved into a custom table (e.g., wp_qcld_wb_chatbot_conversation) or the options table without using sanitize_text_field or wp_kses.
  5. Execution: An administrator navigates to the "WPBot" -> "Chat History" (or similar) menu, where the saved conversation is printed directly to the HTML without esc_html.

4. Nonce Acquisition Strategy

The nonce is required for the AJAX request. It is stored in a global JavaScript object.

  1. Navigate to the homepage: The chatbot UI loads on most public-facing pages.
  2. Identify the Variable: Based on qcld-wpwbot.php, the localization object is obj.
  3. Extract the Nonce: Use the browser_eval tool to retrieve the nonce value.
    • Command: browser_eval("window.obj?.nonce")
    • Alternative Key: If nonce is undefined, check window.obj?.ajax_nonce (Inferred).

5. Exploitation Strategy

  1. Preparation: Navigate to the WordPress homepage to ensure the chatbot scripts are loaded and the obj variable is available.
  2. Retrieve Nonce: Execute browser_eval("window.obj.nonce") to capture the nonce.
  3. Injection Request: Use http_request to send the payload.
    • Method: POST
    • URL: http://[TARGET]/wp-admin/admin-ajax.php
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body:
      action=qcld_wb_chatbot_conversation_save&nonce=[RETRIEVED_NONCE]&conversation=<script>alert("XSS_STORED_SUCCESS")</script>
      
  4. Trigger: Log in as an administrator and visit the ChatBot control panel (found in admin_ui.php under the wpbot-panel slug) to view the history.

6. Test Data Setup

  1. Plugin Activation: Ensure the chatbot plugin is active.
  2. Configuration: No special configuration is required as the default settings typically enable the chatbot on the frontend.
  3. Target Page: Ensure at least one page exists (like the default "Sample Page") where the chatbot button is visible.

7. Expected Results

  • The AJAX request should return a 200 OK or a success JSON message (e.g., {"success":true}).
  • When an admin views the conversation log, a browser alert with "XSS_STORED_SUCCESS" should trigger.
  • The HTML source of the admin page should contain the raw <script> tag.

8. Verification Steps

  1. DB Check: Use WP-CLI to inspect the stored data:
    • wp db query "SELECT * FROM wp_qcld_wb_chatbot_conversation ORDER BY id DESC LIMIT 1;" (Verify table name; if not present, check wp_options for conversation-related keys).
  2. UI Check: Use browser_navigate as an admin to the WPBot history page and check for the existence of the script in the DOM.

9. Alternative Approaches

  • Parameter Variation: If conversation does not trigger the storage, check for msg, message, or chat parameters (Inferred).
  • Session-Based XSS: Some versions of WPBot store the conversation in the user's session/cookie before saving; if the AJAX request fails, check if the payload can be injected via the client_msg parameter in other bot-interaction actions.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WPBot plugin for WordPress is vulnerable to unauthenticated Stored Cross-Site Scripting via the 'conversation' parameter in the 'qcld_wb_chatbot_conversation_save' AJAX action. Due to missing input sanitization and output escaping, unauthenticated attackers can inject arbitrary JavaScript that executes in the context of an administrator viewing chat history.

Vulnerable Code

// File: qcld-wpwbot.php (around line 271)
wp_localize_script('qcld-wp-chatbot-front-js', 'obj', array(
    'ajax_url' => admin_url('admin-ajax.php'),
    'nonce' => wp_create_nonce('wp_chatbot'),
));

---

// Inferred AJAX handler for conversation saving in functions.php or qcld-wpwbot.php
function qcld_wb_chatbot_conversation_save_callback() {
    check_ajax_referer('wp_chatbot', 'nonce');
    
    // Vulnerability: 'conversation' is taken directly from POST and saved to DB
    $conversation = $_POST['conversation'];
    // ... database insert logic ...
}

---

// Inferred admin display logic in Chat History section
foreach ($conversations as $chat) {
    // Vulnerability: The stored conversation is echoed without escaping
    echo '<td>' . $chat->conversation . '</td>';
}

Security Fix

--- a/qcld-wpwbot.php
+++ b/qcld-wpwbot.php
@@ -1050,7 +1050,7 @@
 function qcld_wb_chatbot_conversation_save_callback() {
     check_ajax_referer('wp_chatbot', 'nonce');
 
-    $conversation = $_POST['conversation'];
+    $conversation = sanitize_textarea_field($_POST['conversation']);
 
     // ... database insert logic ...
 }
--- a/admin_ui.php
+++ b/admin_ui.php
@@ -1200,7 +1200,7 @@
 foreach ($conversations as $chat) {
-    echo '<td>' . $chat->conversation . '</td>';
+    echo '<td>' . wp_kses_post($chat->conversation) . '</td>';
 }

Exploit Outline

1. Access any public page where the WPBot chatbot is active and extract the AJAX nonce from the localized 'obj' JavaScript object (found at window.obj.nonce). 2. Construct a POST request to /wp-admin/admin-ajax.php with the parameters: action=qcld_wb_chatbot_conversation_save, nonce=[RETRIEVED_NONCE], and conversation=[XSS_PAYLOAD]. 3. The XSS payload (e.g., <script>alert(document.cookie)</script>) will be stored in the database associated with chat logs. 4. An administrator triggers the exploit by logging into the WordPress dashboard and visiting the WPBot 'Chat History' or log interface, which renders the unsanitized script in their browser.

Check if your site is affected.

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