When a prompt becomes a shell: what Microsoft found in agent frameworks
by detektd team

On May 7, 2026, the Microsoft Defender Security Research Team published "When Prompts Become Shells", an analysis of how prompt injection can turn into remote code execution in the most widely used agent frameworks: LangChain, AutoGen and Semantic Kernel. For the latter, built by Microsoft itself, the team disclosed two critical vulnerabilities, CVE-2026-25592 and CVE-2026-26030, since fixed.
Microsoft's demonstration is deliberately understated: a single instruction, slipped into the content the agent processed, was enough to launch a program on the machine running it. No browser exploit, no booby-trapped attachment, no memory corruption. Just text, read by an agent that had the means to act.
How an agent framework works
An agent framework connects a language model to the rest of the world. The developer declares tools (Semantic Kernel calls them "plugins", LangChain "tools", AutoGen ships code executors), and at each step the model decides which one to call and with what arguments. The framework then actually carries out the call: read a file, query a database, run code.
The whole security of that model hinges on one point: the arguments the model passes to tools must be treated as untrusted input. Because the model doesn't come up with them on its own: it derives them from everything it has read, including documents, emails, web pages or database rows an attacker may have written. If the framework trusts those arguments, whoever controls the text controls the tool.
A change in kind
That's the heart of Microsoft's analysis. As long as a model only produces text, prompt injection stays a content problem: a wrong answer, a hijacked tone, a leaked piece of information. The moment it's wired to tools (code execution, filesystem, network requests), the same injection becomes an execution problem. Severity no longer depends on the model, but on what its tools are allowed to do.
The problem isn't new. As early as 2023, LangChain fixed CVE-2023-29374: its LLMMathChain, designed to solve calculations, ran the expression produced by the model as Python. A well-phrased question was enough to turn a calculation into arbitrary code. Three years later, frameworks have grown more sophisticated, but the trust boundary has stayed in the same fragile place: between what the model decides and what the machine executes.
An epidemic, not an incident
Microsoft's write-up isn't isolated. Adversa AI's monthly tracker lists six confirmed code-execution or sandbox-escape disclosures in agentic frameworks for May 2026 alone. Several research teams now call it an epidemic. The explanation is structural: every new framework reinvents its tool registry, and with it its own trust boundary, often in the wrong place, because the first goal is for the agent to "get things done", not to be stopped.
code-execution or sandbox-escape disclosures in agent frameworks, in May 2026 alone (Adversa AI)
For teams building these frameworks into a product, the consequence is concrete. A support agent reading customer tickets, an assistant summarizing emails, an internal tool analyzing documents: all of them read content written by third parties. If any of them has, even indirectly, a tool that can run code or write files, every ticket, every email, every document becomes a potential vector.
What actually protects you
Since the model will eventually read a malicious instruction, defense focuses on what happens next. Four principles do most of the work:
- Update the framework. Both Semantic Kernel CVEs are fixed; vulnerable versions never will be.
- Give the agent the bare minimum of tools. An agent that summarizes emails has no reason to hold a shell, or to write files.
- Isolate any code execution: throwaway container, read-only filesystem, no secrets, no outbound network by default.
- Treat retrieved content like user input, and require human approval for irreversible actions (payment, deletion, sending).
For the third point, most frameworks offer a Docker-based executor rather than running directly on the host; AutoGen, for instance, ships both. Whatever the tool, the container should be locked down by default. Standard Docker options are enough to cover the essentials:
docker run --rm \ --network none \ --read-only --tmpfs /tmp \ --cap-drop ALL --security-opt no-new-privileges \ --pids-limit 128 --memory 512m --cpus 1 \ -v "$PWD/job:/job:ro" \ python:3.12-slim python /job/task.py
The environment variable trap
An isolated container that inherits the application's environment variables (API keys, database URLs) isn't isolated. Pass explicitly what the task needs, and nothing else.
Why a Python sandbox isn't one
Many code-execution tools start with an "application-level" sandbox attempt: run the generated code in the same process, removing some dangerous functions or filtering the syntax tree. Python's history shows this approach doesn't hold. The language exposes too many introspection mechanisms (every object leads to its class, every class to its subclasses) for a denylist to cover every path to a dangerous function. The 2023 LangChain CVE is a direct example.
So the boundary has to be at the operating system level, not the language. In increasing order of isolation: a container with reduced capabilities and a seccomp profile, an application kernel like gVisor that intercepts system calls, or a Firecracker-style micro-VM, designed to run untrusted code from multiple tenants on the same host. The choice depends on how much you trust the content the agent reads, and for an agent exposed to the web, that trust is zero.
The metadata service, a prime target
On a cloud instance, the address 169.254.169.254 serves the metadata service, which among other things hands out temporary credentials for the role attached to the machine. It's the first target of a hijacked HTTP tool: the arXiv study on exposed MCP servers explicitly cites SSRF aimed at this service. On AWS, enforcing IMDSv2 requires a token obtained through a prior PUT request, and limiting the hop count to 1 stops the instance's containers from reaching it:
aws ec2 modify-instance-metadata-options \ --instance-id i-0123456789abcdef0 \ --http-tokens required \ --http-put-response-hop-limit 1
Exfiltration through rendering
An agent with no network tool at all can still send data out, through its answer itself. If the interface renders the model's output as Markdown, an image whose URL contains data (a key, a snippet of conversation) is loaded automatically by the user's browser. The attacker's server receives the data in its logs, without anyone clicking. The defense sits in the interface: don't render external images in model output, or restrict their domains through the Content-Security-Policy (img-src directive).
Finally, logging every tool call (name, arguments, the content that triggered it, result) prevents nothing, but it's the only way to reconstruct afterwards what an agent did, and why. A call to an execution tool right after reading an external document is exactly the signal to watch for.
What we check from the outside
The part of this risk visible from the internet is exposed agent endpoints. When detektd finds an MCP (Model Context Protocol) server reachable without authentication, the scan lists the tools it exposes, read-only, without ever calling them. Severity depends on what those tools allow: a server exposing a shell, file access, SQL queries or arbitrary HTTP calls to any visitor is rated high: that's exactly the situation Microsoft describes, without even needing a prompt injection.

21,000 MCP servers exposed on the internet: why authentication is "optional"
Censys counted more than 12,500 internet-reachable MCP servers in April, then more than 21,000 in May. A study published in late July audited 414 of them: 91.8% had no OAuth authentication. How the protocol works, and how things got here.

Prompt injection isn't a bug you patch once
Researcher RyotaK ended up cataloguing around fifty separate ways to bypass Claude Code's permission system. The most critical one let an attacker steal CI/CD secrets in January 2026, starting from a single GitHub issue.