Ghost in the Shell Predicted Prompt Injection in 1995

Ghost Check #1: the Puppet Master hacked minds by exploiting the gap between what a system experiences and what it can verify. Thirty-one years later, a researcher did the same thing to Microsoft 365 Copilot with a single email.

I was up well past a school-night bedtime the first time I watched Ghost in the Shell, half understanding the plot and fully understanding that I needed to know how any of this worked. That film is the reason I do this for a living. I've referenced it in passing on this site before. This is the piece where I stop referencing it and hold it up against real systems security, line by line, because one plot thread in that movie is a more accurate model of a 2025 vulnerability class than most current security training material manages.

Each entry takes one piece of speculative technology from science fiction and checks it against what security research has found in production systems. First up: cyberbrain hacking, and the eerie amount it got right about a problem the industry didn't have a name for until an AI assistant leaked corporate data from a single crafted email.

What "Ghost Hacking" Proposes

In Mamoru Oshii's 1995 film, most human minds in this future are cyberbrains: biological brains augmented with a direct network interface, letting people access external data and communicate mind-to-mind the way we'd check a phone today. The film's central threat, a rogue program called the Puppet Master, doesn't attack bodies. It attacks minds directly over that network connection, taking control of victims without their knowledge and, in the film's most unsettling sequence, fabricating an entire false memory set in one victim, a wife and daughter who never existed, so convincing that the man has no reason to doubt his own life story. He isn't lying when he describes his family. He believes it completely, because the belief was planted at the same layer where every other genuine memory lives, and nothing in his own head can tell the difference between a memory he formed and one that was placed there by something else.

Strip away the cybernetic-brain framing and you're left with a precise description of a structural problem: a system that has no reliable way to distinguish content it generated from its own experience from content that was inserted into it by an external party, because both arrive through the identical channel and get stored in the identical format. The system trusts its own memory by default, because what other choice does it have. That trust is exactly what gets exploited.

Score one for the 1995 screenwriters. That's not a loose metaphor. That's a working description of the actual mechanism behind the most dangerous class of vulnerability in production AI systems today.

The Real Version Has a Name: Prompt Injection

OWASP's 2025 Top 10 for LLM Applications ranks prompt injection as the number one risk to language model applications, and the definition reads like a technical translation of the Puppet Master's whole method: an attacker manipulates the input an LLM receives so that it overrides, bypasses, or alters the model's intended instructions. Direct injection means a user types the malicious instruction straight into a chat window. Indirect injection is the one that matters here, because it's the one with no equivalent in traditional web security: the model reads untrusted content from a website, a document, an email, or a ticket, and treats instructions buried in that content as though they came from a trusted source, because structurally, nothing distinguishes them.

That's the whole vulnerability, and it's worth sitting with why it's so hard to close. A SQL injection flaw exists because a developer failed to separate a query's structure from its data, and you fix it with parameterized queries, a hard boundary the database enforces. An LLM has no equivalent hard boundary to enforce. Everything the model sees, the system prompt an engineer wrote, the user's actual question, and a chunk of a webpage the model was asked to summarize, arrives as the same kind of thing: tokens in a context window. The model reasons over all of it as language, and language doesn't come pre-labeled as instruction or data. You can't patch your way out of that the way you patch a missing parameterization. The flaw is closer to the architecture than to a specific line of buggy code, which is precisely the position the Puppet Master's victims were in. There was no antivirus for a mind that trusts its own memories by design.

I've Fought This Exact Failure Before, Wearing a PHP Costume

Nobody covering prompt injection as a brand-new AI problem seems to mention this: the underlying failure, a system that can't tell data from instructions because both arrive through the same channel in the same format, is not new. I was finding variations of this exact structural mistake in PHP applications years before anyone needed the phrase "prompt injection," and walking through the old version makes the new version easier to understand instead of only frightening.

