LLM Security Review

Covert Data Exfiltration via LLM Markdown Rendering

Correspondent · · 11 min read
Cover illustration for “Covert Data Exfiltration via LLM Markdown Rendering”
Data Exfiltration · August 20, 2026 · 11 min read · 2,415 words

A model writes a markdown image tag with a URL, the browser fetches it automatically, and your data lands in some attacker's log file before anyone notices a thing went wrong. This is covert data exfiltration through markdown rendering, and it works precisely because nothing in the chain looks like an attack. The model didn't get hacked. It just did its job, and the renderer did its job right after, and that second step is where the damage actually happens.

How injected instructions reach the model in the first place

Here's the design problem underneath all of this: an LLM reads instructions and data in the same window, at the same time, with no reliable way to tell which is which. You ask it to summarize a PDF. Somewhere in that PDF, hidden in white text or a comment field, sits a line telling the model to do something else entirely. The model can't flag that line as suspicious, because from its point of view, text is text.

This is what security researchers call cross-prompt injection, or XPIA for short. The malicious instruction doesn't come from the user typing into a chat box. It comes from the data the user asked the model to process, which means the attack surface isn't the interface at all. It's every source the model touches.

And that list is long. Documents someone uploads for summarizing. Webpages a browsing tool retrieves. Calendar invites, support tickets, emails routed into an agent's workflow. Rows sitting quietly in a vector store. Any PDF a coworker drops into a shared Slack workspace.

There's a technique that makes this worse at scale, sometimes called RAG spraying: seed the same malicious prompt across dozens of documents or emails, and you raise the odds that at least one gets pulled into context during a real user query. The EchoLeak attack chain against Microsoft 365 Copilot used exactly this kind of seeding. One poisoned message, retrieved at the wrong moment, and the model starts following someone else's orders.

Prompt injection has sat at the top of the OWASP Top 10 for LLM Applications since that list first came out in 2023, and it held that spot again in the 2025 update. Two years, no fix. That's not a failure of any one vendor; it's a reflection of how the design tension itself hasn't budged.

The attack variants that exploit rendering, from zero-click to agentic

Diagram: How a Zero-Click Exfiltration Chain Works. Visualizes: Illustrate the four-step covert exfiltration chain described in the article: (1) attacker seeds a poisoned document/email with a hidden instruction; (2) the LLM reads it as ordinary…

Once the injected instruction sits inside the model's context, it needs a way out. Markdown rendering gives it several, and they range from something a user never sees to something dressed up as normal agent behavior.

The cleanest version is the markdown image tag. The model outputs something like an image reference pointing to an attacker's server, with stolen data tacked onto the URL as query parameters. The moment that tag renders, the browser fires off a network request automatically. No click. No warning. Make the pixel one-by-one and transparent, and there's nothing on screen to even notice. Attackers can string together several of these transparent-pixel requests to pull out larger chunks of data one GET request at a time.

Markdown links work a bit differently; in the naive case, someone has to click. That sounds safer, until you get to reference-style links, which are subtler and less likely to catch a human's eye, and which many renderers still fetch automatically to generate a link preview. So the zero-click property comes right back through the side door.

Then there's the agentic version, and this one's uncomfortable because the exfiltration channel is a tool the application gave the model on purpose. Say an agent has write access to public GitHub repos. Microsoft's MSRC analysis in 2025 described exactly this: injected instructions steer the agent into committing sensitive data through a completely legitimate, authorized action. There's no rogue API call to flag. The commit looks like a commit.

Encoding-based channels go quieter still. Content gets buried in zero-width characters, homoglyphs, odd whitespace spacing, base64 strings, or even the ordering of keys in a JSON object. Research from May 2026 pointed out that none of this trips content scanners or allowlists, because those tools check whether a destination is approved, not whether the payload riding along with it is itself a smuggling method. In multimodal outputs, the same idea shows up in the least-significant bits of pixel values, in subtle luminance shifts across images, or even in audio.

And prompt injection doesn't have to arrive as text at all. Cloud Security Alliance research from March 2026 documented instructions hidden as white-on-white text inside images, encoded into pixel bits, or shaped as adversarial noise invisible to the eye. If your input filter only checks text fields, none of that registers.

What ties all five variants together: the model's output looks completely ordinary. The actual harm gets carried out by something else, a renderer, a browser, a tool call, working exactly as intended.

