WPBot <= 8.4.9 - Unauthenticated Stored Cross-Site Scripting via 'conversation' Parameter
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:NTechnical Details
What Changed in the Fix
Changes introduced in v8.5.0
Source Code
WordPress.org SVNThis 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
- Nonce Generation: In
qcld-wpwbot.php(around line 271), the plugin callswp_localize_scriptfor the handleqcld-wp-chatbot-front-js, exposing a configuration object namedobj. - Entry Point: An unauthenticated user sends a POST request to
admin-ajax.phpwithaction=qcld_wb_chatbot_conversation_save. - Processing (Inferred): The handler function (e.g.,
qcld_wb_chatbot_conversation_save_callback) retrieves theconversationparameter from$_POST. - Sink: The raw input is saved into a custom table (e.g.,
wp_qcld_wb_chatbot_conversation) or the options table without usingsanitize_text_fieldorwp_kses. - 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.
- Navigate to the homepage: The chatbot UI loads on most public-facing pages.
- Identify the Variable: Based on
qcld-wpwbot.php, the localization object isobj. - Extract the Nonce: Use the
browser_evaltool to retrieve the nonce value.- Command:
browser_eval("window.obj?.nonce") - Alternative Key: If
nonceis undefined, checkwindow.obj?.ajax_nonce(Inferred).
- Command:
5. Exploitation Strategy
- Preparation: Navigate to the WordPress homepage to ensure the chatbot scripts are loaded and the
objvariable is available. - Retrieve Nonce: Execute
browser_eval("window.obj.nonce")to capture the nonce. - Injection Request: Use
http_requestto 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>
- Method:
- Trigger: Log in as an administrator and visit the ChatBot control panel (found in
admin_ui.phpunder thewpbot-panelslug) to view the history.
6. Test Data Setup
- Plugin Activation: Ensure the
chatbotplugin is active. - Configuration: No special configuration is required as the default settings typically enable the chatbot on the frontend.
- 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 OKor 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
- 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, checkwp_optionsfor conversation-related keys).
- UI Check: Use
browser_navigateas an admin to the WPBot history page and check for the existence of the script in the DOM.
9. Alternative Approaches
- Parameter Variation: If
conversationdoes not trigger the storage, check formsg,message, orchatparameters (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_msgparameter in other bot-interaction actions.
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
@@ -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 ... } @@ -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.