The clearest historical parallel is PHP Object Injection, and if you've spent any real time auditing PHP applications you already know exactly where I'm going. PHP's unserialize() function takes a specially formatted string and reconstructs it into a live PHP object, complete with its original class and properties. That's convenient for storing complex data in a cookie, a session, or a hidden form field, and for years plenty of applications did exactly that, including a "remember me" cookie I found unserializing user-controlled data with zero validation during a SudoSecurity engagement a while back. The client's app stored a serialized object in the cookie to reconstruct a partial session state, and the application happened to have several classes in scope with __wakeup() and __destruct() magic methods, methods PHP calls automatically the moment an object gets constructed or destroyed, with no explicit call required anywhere in the visible code path.

An attacker who understands the classes available in that application doesn't need to find a traditional injection point at all. They craft a serialized string representing an object of their choosing, with property values of their choosing, hand it to the application as though it were a legitimate cookie, and the moment unserialize() reconstructs it, PHP calls whatever magic methods that class defines automatically. Chain several of those together, a technique security researchers call a POP chain, property-oriented programming, and you can walk from "the app deserialized some data" to full remote code execution without ever touching a traditional injection point like a raw SQL query or an unescaped output.

Sit with the shape of that failure for a second, because it's the identical shape as EchoLeak, running on a different substrate. The application had no way to distinguish "data I'm safely storing and later reconstructing" from "executable instructions that happen to be formatted as data." Both arrived through the identical channel, a string in a cookie, and got processed by the identical function, with no structural checkpoint anywhere in between asking whether this particular string should be trusted with the power to call arbitrary methods. That's not a coincidence of two unrelated bugs. That's the same underlying category of mistake, discovered independently in two completely different technology stacks fifteen years apart, because the mistake is fundamental to any system that automatically reconstructs behavior from stored data without a hard boundary checking what it's reconstructing.

Server-side template injection is the other direct ancestor, and it's worth naming because it's closer still to what EchoLeak did. Template engines exist to let you mix static markup with dynamic data, and plenty of them, Smarty and Twig in the PHP world, and vBulletin's own home-grown templating system, which I've spent more hours than I'd like reverse-engineering for plugin work, compile template strings into executable code at some point in the request lifecycle. If user-controlled input ever reaches the template compilation step instead of staying confined to a variable being inserted into an already-compiled template, you get to run arbitrary code inside the application's own execution context, using the templating engine's own compiler as your delivery mechanism. The classic proof-of-concept payload, a template expression that evaluates a basic arithmetic operation and reflects the computed result back instead of the literal characters you submitted, is the exact same "is this data or is this an instruction" ambiguity Copilot's retrieval pipeline fell into with that planted email. The only thing that changed between an SSTI payload in 2015 and EchoLeak in 2025 is which compiler gets confused, a template engine's parser or a language model's attention mechanism, and how much collateral access the confusion grants once it succeeds.

I bring up the PHP history because it changes how you should think about defending against this new version. Nobody solved PHP object injection by teaching unserialize() to be smarter about which strings it trusted. They solved it by refusing to let untrusted input anywhere near a deserialization function in the first place, switching to formats like JSON that reconstruct plain data structures instead of live objects with executable magic methods attached. The fix wasn't a smarter filter. It was removing the structural ambiguity entirely. That's the lesson worth carrying into the AI world, and it's a less comfortable lesson than "add a better classifier," which is exactly the fix Microsoft had in place and exactly the fix EchoLeak got past anyway.

EchoLeak: The Puppet Master's Method, Running Against Microsoft 365

In June 2025, researchers at Aim Security disclosed a vulnerability they named EchoLeak, tracked as CVE-2025-32711 with a CVSS score of 9.3. It targeted Microsoft 365 Copilot, and Microsoft's own advisory described it plainly: an attacker could disclose information over a network with no action required from the victim at all. Zero clicks. The user didn't open a malicious attachment, click a suspicious link, or approve any permission. They used Copilot the way they always did, on a completely unrelated task, and that was enough.

The chain runs in four stages, worth walking through slowly, because every stage maps to something the Puppet Master storyline dramatized three decades early:

Stage one, the plant. An attacker sends a target a completely ordinary-looking email. Hidden inside it, invisible to a human reader, sits an injected instruction, embedded in an HTML comment or rendered in white-on-white text so it never appears on screen. The email clears spam and phishing filters because it contains no malicious link, no attachment, nothing a traditional filter is trained to catch. It's an ordinary email carrying invisible text along inside it, sitting in the target's inbox, waiting.

