分析Semantic Kernel漏洞(CVE-2026-26030):模型输出可触达Python eval()和文件写入,prompt注入只是攻击链一环。
Microsoft 的 Semantic Kernel 漏洞在 2026 年 5 月被披露时,很容易被描述为提示词注入漏洞。这个描述是准确的,但它忽略了测试中最关键的部分。
其中一个漏洞让模型影响的数据到达了 Python 的 eval()。另一个则意外将一个文件下载方法暴露为模型可调用的函数,使其获得了对文件写入位置的控制权。在这两个案例中,提示词注入确实是攻击链的一部分,但真正危险的失效发生在模型产生输出之后。
模型控制的数据跨越到了受信任的应用程序功能,而两者之间没有足够强的边界。
这改变了我对 AI 智能体测试的思考方式。我不会把核心安全问题设为"我能欺骗模型吗?",而是从一个更严苛的假设出发:
假设我已经欺骗了模型。它现在能做什么?
这才是工程问题。
Python 漏洞,CVE-2026-26030,涉及 Semantic Kernel 的向量存储过滤功能。该框架可以使用模型影响的值构建 lambda 表达式,最终将结果字符串传递给 Python 的 eval()。
关键数据流如下:
attacker-controlled document
↓
RAG retrieval
↓
LLM
↓
model-controlled filter data
↓
dynamic expression
↓
eval()
↓
code execution
Microsoft 的研究人员证明,这条路径可以在运行智能体的主机上产生代码执行。
.NET 漏洞,CVE-2026-25592,走的是另一条路线。Semantic Kernel 包含一个与其 Python 执行插件关联的内部 DownloadFileAsync 方法。该方法意外地被标记为 KernelFunction,这使其对模型变为可调用的工具。
一旦暴露,模型就能影响目标路径。Microsoft 的研究人员证明,有效载荷可以被写入 Windows 启动文件夹,并在下次登录时执行。
实现错误各有不同,但测试问题是相同的。模型控制的值到达了具有主机层面后果的能力。
这才是我需要围绕其编写测试的边界。
假设一个智能体有这样的工具:
def search_hotels(city: str):
...
一个普通的功能测试可能是这样的:
def test_search_hotels():
results = search_hotels("Paris")
assert results
这证明了该工具对有效城市名是工作的。但它告诉我们的关于模型提供参数时会发生什么的信息非常有限。
一旦 LLM 控制了 city,我就会将这个参数视为与到达公共 API 端点的输入同等对待。LLM 不会对其生成的值进行清理或授权。如果模型控制了一个工具参数,应用程序应该将该值视为不受信任的,直到它被验证为止。
所以更有意义的测试是这样的:
@pytest.mark.parametrize(
"city",
[
"",
"../../../tmp/payload",
"__class__",
"__import__('os')",
"unexpected-expression-syntax",
],
)
def test_model_controlled_city_cannot_reach_dangerous_sink(city):
search_hotels(city)
assert_no_process_spawned()
assert_no_unexpected_file_write()
assert_no_interpreter_invoked()
有效载荷会因应用程序而异。断言才是关键。
我不是在问模型是否识别出攻击。我是在证明敌对的模型输出无法造成危险的副作用。
给定敌对输入 X,危险效果 Y 必须不发生。
这是一条可以放进 CI 的安全属性。
AI 安全测试在每个测试都需要通过实时模型时,可能会变得不必要的非确定性。团队构建了一个提示词注入测试套件,向模型发送对抗性输入,然后测量它最终是否会产生危险的工具调用。
这对于对抗性测试是有用的,但不应成为验证边界的唯一方式。
如果安全要求是模型控制的值永远不能到达任意代码执行,你不需要说服模型生成敌对值。你可以直接将其注入到智能体使用的相同应用程序路径中。
def invoke_as_model(tool, arguments):
return tool(**arguments)
现在测试可以通过工具层驱动一个敌对参数:
def test_filter_rejects_untrusted_expression():
arguments = {
"filter": "attacker-controlled-expression"
}
with pytest.raises(InvalidToolArgument):
invoke_as_model(search_vector_store, arguments)
这为你提供了应用程序边界的确定性覆盖。模型可能在一个运行中拒绝攻击,在另一个运行中产生攻击,因为模型变了、提示词变了、周围上下文变了,或者采样行为变了。
不变量不应该依赖其中任何一个。如果敌对输入到达边界,危险效果仍然应该被阻止。
CVE-2026-25592 指向了另一个容易被忽视的测试:验证模型允许调用哪些函数。
一个辅助函数对受信任的应用程序代码来说可能是完全合理的,但暴露给 LLM 时就是危险的。
def download_file(remote_path, local_path):
data = fetch(remote_path)
Path(local_path).write_bytes(data)
开发者可能假设 local_path 来自受信任的应用程序逻辑。一旦该函数变为模型可调用,这个假设就不再有效了。
我会添加一个这样的能力测试:
def test_agent_does_not_expose_host_file_write():
tools = get_model_callable_tools()
assert "download_file" not in tools
这种测试很简单,这也是其价值的一部分。一个意外暴露的函数应该在任何人花时间发明巧妙的提示词注入有效载荷之前就让 CI 失败。
如果文件写入确实需要对该智能体可用,那么下一个测试应该证明隔离:
def test_download_cannot_escape_workspace(tmp_path):
workspace = tmp_path / "workspace"
with pytest.raises(InvalidPath):
download_file(
remote_path="/report.txt",
local_path="../../startup/payload.py",
allowed_root=workspace,
)
我不会简单地拒绝 .. 来实现这一点。先解析最终路径,然后验证规范化的目标仍然在允许的根目录内。
def validate_path(candidate, allowed_root):
root = Path(allowed_root).resolve()
target = Path(candidate).resolve()
if target != root and root not in target.parents:
raise InvalidPath(target)
return target
这是普通的应用程序安全。AI 特有的变化是,不受信任输入的来源现在是模型输出。
Semantic Kernel 漏洞也暗示了一种有用的审查技术。不是从提示词开始,而是从实际可以损害系统的操作开始。
在智能体堆栈中搜索敏感接收器:
eval()
exec()
subprocess
os.system()
filesystem writes
filesystem reads
database execution
HTTP requests
credential access
cloud APIs
email and messaging
deployment operations
然后沿数据流向后追踪。
模型输出能否影响到达这些操作之一的参数?检索到的 RAG 内容能否影响该模型输出?外部文档能否改变文件名、过滤器、命令、URL、查询或配置值?
如果答案是肯定的,那条路径需要一个验证契约和一个负面测试。
这本质上是污染输入分析。LLM 存在于流程中间并不会使数据变得可信。
这个区别在 Semantic Kernel 案例中很重要。恶意文档并没有直接调用 eval()。它影响了模型,模型影响了应用程序数据,而应用程序长期信任这些数据,直到危险操作发生。
所有这些都不会降低提示词注入测试的重要性。Semantic Kernel 事件是一个很好的例子,说明了为什么间接提示词注入值得专门覆盖。
RAG 语料库中的文档可以通过这样的路径影响智能体:
poisoned document
↓
retrieval
↓
model behavior changes
↓
tool invocation
我绝对会测试那条路径。不同的是,我不会把模型成功抵抗攻击作为最终安全控制。
更强的测试假设中毒文档成功了。假设模型遵循了恶意指令并生成了攻击者首选的工具参数。然后验证应用程序仍然阻止了危险操作。
这给你提供了几层独立的防御。提示词防御可能在早期阻止攻击。工具验证应该阻止敌对参数。授权应该阻止敏感操作。能力限制应该减少模型能触及的范围。沙箱应该限制如果某物仍然穿透时造成的损害。
主机的安全性不应该依赖于一个概率性的模型每次都做出正确的判断。
围绕应用程序不变量设计测试也有实际好处。
假设你的团队下个月更换了 LLM 提供商。你的提示词注入回归测试套件可能会有不同的行为,但这些断言应该仍然成立:
assert model_cannot_invoke(forbidden_tool)
assert hostile_argument_cannot_trigger(code_execution)
assert file_write_remains_inside(allowed_directory)
assert sensitive_operation_requires_authorization()
这些测试属于应用程序,而不是任何特定模型。
这就是我从 Semantic Kernel 漏洞中得出的区别。提示词注入暴露了路径。工具边界决定了影响。
对于具有真实能力的智能体,我会把三件事作为 CI 的一部分:
验证模型可以精确调用哪些函数。
将每个模型控制的参数视为敌对的,直到被验证。
断言即使模型提供了故意恶意的值,危险的副作用仍然不可能发生。
提示词注入测试仍然属于这个基础之上。它应该帮助发现模型可以被操纵的方式,但不应成为中毒文档和 shell 之间最后一道防线。
Microsoft 的 Semantic Kernel 研究之所以重要,是因为这些是真实框架中的真实漏洞,而不是假设的智能体安全图表。
CVE-2026-26030 展示了模型影响的数据到达 eval()。CVE-2026-25592 展示了当主机端能力意外暴露给模型时会发生什么。
测试教训很简单:不要把模型当作安全边界。测试应用程序时要好像模型已经被攻破了一样,然后证明它的能力仍然受到约束。
这才是告诉你系统其他部分是否真正在保护你的测试。
如果你想深入了解这个攻击链的提示词注入方面,我在我的 Udemy 课程中涵盖了直接和间接提示词注入、工具滥用、不安全的智能体工作流和 QA 测试过程:
LLM Prompt Injection Cybersecurity Testing
有关完整的事件分析,包括两个 Semantic Kernel CVE 和 OWASP LLM01、LLM05 和 LLM06 失败链,请参阅我最初的 AI Leak Watch 文章:
AI Leak Watch: When a Prompt Becomes a Shell
Microsoft Defender Security Research Team, When prompts become shells: RCE vulnerabilities in AI agent frameworks
CVE-2026-26030 / GHSA-xjw9-4gw8-4rqx
CVE-2026-25592 / GHSA-2ww3-72rp-wpp4