MediumStored HTML Injection· 3 min read

The Chatbot That Rendered My HTML

A URL parameter got stored and replayed — inside the site's AI assistant, unescaped, for other users to see.

Target anonymized · a careers portal

New features tend to reintroduce old bugs, because the hard-won lessons live in the old code. A careers site had bolted an AI assistant onto its results pages — and the assistant hadn't learned the lesson about escaping output.

The spark

A parameter on the results page was reflected somewhere I didn't expect: not in the page HTML, but inside the AI chat panel that opened alongside it. And it came back as markup, not text.

Digging in

The question that decides severity here is "reflected only to me, or stored and shown to others?" I injected a harmless tag and reopened the assistant in a separate context — the markup was still there, rendered. It persisted into the chat view rather than just bouncing back in my own response.

The assistant treated page content as trusted. But that content was partly attacker-controlled, and it was being drawn into a UI other people open.

The exploit

A benign proof — an anchor tag — was enough to demonstrate it:

?query=<a href="https://attacker.example">read me</a>

The link rendered as a real, clickable element inside the AI chat. From there it's ordinary phishing and UI-redress, except it's happening inside a component users are inclined to trust because it's "the assistant."

Impact

Stored HTML injection that renders in other users' AI chat sessions: attacker-controlled links and layout inside a trusted surface — a clean phishing and social-engineering vector.

The fix

Contextually escape every piece of user-influenced content before it reaches the chat UI, and add a content-security policy so injected markup can't pull in anything active.

html-injectionstoredai-chatphishing

All identifiers, targets and payloads in this post are anonymized or defanged. Findings were reported and resolved through responsible disclosure.