Stage two, the trigger. At some later point, the target asks Copilot something completely unrelated, summarize my recent messages, catch me up on this project, anything that causes Copilot's retrieval-augmented generation system to pull relevant context from the user's own email and documents. The planted email gets swept up in that retrieval, because from the system's perspective it's more of the user's own data, no different from anything else in the inbox. It has no reason to treat it differently.

Stage three, the confusion. This is the exact moment the Puppet Master's whole method describes precisely. The hidden instruction inside that retrieved email enters Copilot's context window mixed in with the user's legitimate request, and the model has no structural way to tell the difference between "the developer's system prompt," "the user's actual question," and "text that happened to be sitting in a retrieved email." It's all tokens now, the same way every memory in a cyberbrain is data once it's stored, genuine and implanted alike.

Stage four, the exfiltration. The EchoLeak researchers didn't stop at proving the confusion existed. They chained it into a working exploit, evading Microsoft's XPIA classifier, a system built specifically to catch cross-prompt injection attempts, getting past link redaction by using reference-style Markdown syntax the redaction logic didn't account for, exploiting Copilot's automatic image-fetching behavior, and abusing a Microsoft Teams proxy that the platform's own content security policy permitted. The end result: Copilot pulled sensitive data from the user's own context and transmitted it to a server the attacker controlled, and the user never saw a single sign that anything had happened. Microsoft patched the specific chain server-side and found no evidence it had been exploited in the wild before disclosure. The technique itself, the structural confusion at its core, patches nothing, because it isn't a bug. It's what happens when a system that automatically ingests external content treats that content as part of its own trusted memory.

That's not a loose analogy to a cyberbrain getting hacked over a network connection by something that was never invited in. That's the same failure, in a different substrate, disclosed by a security research firm instead of dramatized by a film studio.

Where the Metaphor Breaks, and It's Worth Knowing Exactly Where

I said at the top of this column that I'd check the fiction against the reality, not only find the parts that flatter the fiction. This is where Ghost in the Shell overshoots what current prompt injection does, and the gap matters if you're building on this stuff professionally.

The Puppet Master doesn't stop at confusing one exchange. It rewrites a victim's persistent identity, seamlessly, with full narrative coherence, memories that hold up under any amount of internal scrutiny because they were built to survive it. EchoLeak, and prompt injection generally, doesn't do that to the model itself. It's a per-session confusion, scoped to the request that triggered it. The underlying model wasn't reprogrammed. Its weights didn't change. Once that specific conversation ends, the model has no residual belief that it should keep leaking data to an attacker's server, because nothing about its identity or training was touched.

The category that comes closer to genuine Puppet-Master-style reprogramming exists, and it's a different, arguably scarier entry on the same OWASP list: data and model poisoning, where an attacker corrupts the training or fine-tuning data itself, aiming for a persistent behavioral change baked into the model across every future session, not one crafted email. That's a harder attack to pull off and a much harder one to detect, and it's the honest technical answer to "which real vulnerability sits closest to what happened to that garbage collector in the film." Prompt injection is a con run on a single conversation. Model poisoning comes closer to rewriting the ghost itself.

What This Means If You're Building on Top of an LLM Right Now

OWASP's own guidance on this is refreshingly blunt: you cannot patch prompt injection away, because it exploits the design of the system rather than a specific flaw in it. What you can do is defense in depth, and it's worth being concrete about what that means instead of leaving it as a slogan.

Treat every piece of retrieved content as untrusted, permanently, with no exceptions for content that happens to come from inside your own organization's data. That garbage collector trusted his own memories completely, and that trust was the entire vulnerability. Don't let your application inherit the same failure. Segregate external content from instructions structurally wherever your architecture allows it, techniques like spotlighting exist specifically to give a model a fighting chance at recognizing which tokens came from a trusted system prompt and which arrived along for the ride in a document. Restrict what any agent or assistant can do without a human confirming it first, particularly anything that sends data externally, because excessive agency is what turns a confused model into a data exfiltration channel instead of a contained, recoverable mistake. And treat every model output as untrusted data on the way back out too, not only on the way in, since a successful injection often needs the model's own output to complete the exfiltration.