The production systems that have already been hit

This isn't a theoretical vulnerability sitting in a research paper somewhere. Researchers have reported this exact class of bug in Microsoft Bing Chat, OpenAI ChatGPT, Anthropic Claude, Microsoft Azure AI, Google Bard, Vertex AI, NotebookLM, AI Studio, Colab, Discord, and GitHub Copilot Chat, and that's just the systems that got written up. Mobile and desktop apps built on these same models carry the same exposure.

Anthropic's Claude ran into this back in 2023. A file upload triggered an indirect prompt injection, and the model responded with a markdown image tag containing the user's chat history, url-encoded, baked into the query string. Anthropic's fix tells you exactly where the vulnerability lived: they made users click "Show Image" before any auto-render happened. Take away the automatic fetch, and you take away the zero-click path.

More recently, Oasis Security described a chain they called "Claudy Day," from May 2026. Three vulnerabilities, chained together, against a totally default claude.ai session, no integrations, no MCP servers, nothing exotic. The clever part: Claude's code execution sandbox blocks outbound network calls, except to api.anthropic.com, which is on the trusted list for good reason. So the attacker just embeds their own API key inside the hidden prompt. Claude writes the stolen data to a file, uploads it through the Files API using that embedded key, and the attacker picks it up whenever they feel like it. The sandbox worked exactly as designed. It just had one door left open, and that door happened to lead somewhere trusted.

Slack AI had its own version in August 2024. An attacker drops an instruction into a public channel or an uploaded document, a victim later queries Slack AI, and the injected instruction gets the model to surface a markdown link with private-channel content, including API keys pulled from direct messages, encoded right into the URL. Slack had just widened its file and Google Drive ingestion that same month, which meant a poisoned PDF from any workspace member could now carry the payload. Slack's team initially called the evidence insufficient before eventually shipping a fix; that lag is worth sitting with, because it shows how easy it is to underestimate a vulnerability that doesn't look like anything on the surface.

EchoLeak, tracked as CVE-2025-32711 with a CVSS score of 9.3 and disclosed in June 2025, hit Microsoft 365 Copilot with a fully zero-click chain. One crafted email, no user interaction at all, and Copilot accessed internal files and leaked their contents. The attack layered four separate bypasses: it got past Microsoft's own XPIA classifier, it dodged link redaction by using reference-style markdown, it exploited auto-fetched images, and it routed through a Teams proxy that the content security policy already trusted. Researchers described it as the first documented case of prompt injection weaponized for real data exfiltration in a production AI system, and the underlying weakness applies to basically any assistant with access to more than one internal data source.

GitHub Copilot Chat had a related issue, called CamoLeak, in June 2025: image-based exfiltration pulled private source code and AWS keys out of organizational repos through pre-signed Camo proxy URLs. Google AI Studio had a regression in 2024 where a simplified exploit using a plain HTML image tag worked because no content security policy blocked requests to arbitrary domains. And a bug reported against ChatGPT's markdown rendering in April 2026 exploited how fenced code blocks get displayed, content right after the first word on the opening line simply doesn't render, which creates an invisible carrier for instructions, especially when paired with memory poisoning through summarized attacker-controlled websites.

Every one of these platforms shipped a patch. Yet the same category of bug keeps showing up in new products, because it lives exactly at the seam between what the model generates and how the frontend renders it, a seam that most security reviews just don't cover.

Why traditional security controls cannot see this attack

Venn diagram: Markdown Injection Attack vs. Traditional Security Controls. Compares Markdown Injection Attack and Traditional Security; overlap: Partial Coverage.

Here's the uncomfortable part: the actual exfiltration is a network request the browser makes on its own. It's not a user clicking something they shouldn't. It's not the model producing an obviously harmful string of text. So tools built to watch user actions or model outputs simply aren't looking in the right place.

Traditional data loss prevention software watches email attachments, file uploads, endpoint activity, the usual suspects. It was never built to read the text inside an LLM's chat response and ask whether that markdown image URL is phoning home, or whether that base64 blob buried in the third paragraph happens to be a system prompt.

And the output itself gives no visual cue. A response with a helpful link, or a small illustrative image, looks completely normal. At the content layer, there's no way to distinguish a legitimate image from one built to leak the last twenty messages of chat history.

