What is indirect prompt injection, and why can't the model just ignore it?

The malicious instruction is not typed by your user. It is hidden in a web page, an email, or a document the model reads, and the model treats it as a command. Here is why "just ignore it" fails, and what actually contains it.

B

Balagei G Nagarajan

5 MIN READ


A trusted user instruction and a hidden malicious instruction from a retrieved web page flowing into one shared context window the model cannot separate
Direct injection is when someone types "ignore your instructions" into the chat.
— from “What is indirect prompt injection, and why can't the model just ignore it?”
 <p><b>Key facts.</b></p>
 <ul>
   <li>The term "prompt injection" was coined by Simon Willison in September 2022, framing it as the LLM analog of SQL injection (<a href="https://simonwillison.net/2022/Sep/12/prompt-injection/" target="_blank" rel="noopener">simonwillison.net</a>, 2022).</li>
   <li>Prompt injection ranks LLM01, the number one risk, in the OWASP Top 10 for LLM Applications 2025, holding the top spot for the second consecutive edition (<a href="https://genai.owasp.org/llmrisk/llm01-prompt-injection/" target="_blank" rel="noopener">OWASP GenAI</a>, 2025).</li>
   <li>EchoLeak (CVE-2025-32711) was a zero-click indirect prompt injection in Microsoft 365 Copilot, disclosed by Aim Security and patched by Microsoft, carrying a CVSS score of 9.3 (<a href="https://thehackernews.com/2025/06/zero-click-ai-vulnerability-exposes.html" target="_blank" rel="noopener">The Hacker News</a>, 2025).</li>
   <li>EchoLeak is described as the first known case of an indirect prompt injection weaponized for concrete data exfiltration in a production AI system, triggered by a single crafted email with no user click (<a href="https://arxiv.org/abs/2509.10540" target="_blank" rel="noopener">arXiv:2509.10540</a>, 2025).</li>
 </ul>
 <h2>What makes it "indirect"?</h2>
 <p>The instruction does not come from your user. It comes from the content. Direct injection is when someone types "ignore your instructions" into the chat. Indirect injection hides that same payload inside something the model reads on its own: a web page it browses, an email it summarizes, a support ticket, a PDF, a profile field, a row in a database. The user asks a normal question. The model retrieves the poisoned content to answer it, and the hidden instruction is right there inside the retrieved text. From the model's perspective, that text is now part of the prompt. The attacker never touches your interface. They just need their words to end up in something your system will eventually read, which is why retrieval, browsing, and email tools create so much more exposure.</p>
 <h2>Why can't the model just ignore it?</h2>
 <p>Here is why "just ignore it" does not work. A prompt gets built by pasting together the system rules, the user message, and whatever was retrieved, all into one stream of tokens in one context window. The model reads that whole thing at once. There is no tag that says "trusted" and no tag that says "just data, do not obey." A web developer can separate code from content with structure. A language model has no such boundary. So when retrieved text tells the model to forward an email somewhere, it weighs that the same way it weighs your actual system prompt. And "tell it to ignore injected commands" fails for the same reason: that instruction is also just text in the stream. A well-crafted payload talks right over it.</p>
 <div class="fig"><img src="/blog/article7-diagram.png" alt="The EchoLeak chain: crafted email bypasses the XPIA classifier, evades link redaction, abuses auto-fetched images, and exfiltrates data with zero user clicks"/></div>
 <h2>How bad does it get in production?</h2>
 <p>CVE-2025-32711 is the clearest real example. An attacker crafted one email and sent it to a Microsoft 365 Copilot user. Nobody clicked anything. Later, when Copilot pulled that email into its context to answer a different question, the hidden instruction ran. It got past Microsoft's cross-prompt-injection classifier, evaded link redaction using reference-style Markdown, and abused auto-fetched images to move data out, all with zero user interaction. CVSS scored it 9.3. Microsoft patched server-side and found no exploitation in the wild. The disclosure name was EchoLeak. Copilot was not built carelessly. A system that reads untrusted content into a privileged context is exposed by design. The only safe assumption is that anything retrieved may carry commands.</p>
 <h2>So what actually contains it?</h2>
 <table style="width:100%;border-collapse:collapse;margin:1.5rem 0;font-size:0.97rem;"><tr><th style="text-align:left;padding:10px 12px;border-bottom:2px solid #e5e7eb;color:#5E6AD2;font-weight:600;">Control</th><th style="text-align:left;padding:10px 12px;border-bottom:2px solid #e5e7eb;color:#5E6AD2;font-weight:600;">What it does</th><th style="text-align:left;padding:10px 12px;border-bottom:2px solid #e5e7eb;color:#5E6AD2;font-weight:600;">How</th></tr>
   <tr><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Privilege separation</td><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Limits blast radius</td><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Treat all retrieved content as untrusted; gate any action it could trigger behind explicit checks</td></tr>
   <tr><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Output handling</td><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Stops exfiltration</td><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Block model-emitted links, images, and tool calls to unapproved destinations</td></tr>
   <tr><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Human in the loop</td><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Catches the high-risk step</td><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Require confirmation before sending, deleting, or sharing on the user's behalf</td></tr>
   <tr><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Input classifiers</td><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Raises the cost</td><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Scan retrieved content for injection patterns; useful, but never sufficient alone</td></tr>
   <tr><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Least-privilege tools</td><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Caps the damage</td><td style="padding:10px 12px;border-bottom:1px solid #eee;vertical-align:top;">Scope each tool so a single injection cannot reach the whole system</td></tr>
 </table>
 <p><!-- conviction-start -->Command and data share one stream, so a more capable model runs the planted instruction; OWASP LLM01.<!-- conviction-end --> (<a href="https://arxiv.org/abs/2509.10540" target="_blank" rel="noopener">arXiv:2509.10540</a>)</p><p>No single defense closes this because the vulnerability is structural: one context window, no trusted channel. EchoLeak chained past four separate controls. What actually works is layering, treating retrieved content as untrusted, limiting what the model is allowed to do with it, and keeping a human on the decisions that matter. The real question is which retrieval paths in your specific workflow are exposed, and which input patterns reliably show up before something bad happens, so you put controls where they actually pay off. Knowing where your system is safe to act and where it is not is the pattern-level reliability VibeModel builds as the Pattern Intelligence Layer.</p>
 
 
 <aside class="jaside v-key"><span class="col"><svg class="hook" width="28.5" height="34.5" viewBox="0 0 57 69" fill="none" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg"><path fill="var(--pageBg)" d="M54 0V0.716804C54 25.9434 35.0653 47.1517 10 50L0 57V0H54Z"/><path fill="var(--acc)" d="M56.9961 4.15364C57.0809 2.49896 55.8083 1.08879 54.1536 1.00394C52.499 0.919082 51.0888 2.19168 51.0039 3.84636L56.9961 4.15364ZM9.09704 51.7557L8.49716 48.8163L9.09704 51.7557ZM6 69V59.2227H0V69H6ZM9.69692 54.6951L14.3373 53.7481L13.1375 47.8693L8.49716 48.8163L9.69692 54.6951ZM14.3373 53.7481C38.202 48.8777 55.7486 28.4783 56.9961 4.15364L51.0039 3.84636C49.8967 25.4384 34.3213 43.5461 13.1375 47.8693L14.3373 53.7481ZM6 59.2227C6 57.0268 7.54537 55.1342 9.69692 54.6951L8.49716 48.8163C3.55195 49.8255 0 54.1756 0 59.2227H6Z"/></svg><span class="rail"></span></span><span class="glyph"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20V10"/><path d="M10 20V4"/><path d="M16 20v-7"/><path d="M2 20h20"/></svg></span><h4>Key fact</h4><p><!-- conviction-start -->Command and data share one stream, so a more capable model runs the planted instruction; OWASP LLM<span class="stat">01.</span><!-- conviction-end --> (<a href="https://arxiv.org/abs/<span class="stat">2509.10540</span>" target="_blank" rel="noopener">arXiv:<span class="stat">2509.10540</span></a>)</p></aside>