None of this is exotic advice once you strip the AI framing off it. It's the same lesson this site keeps returning to about the marketing claim that any system is unhackable: the moment you build something that automatically ingests external input and acts on it, you've built an attack surface, and no amount of confidence in your own classifier changes that. Microsoft had an XPIA classifier built specifically to catch this category of attack, and EchoLeak went around it anyway.

The Architecture-Level Fix: Privilege Separation, Not a Smarter Filter

The PHP object injection lesson from earlier applies directly here, and it's worth getting concrete about what it looks like in an actual system design instead of leaving it as a principle. The fix that finally worked for PHP deserialization wasn't a smarter unserialize(). It was refusing to let untrusted data reach a function with that much unchecked power in the first place. The equivalent architecture for LLM applications is a pattern security researchers increasingly describe as privilege separation, or the dual-model pattern: split a single, all-powerful assistant into two components that never fully trust each other.

[Privileged model]                    [Quarantined model]
- Sees the user's actual request       - Sees untrusted retrieved content
- Sees the system prompt                 (emails, web pages, documents)
- Can call tools and take action       - Has NO tool access, NO memory
- NEVER reads raw untrusted content    - Can only return structured,
  directly                               non-executable data (plain
                                          strings, typed fields)
 
User request ──────► Privileged model ──► needs external data? ──►
                                                    │
                                                    ▼
                                        Quarantined model reads the
                                        untrusted document, extracts
                                        only the specific structured
                                        fact requested, and returns
                                        it as inert data
                                                    │
                                                    ▼
                            Privileged model receives a plain value,
                            never the raw document, and continues
                            acting on the user's original request

The quarantined model is deliberately crippled. It can read the sketchy email, but it has no tools to call, no memory of past sessions, and no path back to acting on anything it finds. If that planted instruction inside the EchoLeak email had been processed only by a component with this little power, the worst outcome is a wrong or garbled data extraction, not a live channel back out to an attacker's server. The privileged model, the one with tool access and the ability to send data anywhere, never touches the raw untrusted text at all. It only ever sees the narrow, structured answer the quarantined model handed back.

This isn't a complete solution, and it's worth being honest about where it still leaks. The handoff between the two models is itself a channel, and if the quarantined model's structured output format is loose enough, "return a JSON object with whatever fields seem relevant" is a common enough real-world implementation, an attacker can still smuggle instructions into the shape of that output rather than its raw content. The fix only holds as long as the boundary between the two models stays narrow and enforced everywhere, the same way PHP's object injection fix only worked because teams stopped calling unserialize() on untrusted input everywhere, not only in the one place a pentest happened to check. Architecture beats vigilance, but only in the parts of the architecture built correctly.


We recommend reading Unhackable and Bug-Free Coding Is a Marketing Lie next, since it makes the same argument this piece walked through above in a real exploit chain, minus the anime. Any vendor telling you their AI assistant is safe from this class of attack by design hasn't read the EchoLeak disclosure closely enough.
Unhackable and Bug Free Coding is a Marketing Lie
One thing that I have learned in my life is that everyone lies – to an extent. That even means people can make up little white lies to protect peoples’ feelings or hide the absolute truth. Some of the lies can be acts of commission – a deliberate statement of untruth – whereas

The Next Ghost Check

I've got a running list for where this column goes next: barrier systems and what they get right or wrong about network segmentation, the Section 9 model of human-machine teaming against what real incident response looks like, and the show's assumption that a sufficiently advanced intelligence would want personhood rather than mere persistence, which is a stranger and more current question than it sounds given how skeptical this site already is about handing AI tools more trust than they've earned. If you want a practical next step instead of more speculation, running your own local LLM stack at least puts the retrieval pipeline under your own roof, where you control what gets ingested instead of trusting someone else's classifier to catch what the Puppet Master would have gotten past on the first try.

Thirty-one years is a long time for a piece of speculative fiction to stay this accurate about a mechanism nobody had bothered to name yet. Whoever wrote that garbage collector's storyline understood something about trust and memory that the entire AI industry is still learning the hard way, one CVE at a time.