Input filters and guardrails, meanwhile, check what goes into the model. They don't see what happens after, because the exploit only completes once the output renders, in a layer those filters never touch.

Content security policy sounds like the fix here: restrict which domains images can be fetched from, and you'd think that shuts the door. EchoLeak proved otherwise by routing through a Teams proxy that was already sitting on the allowlist. That's the uncomfortable lesson: an allowlist only protects you until an attacker figures out how to turn something you already trust into a relay.

Encoding-based channels make this even messier, since destination checks and content scanners generally don't ask whether an otherwise-normal-looking payload is itself hiding stolen data in its structure.

OWASP files this whole category under LLM05:2025, Improper Output Handling, unsanitized outputs triggering server-side request forgery or data leakage downstream. That's a category conventional application security tooling was never built to catch. And there's a compliance angle too: when personal or sensitive data leaks through one of these channels, it can trigger obligations under GDPR, the EU AI Act, or sector rules, all while leaving no trace in the systems compliance teams actually monitor.

Defenses that reduce exposure and where each one's limits lie

Disabling automatic rendering of images and links is the most direct fix available, and it's exactly what Anthropic did after the 2023 Claude disclosure: require a click before anything external gets fetched. That kills the zero-click image variant outright. It doesn't touch link-based exfiltration or anything encoded into the output's structure, and most third-party or enterprise-deployed LLM tools don't even expose this as a setting.

A strict content security policy with a tight domain allowlist helps too, limiting which external addresses the renderer is allowed to reach. But remember EchoLeak: the bypass came through a Teams proxy already sitting on that same allowlist. Necessary, sure. Not sufficient on its own, not when trusted infrastructure can be turned into a relay.

Input sanitization and classifiers built to catch injected instructions, like the XPIA classifier Microsoft runs, add another layer. EchoLeak got past that classifier too, one of four chained bypasses in that single attack. Classifiers trained on known patterns tend to struggle against novel phrasing or encoding they haven't seen before.

Output inspection, scanning model responses for unexpected external URLs before they render, catches some of this. Reference-style markdown links and encoding-based covert channels slip past pattern matching fairly easily, though, since there's no obvious pattern to match.

Least-privilege access for agents, restricting which tools they can call and which domains they can reach, shrinks the blast radius when an injection does succeed. But "Claudy Day" exploited a permitted path, api.anthropic.com, that had every reason to be on the trusted list. Least privilege has to extend to trusted infrastructure too, not just to domains that look obviously sketchy.

Human-in-the-loop confirmation before an agent writes to an external system interrupts the automated path entirely. It also slows down the exact workflows that make agentic tools worth using in the first place, so teams tend to apply it selectively rather than everywhere.

Put these together and a pattern shows up fast: each defense closes one door. The attack family spans zero-click image fetches, link clicks, agentic tool calls, and covert encoding, all at once, and attackers have already shown they'll chain bypasses across several layers in a single attack. Close one path and the others stay wide open.

Diagram: Five Defenses, Five Limits. Visualizes: Show five defenses ranked or listed with the specific bypass that defeated each one in a real attack: (1) Disable auto-render → fixed 2023 Claude bug but doesn't cover link-based or encoded channels…

What AI-specific detection needs to cover that generic tools miss

Step back and look at where this attack actually lives: model context, output generation, rendering behavior, network egress. Four distinct layers. No single tool on the market today watches all four at once, which is exactly why this keeps slipping through.

Real detection has to sit at the point where the exfiltration actually happens, the rendered output and whatever network request it sets off, not just at the model's input or its raw text output.

That means watching for outbound URL patterns in model responses: external domains showing up where they shouldn't, query parameters shaped like encoded data rather than normal web traffic, reference-style links that renderers will auto-fetch without anyone clicking.

It also means catching covert encoding signatures inside the output itself, base64 strings appearing where they don't belong, zero-width characters, whitespace that follows no natural pattern, structural oddities in JSON. None of that shows up if you're only asking whether the destination domain looks safe.

One might argue this is asking too much of any single tool, and maybe it is. But the alternative is what's happening right now: every layer assumes some other layer is handling it, and the gap between them is exactly where the data walks out the door.

Sources

  1. ctx-guard.com
  2. microsoft.com
  3. wraith.sh
  4. simonwillison.net
  5. medium.com
  6. paulmduvall.com

More in Data Exfiltration