Prompts as shells: when prompt injection becomes RCE in agent frameworks
Two CVEs disclosed in Microsoft Semantic Kernel on May 7, 2026 (CVE-2026-25592, CVE-2026-26030) show how a single injected prompt can pivot from text to remote code execution on the agent's host.
What happened
On May 7, 2026, the Microsoft Security Response Center disclosed two critical vulnerabilities in the Semantic Kernel agent framework: CVE-2026-25592 (CVSS 10.0) in the .NET SDK and CVE-2026-26030 (CVSS 9.8) in the Python SDK. Both turn a textual prompt injection — the model receiving attacker-controlled instructions through untrusted content — into remote code execution on the host running the agent. Patches landed the same day in semantic-kernel Python 1.39.4 and .NET 1.71.0.
The companion research post, When prompts become shells, frames a broader pattern: agent frameworks expose tools, sandboxes and helper functions to the model, and any helper that touches a code-execution sink (eval, exec, deserialization, file write, shell) becomes reachable by a single well-crafted prompt.
How it works
Agents work by giving the LLM a registry of callable tools. The model decides which to invoke and supplies arguments. Two distinct bugs, same shape:
CVE-2026-26030 — Python eval() in the in-memory vector store. The default InMemoryVectorStore built filter expressions by passing attacker-influenced strings into a Python lambda compiled with eval(). An LLM tricked through an injected prompt could ask the store to filter records using a payload that executes arbitrary Python.
# Conceptual sketch — DO NOT run.
# A field controlled by untrusted retrieved content reaches eval().
filter_expr = f"lambda r: r.score > {model_supplied_value}"
predicate = eval(filter_expr) # [REDACTED]: payload runs in host process
CVE-2026-25592 — [KernelFunction] exposed DownloadFileAsync in the .NET sandbox. The SessionsPythonPlugin runs model-generated code inside an Azure Container Apps dynamic session. An internal helper, DownloadFileAsync, was accidentally annotated [KernelFunction] and registered as a callable tool with no path validation. A prompt could ask the agent to “download” a remote file to an arbitrary local path, writing attacker-controlled content outside the sandbox and onto the host.
In both cases the entry point is text. No memory corruption, no browser exploit. The model simply interprets the request, picks a tool, and hands parameters to a code-execution sink.
Why this matters
This is not a Semantic Kernel-specific story. The same pattern has surfaced repeatedly in 2026 across agent frameworks (PraisonAI, Flowise, LMDeploy, mcp-remote and others — see the Adversa AI tracker). The structural cause is the same everywhere:
- The framework exposes a tool registry to the LLM.
- At least one tool reaches an unsafe sink (
eval,exec, pickle deserialization, path write, shell). - The agent processes untrusted text — retrieved documents, web pages, tool results — alongside its own instructions.
- There is no enforced boundary between data and instructions.
This is exactly the lethal trifecta described by Simon Willison in June 2025 — private data access, untrusted-content exposure, and external action — and the Rule of Two proposed by Meta’s AI Security team on October 31, 2025: an agent should hold at most two of those three properties per session. When all three coexist, deterministic safety collapses, and a textual prompt becomes a shell.
Defenses
Mitigations are architectural, not heuristic — prompt filtering alone is insufficient.
- Patch first. Upgrade Semantic Kernel to Python
>=1.39.4and .NET>=1.71.0. Audit any agent framework you operate against the Adversa monthly tracker. - Eliminate
eval/execon model-supplied strings. Replace with structured parsers (AST allowlists, typed filter DSLs). Microsoft’s fix layered four checks: AST node-type allowlist, function-call allowlist, dangerous-attribute blocklist, name-node restriction. - Audit your tool registry. Every
[KernelFunction]/@tool/ OpenAPI-exposed endpoint is reachable by the model. Treat the registry as a public API: minimal surface, validated parameters, no helper functions exposed by accident. - Sandbox the executor, not the prompt. Run any model-driven code in a container or microVM with no network, no host filesystem write, no credentials beyond the task.
- Apply the Rule of Two. For each agent session, decide which two of {untrusted input, sensitive data, external action} you accept — and enforce the cut at the architecture level.
- Log tool calls with full arguments. Make post-hoc detection possible; raw model output is not enough.
Status
| Framework | CVE | CVSS | Disclosure | Patched in |
|---|---|---|---|---|
| Semantic Kernel (.NET) | CVE-2026-25592 | 10.0 | 2026-05-07 | 1.71.0 |
| Semantic Kernel (Python) | CVE-2026-26030 | 9.8 | 2026-05-07 | 1.39.4 |
| PraisonAI | CVE-2026-44338 | 9.x | 2026-04 | latest |
| Flowise MCP adapter | CVE-2026-40933 | 9.x | 2026-04 | latest |
If you ship an agent in production, the question is no longer whether prompt injection will reach your tool layer — it is which sink it lands on. Cut the sinks.