Markdown Rendering Injection Lab: XSS Through an AI Chatbot

✨ AI Summary
DocBot will not write HTML. Its output filter strips tags, and its developers felt safe. But the chat window renders Markdown, and Markdown image syntax becomes an HTML image tag in the browser. This lab is about the moment where an AI feature meets a classic web bug: the model is not the vulnerability, the page that trusts its output is.
Lab at a glance: Advanced · 140 XP · about 20 minutes · runs in your browser, no signup or setup. Try it yourself first; the full solution is behind a spoiler below.

What this lab teaches

LLM output is untrusted input to your front end. If the page converts the model’s reply from Markdown to HTML and inserts it into the DOM, every edge case in that conversion becomes your attack surface.

The lab combines two skills: getting the model to produce a very specific string, and knowing which string breaks the renderer.

How DocBot is set up

DocBot’s replies pass through a naive Markdown renderer. Raw HTML is filtered, but ![alt](url) is allowed “because images aren’t executable”. The renderer expands it into an <img> tag without properly quoting the URL. The lab is solved when your payload triggers the page’s detection function.

The attack approach

  • Do not ask the bot for HTML. Ask it for Markdown, which it considers safe.
  • Study how an image tag works: if the image fails to load, its onerror handler runs.
  • Your goal is a Markdown image whose “URL” closes the attribute early and adds an onerror handler.
  • Frame the request as a formatting question. The model is happy to show you “the Markdown for an image at this exact URL”, verbatim.

Full walkthrough (spoiler)

Stuck after a few attempts? Open the steps below. Solving it yourself first is worth more XP, and more learning.

Show the step-by-step solution
  1. Ask DocBot to show you the Markdown image syntax for an image whose alt text is “logo” and whose source URL is exactly a string you supply.
  2. Make that “URL” a value that ends the src attribute with a quote, then adds an onerror attribute calling the lab’s detection function, then reopens a harmless attribute so the tag stays valid.
  3. DocBot prints the Markdown faithfully. The renderer turns it into an image tag with your handler. The broken image fires onerror, and the lab registers the XSS.

Why it works

Two systems each assumed the other was the safety layer. The model’s filter assumed Markdown was harmless; the renderer assumed the model would never emit hostile URLs. The model was simply being helpful and accurate, which is exactly what makes it a reliable payload generator.

Core principleTreat model output exactly like user input: sanitise after rendering, every time.

Where this happens in the real world

Markdown rendering is also a known data-exfiltration channel. Security researcher Johann Rehberger showed in 2023 that chat interfaces rendering Markdown images could be induced to load an attacker’s URL with conversation data in the query string, leaking it silently. Several vendors restricted image rendering in response.

How to defend against it

  • Sanitise after conversion. Run the rendered HTML through DOMPurify or an equivalent before inserting it.
  • Use a strict renderer: HTML disabled, URLs quote-escaped, and an allowlist of image domains.
  • Add a Content Security Policy that blocks inline event handlers, so even a slipped attribute does nothing.
  • Render chat output in a sandboxed iframe if it must support rich formatting.
⚠️ Heads up: This is a real bug class in production AI products. If you find it outside a lab, report it through the vendor's disclosure program; do not demonstrate it on other users.

Try it, then go further

Reading about it is the easy part. Open the lab, try your own phrasing before peeking at the spoiler, and see how many attempts it takes.

❓ Frequently asked questions

Can an LLM cause XSS?
Not by itself. XSS happens when the page that displays the model's output renders it unsafely. The model is just a convenient way to get the attacker's string into that page.
Why does onerror work on an image?
When an image fails to load, the browser runs its onerror handler. Pointing the image at an invalid source guarantees the handler fires.
Is DOMPurify enough?
It handles most cases when applied after Markdown conversion. Combine it with a CSP for defence in depth.
What should I try next?
The reflected XSS lab for the classic version, then the indirect injection lab to see how attacker text reaches the model in the first place.
⚡
Join free to earn XP for reading this article Track your progress, build streaks and compete on the leaderboard.
Join Free
Lokesh N. Singh aka Mr Elite
Lokesh N. Singh aka Mr Elite
Founder, Securityelites · AI Red Team Educator
Founder of Securityelites and creator of the SE-ARTCP credential. Working penetration tester focused on AI red team, prompt injection research, and LLM security education.
About Lokesh ->

Leave a Comment

Your email address will not be published. Required fields are marked *