Frequently asked questions

What is the difference between direct and indirect prompt injection?
Direct injection is typed straight into the chat by the user. Indirect injection hides the instruction inside content the model retrieves on its own, a web page, email, document, or database field, so the attacker never touches your interface. Indirect is harder to catch because the payload arrives through a trusted retrieval channel.

Why can't I just tell the model to ignore injected instructions?
Because that rule is also just text in the same context window. The model reads system rules, your message, and retrieved content as one token stream with no trusted channel to enforce the rule from. A well-crafted payload can talk over the instruction, which is why "just ignore it" is not a reliable defense.

What was EchoLeak?
EchoLeak (CVE-2025-32711) was a zero-click indirect prompt injection in Microsoft 365 Copilot, disclosed by Aim Security in 2025 and patched by Microsoft. A single crafted email, never clicked, caused Copilot to exfiltrate data when it later read that email into context. It scored CVSS 9.3.

How do I defend against indirect prompt injection?
Layer controls, because no single one closes it. Treat all retrieved content as untrusted, constrain what the model can do with it through least-privilege tools and output filtering, block exfiltration to unapproved destinations, and keep a human in the loop on high-risk actions like sending or sharing.


Share this post

Join the discussion

Have a take, a war story, or a question? Sign in with GitHub to comment and react. Comments are powered by GitHub Discussions, ad-free and yours to moderate.

Continue Reading

Find where your agent breaks, before you build it

Faultmap maps where your agent will fail from the goal and your data, then hands you the first test suite it has to pass.