CVE-2026-0736

Chatbot for WordPress by Collect.chat ⚡️ <= 2.4.8 - Authenticated (Contributor+) Stored Cross-Site Scripting via Post Meta Field

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

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: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<=2.4.8
PublishedFebruary 13, 2026
Last updatedFebruary 16, 2026
Affected plugincollectchat

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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.php or wp-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)

  1. Registration: The plugin likely registers a metabox on the post edit screen using add_meta_boxes.
  2. Input Rendering: Inside the metabox callback, an input or textarea is rendered with the name _inpost_head_script[synth_header_script].
  3. Storage: The plugin hooks into save_post or wp_insert_post. It retrieves $_POST['_inpost_head_script'] and calls update_post_meta($post_id, '_inpost_head_script', $data) without adequate sanitize_text_field or wp_kses application on the array keys.
  4. Output: The plugin hooks into wp_head. It retrieves the meta value using get_post_meta(get_the_ID(), '_inpost_head_script', true) and directly echoes 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.

  1. Identify the Edit Page: Create a dummy post or find an existing one that the Contributor can edit.
  2. Navigate to Editor: Use browser_navigate to go to wp-admin/post.php?post=POST_ID&action=edit.
  3. Extract Nonce: Use browser_eval to extract the core WordPress _wpnonce and 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.
  4. 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

  1. Authenticate as a Contributor user.
  2. 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

  1. Obtain the _wpnonce from the edit page of the created post.
  2. Construct a POST request to wp-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: editpost
    • post_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

  1. Navigate to the public URL of the post: http://localhost:8080/?p=[POST_ID].
  2. Observe the script execution.

6. Test Data Setup

  1. Plugin: Ensure collectchat version 2.4.8 is installed and active.
  2. User: Create a user with the contributor role.
    wp user create attacker attacker@example.com --role=contributor --user_pass=password
    
  3. Post: Create a post owned by the contributor.

7. Expected Results

  • The POST request should return a 302 redirect back to the post edit page (indicating a successful save).
  • The get_post_meta check (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

  1. Check Database Storage:
    wp post meta get [POST_ID] _inpost_head_script
    
    Confirm the output matches the payload.
  2. Verify Frontend Render:
    Use the http_request tool 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_post hook processes it regardless, manually add the parameter to the editpost request.
  • 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 name synth_header_script, it is highly likely it is intended for raw script output.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/collectchat.php
+++ b/collectchat.php
@@ -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.