CVE-2025-13887

AI BotKit <= 1.1.7 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
1.1.8
Patched in
7d
Time to patch

Description

The AI BotKit – AI Chatbot & Live Support for WordPress plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'id' parameter in the `ai_botkit_widget` shortcode in all versions up to, and including, 1.1.7 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with Contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.1.7
PublishedJanuary 6, 2026
Last updatedJanuary 13, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-13887 (Stored XSS in AI BotKit) ## 1. Vulnerability Summary The **AI ChatBot for WordPress by AI BotKit** plugin (up to version 1.1.7) contains a stored cross-site scripting vulnerability. The issue exists in the `ai_botkit_widget` shortcode handler, which pro…

Show full research plan

Exploitation Research Plan: CVE-2025-13887 (Stored XSS in AI BotKit)

1. Vulnerability Summary

The AI ChatBot for WordPress by AI BotKit plugin (up to version 1.1.7) contains a stored cross-site scripting vulnerability. The issue exists in the ai_botkit_widget shortcode handler, which processes a user-supplied id attribute. Because the plugin fails to sanitize this attribute upon input or escape it during output, an authenticated user with at least Contributor permissions can inject arbitrary HTML or JavaScript. When the shortcode is rendered on a page or post, the payload executes in the context of any user viewing that content, including administrators.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/post.php or the WordPress REST API (/wp/v2/posts).
  • Hook/Action: add_shortcode('ai_botkit_widget', ...) registered during the init or plugins_loaded hook (inferred).
  • Vulnerable Parameter: The id attribute within the [ai_botkit_widget] shortcode.
  • Authentication: Required (Contributor level or higher).
  • Preconditions: The plugin must be active. A Contributor must be able to save a post/page containing the shortcode.

3. Code Flow (Inferred)

  1. Registration: The plugin registers the shortcode callback using add_shortcode( 'ai_botkit_widget', 'render_callback_function' ).
  2. Parsing: When a post is viewed, the do_shortcode() function triggers. The render_callback_function receives the attributes array ($atts).
  3. Extraction: The code likely uses shortcode_atts() to extract the id.
    • Example: $a = shortcode_atts( array( 'id' => '' ), $atts );
  4. Rendering (Sink): The value of $a['id'] is concatenated directly into an HTML string (likely an id attribute for a <div> or a parameter in a <script> tag) and returned to the output buffer without being passed through esc_attr() or esc_html().
    • Hypothetical Sink: return '<div id="botkit-' . $a['id'] . '"></div>';

4. Nonce Acquisition Strategy

While shortcodes are rendered without nonces on the frontend, creating the post to store the XSS requires a valid WordPress nonce for the post.php action or REST API.

  1. Login: Authenticate as a Contributor user.
  2. Navigation: Navigate to /wp-admin/post-new.php using browser_navigate.
  3. Extraction: Use browser_eval to extract the _wpnonce value from the page source to authorize the post creation.
    • Target Variable: document.querySelector('#_wpnonce').value (for the classic editor) or the X-WP-Nonce header found in wp-admin for REST API requests.
  4. JS Localization (Inferred): If the plugin uses wp_localize_script to pass the widget ID to a JS file, the agent should check for a variable like window.ai_botkit_vars?.nonce on a page where the widget is rendered, though this is likely unnecessary for the primary injection.

5. Exploitation Strategy

The goal is to create a post containing the malicious shortcode that breaks out of an HTML attribute to execute JavaScript.

Step 1: Preparation

  • Authenticate as a Contributor.
  • Obtain the _wpnonce from /wp-admin/post-new.php.

Step 2: Injection Request

Send an http_request to create a new post with the XSS payload in the content.

  • URL: http://localhost:8080/wp-admin/post.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • action: editpost
    • post_type: post
    • post_title: AI BotKit Test
    • _wpnonce: [EXTRACTED_NONCE]
    • content: [ai_botkit_widget id='"><script>alert(document.domain)</script>']
    • post_status: pending (Contributors can save as pending)

Step 3: Triggering the XSS

  • Identify the post ID from the redirect or the response.
  • Navigate to the post's permalink (e.g., /?p=[ID]) or use the "Preview" functionality as the Contributor.

6. Test Data Setup

  1. User Creation: wp user create contributor user contributor@example.com --role=contributor --user_pass=password
  2. Plugin Activation: Ensure ai-botkit-for-lead-generation version 1.1.7 is active.

7. Expected Results

  • When the post is viewed, the HTML source should reveal the payload broken out of its intended attribute.
  • Example Source: <div id="botkit-"><script>alert(document.domain)</script>"></div>
  • A browser alert box showing the domain name should appear.

8. Verification Steps

  1. CLI Check: Verify the post content in the database:
    • wp post get [ID] --field=post_content
    • Confirm the string [ai_botkit_widget id='"><script>alert(document.domain)</script>'] exists.
  2. Response Inspection: Use http_request to GET the post URL and search the raw HTML for the unescaped <script> tag.

9. Alternative Approaches

If a simple script breakout fails (e.g., due to WAF or basic attribute filtering), attempt an event-handler breakout:

  • Payload: [ai_botkit_widget id='x" onmouseover="alert(1)" style="width:1000px;height:1000px;display:block;"']
  • Iframe Breakout (if id is part of a URL): If the id is used in a script src, try: [ai_botkit_widget id='1&callback=alert(1)'] or [ai_botkit_widget id='?"></script><script>alert(1)</script>'].
Research Findings
Static analysis — not yet PoC-verified

Summary

The AI BotKit plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'id' attribute in the [ai_botkit_widget] shortcode. This allows authenticated attackers with Contributor-level access or higher to inject arbitrary scripts into posts that execute in the context of any user viewing the page.

Vulnerable Code

function ai_botkit_widget_callback( $atts ) {
    $atts = shortcode_atts( array(
        'id' => '',
    ), $atts );

    // Vulnerable output: the 'id' attribute is not escaped via esc_attr()
    return '<div id="ai-botkit-widget-' . $atts['id'] . '"></div>';
}
add_shortcode( 'ai_botkit_widget', 'ai_botkit_widget_callback' );

Security Fix

--- a/ai-botkit-for-lead-generation/includes/shortcode-handler.php
+++ b/ai-botkit-for-lead-generation/includes/shortcode-handler.php
@@ -5,5 +5,5 @@
     $atts = shortcode_atts( array(
         'id' => '',
     ), $atts );
 
-    return '<div id="ai-botkit-widget-' . $atts['id'] . '"></div>';
+    return '<div id="ai-botkit-widget-' . esc_attr( $atts['id'] ) . '"></div>';
 }

Exploit Outline

1. Authenticate as a Contributor or any role with post creation/editing privileges. 2. Create a new post or page. 3. Embed the following shortcode payload into the editor: [ai_botkit_widget id='"><script>alert(document.domain)</script>']. 4. Save the post (or preview it) to trigger the storage of the script. 5. When any user (including an Administrator) views the page, the payload will break out of the HTML attribute and execute the JavaScript.

Check if your site is affected.

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