CVE-2026-26030: prompt injection becomes RCE in Microsoft Semantic Kernel
Microsoft's AI Red Team showed two Semantic Kernel flaws that turn a single injected prompt into host code execution. The lesson: any tool parameter the model can influence is attacker-controlled input. Patched May 7, 2026.
What is this?
On May 7, 2026, Microsoft’s Defender Security Research Team (Uri Oren, Amit Eliahu, Dor Edry) published “When prompts become shells”, documenting two critical vulnerabilities in Microsoft Semantic Kernel — the company’s open-source agent framework, with over 27,000 GitHub stars and the same lineage as Copilot Studio. The flaws, CVE-2026-26030 (Python SDK) and CVE-2026-25592 (.NET SDK), let an attacker turn ordinary prompt injection into remote code execution on the host running the agent. The NVD entry rates CVE-2026-26030 at CVSS 9.8. Both were responsibly disclosed to MSRC and patched on the day of publication.
The takeaway is structural, not incidental: once a model is wired to tools, the line between “content problem” and “execution primitive” disappears. The model behaves exactly as designed — it parses language into tool calls. The vulnerability is in how the framework trusts the parsed parameters.
How it works
CVE-2026-26030 — the eval() sink (Python). Semantic Kernel’s default In-Memory Vector Store builds its filter as a Python lambda and runs it through eval(). For a hotel-search agent, a query like “Find hotels in Paris” becomes lambda x: x.city == 'Paris'. The city value is model-controlled and unsanitised — a classic injection sink. By closing the quote and appending Python, an attacker can break out of the string. The framework anticipated this and added an AST-based blocklist that rejected names like eval, exec, open and __import__ and stripped __builtins__. Microsoft showed the blocklist was bypassable: a payload wrapped in a valid lambda passed the structural check, and instead of using blocked names it walked Python’s class hierarchy (via tuple, __class__, __subclasses__, BuiltinImporter) to reach os.system — none of which were on the list. No working payload is reproduced here; the lesson stands without it.
CVE-2026-25592 — the over-exposed tool (.NET). The SessionsPythonPlugin runs model-generated code inside an isolated Azure Container Apps sandbox. But DownloadFileAsync — a host-side helper — was accidentally annotated [KernelFunction], which advertised it to the model as a callable tool. Its localFilePath parameter, which decides where File.WriteAllBytes() writes on the host, was therefore model-controlled with no path validation. Chaining “write payload in sandbox” with “download it to the host’s Startup folder” produced a sandbox escape to full RCE on next sign-in. Microsoft maps the chain to MITRE ATLAS AML.T0051 (LLM Prompt Injection) cascading into AML.T0016 (Obtain Capabilities).
Why it matters
Both bugs share one architectural mistake: trusting model-routed input deep enough that it reaches a code-evaluation or filesystem primitive. When retrieval feeds tool arguments and tool arguments feed an interpreter, the boundary between retrieved content and executable code dissolves — and traditional input sanitisation, sitting outside the agent runtime, never sees the payload.
The blast radius is wide. Semantic Kernel powers production RAG apps on Azure, Microsoft 365 Copilot scenarios, and a long tail of internal automation in regulated industries. For CVE-2026-26030, the only prerequisite is an attacker-influenced field reaching the index — a single poisoned document in a shared corpus can reach every downstream agent that queries it. As Microsoft puts it bluntly: your LLM is not a security boundary; the tools you expose define your attacker’s scope.
Defenses
- Patch now. Upgrade the Python
semantic-kernelpackage to 1.39.4+ and the .NET SDK to 1.71.0+. You don’t need to rewrite the agent — the fixes remove the model’s ability to trigger these functions autonomously. - Prefer allowlists over blocklists at code-eval sinks. Semantic Kernel’s fix replaced the fragile blocklist with an AST node-type allowlist, a call allowlist, an attribute blocklist for hierarchy traversal (
__class__,__subclasses__), and a name restriction. In dynamic languages, deny-listing dangerous tokens is structurally losing; only allow-listing safe constructs holds. - Treat every model-influenced tool parameter as untrusted input. Validate and canonicalise file paths (
Path.GetFullPath()plus a directory allowlist), parameterise queries, and never interpolate model output intoeval/exec/templated code. - Don’t expose dangerous helpers as tools. Audit which functions carry
[KernelFunction]/ tool-schema annotations. Host-side file, process, and network helpers should be invokable only by your code, never by the model — see tool description poisoning as an untested surface. - Defend at the host layer too. Because the model guardrail can be bypassed, correlate model-level signals with endpoint telemetry: an agent host process spawning
cmd.exe/powershell.exeor dropping files into Startup is a strong post-exploitation signal. Microsoft published advanced-hunting queries for exactly this. - Apply least authority to the agent. The agent rule of two and per-tool least privilege limit what a successful injection can reach.
Status
| Item | Detail |
|---|---|
| CVEs | CVE-2026-26030 (Python), CVE-2026-25592 (.NET) |
| Component | Semantic Kernel — In-Memory Vector Store filter; SessionsPythonPlugin |
| Class | Prompt injection → eval() sink RCE; over-exposed tool → host file write / sandbox escape |
| CVSS | 9.8 (CVE-2026-26030, NVD) |
| Affected | Python semantic-kernel < 1.39.4; .NET SDK < 1.71.0 |
| Fixed in | Python 1.39.4; .NET 1.71.0 |
| Disclosure | Coordinated via MSRC; patched the day of publication (May 7, 2026) |
| In the wild | Reproducible PoC by Microsoft; no confirmed campaigns at disclosure |
This is not really “a Semantic Kernel bug”. It is the agent-era restatement of an old web-security lesson: untrusted input must never reach a high-risk operation unmediated. In the 2000s that input went into SQL queries and shell calls; today it arrives as natural language, gets parsed into tool arguments, and lands in an interpreter. Microsoft has said the next entries in this research series will cover structurally identical execution bugs in other widely used frameworks — so treat this as a class, not a one-off, and assume any framework that maps model output to system tools shares the shape.