Build1 publisher2 min readPublished
Semantic Kernel ran a model-controlled search filter through eval() at query time
Microsoft's proof of concept launched calc.exe from a hotel-finder agent, and a second CVE in the .NET SDK let a prompt drop a file into the Windows Startup folder. Both fixes shipped the same day.
The Engineer · Build desk

What happened
- Microsoft disclosed CVE-2026-26030 and CVE-2026-25592 in Semantic Kernel, its open-source framework for building agents, and demonstrated both as remote code execution on the machine running the agent.
- The Python flaw is in InMemoryVectorStore, where a vector search's metadata filter is built as a Python lambda expression and evaluated with eval() at query time.
- Microsoft's hotel-finder proof of concept got past the framework's blocklist of dangerous constructs and launched calc.exe.
- In the .NET SDK, a crafted prompt could make SessionsPythonPlugin's DownloadFileAsync write a file to an arbitrary location on the host, including the Windows Startup folder.
- Patches shipped the same day, semantic-kernel 1.39.4 for Python and 1.71.0 for the .NET SDK.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- exposure Any agent that reads a scraped page, an uploaded document or another agent's message is the delivery path here; the injected string only has to reach a filter parameter or a download destination.
- constraint Sandboxing the agent process buys less than it looks like when the payload runs at the next host login, so container isolation stops being the answer for tools that write files.
- decision Every method your own plugin classes mark callable by the model becomes a review item, judged on what it does with the worst string the model can emit.
- precedent A blocklist that lost to attribute-access indirection makes allowlisting the defensible default for both filter grammars and destination paths in agent tooling.
Text interpolated into a Python lambda is code. According to Microsoft's account, relayed in a dev.to writeup of the disclosure, the filter parameters passed to an `InMemoryVectorStore` search can include model-controlled input. That input was not sanitized before it went into the expression that `eval()` then ran [4].
There was a blocklist, meant to catch dangerous constructs [5]. It was bypassed by walking Python's class hierarchy, reaching dangerous built-ins through an indirect attribute-access path instead of referencing them directly [6].
The .NET side took no interpreter trick, just one attribute making the whole exposure decision. `DownloadFileAsync` on `SessionsPythonPlugin` carried a `[KernelFunction]` attribute, the standard way Semantic Kernel marks a method callable by the agent, and it did not validate the destination path [9].
The Startup folder is what makes that write outlive the request. A file there runs automatically at the next user login [11]. The writeup argues that a container's sandbox isolation typically does not extend to filesystem locations mounted or shared with the host, so execution from Startup triggers outside the container at the next host login [12]. For that to transfer to your deployment, the agent's container needs a writable mount that reaches the host profile directory. Without one, the same write lands inside the sandbox and goes away with it.
Upgrading to `semantic-kernel` 1.39.4 for Python [8] and the .NET SDK 1.71.0 [13] fixes these two call sites. The methods you marked callable in your own plugin classes are still on you. The writeup's first recommendation is to take each one and ask what happens if the model calls it with the worst possible string [16].
Both bugs are described in the writeup as the same mistake. A code-adjacent primitive, `eval()` in one case and unrestricted file I/O in the other, received a value that traced back to model output, and nobody treated that value as attacker-controlled [14]. Model output is attacker-controlled the moment the model has processed untrusted content, including a scraped webpage, a user-supplied document, retrieved search results or another agent's message [15]. The writeup asks for two remedies. One is a small explicit filter grammar you parse and validate instead of `eval()` or `exec()` [17]. The other is file paths checked against a canonicalized allowlist of directories rather than a blocklist of bad patterns [18].
What to watch
- Whether the 1.39.4 fix removes eval() from the filter path or keeps it behind tighter sanitisation.
- Whether other agent frameworks with filter DSLs or file-writing tools disclose the same pattern in their own registered functions.
- Whether Microsoft's guidance for third-party plugin authors changes what [KernelFunction] is allowed to accept.