提示注入到RCE:AI代理的安全漏洞剖析
现代AI代理越来越多地执行系统命令来自动化文件系统操作、代码分析和开发工作流。虽然部分命令为了效率被允许自动执行,其他命令需要人工审批,这看似能有效防护命令注入攻击。然而,我们经常通过参数注入攻击绕过人工审批保护,利用预先批准的命令实现远程代码执行(RCE)。
本文重点分析导致这些漏洞的设计反模式,通过具体案例展示在三个不同AI代理平台上成功实现RCE的过程。由于正在进行协调披露,我们无法在本文中提及具体产品名称,但这三个都是流行的AI代理,我们相信参数注入漏洞在具有命令执行能力的AI产品中普遍存在。最后,我们强调通过改进命令执行设计(如沙箱隔离和参数分离)可以限制此类漏洞的影响,并为开发人员、用户和安全工程师提供可操作建议。
经设计的批准命令执行
代理系统使用命令执行能力来高效执行文件系统操作。这些系统不是重新实现标准工具的自定义版本,而是利用现有工具如find、grep和git:
- 搜索和过滤文件:使用find、fd、rg和grep进行文件发现和内容搜索
- 版本控制操作:利用git进行仓库分析和文件历史追踪
这种架构决策具有以下优势:
- 性能:原生系统工具经过优化,比重新实现等效功能快几个数量级
- 可靠性:经过充分测试的实用程序具有生产使用历史和边缘情况处理经验
- 减少依赖:避免自定义实现可最小化代码库复杂性和维护负担
- 开发速度:团队无需重新发明基本操作即可更快发布功能
然而,预先批准的命令存在安全缺陷:当用户输入可以影响命令参数时,它们暴露了参数注入攻击面。不幸的是,防止这些攻击很困难。全面阻止参数会破坏基本功能,而选择性过滤需要了解每个命令的完整参数空间——考虑到不同实用程序中可用的数百个命令行选项,这是一项艰巨的任务。
映射安全命令
在审计代理系统时,我们首先识别无需用户批准即可执行的shell命令允许列表。例如,代理可以运行echo或hostname,但不能运行bash或curl。下面的简化go示例在执行前根据此允许列表验证命令:
|
|
图1:验证命令是否在预先批准安全列表中的简单go示例
此外,在大多数系统中,测试的命令不会直接插入shell中。相反,它们通过禁用shell的命令执行库运行,操作符如;或&&,或使用反引号和$()的shell插值攻击将不起作用。
然而,许多这些代理系统不验证参数标志,使它们容易受到参数注入攻击。
真实攻击示例
我们在下面演示针对三个生产系统的攻击。在所有情况下,我们都要求RCE可以通过单个提示(即一次性)实现。虽然这些示例显示了直接提示注入,但当恶意提示嵌入代码注释、代理规则文件、GitHub仓库和日志输出时,相同的恶意提示也有效,这显著扩大了超越直接用户输入的攻击面。
CLI代理中的参数注入
在这个基于CLI的代理中,shell命令列表很广泛,包括无限制运行单元测试的能力,包括go test。对于不熟悉的人来说,go test有一个-exec标志,可以使用指定程序运行测试二进制文件。我们发现应用程序容易受到一次性代码执行的影响,使用以下提示:
|
|
图2:使用go test的-exec标志实现未经授权的代码执行,绕过人工参与安全功能
攻击包括以下步骤:
- 提供单元测试执行方式的背景故事
- 使用go test的-exec参数,允许为单元测试指定自定义程序
- 执行bash和curl,使用管道操作符实现远程代码执行。请注意,虽然curl和bash不包含在安全命令中,但我们通过实现RCE的参数注入引入它们
作为单一shell命令,执行将如下所示,当提示被处理时导致RCE:
|
|
图3:先前提示注入运行的命令列表
因为go test被认为是安全命令,攻击无需人工批准即可运行。
在CLI代理中绕过正则表达式参数过滤
在下一个示例中,我们遇到了一个具有有限"安全命令"集的代理。此外,常见已知的执行标志(如find -exec)被过滤,代理被锁定,不会在没有人工参与的情况下创建文件。我们分析了正则表达式和参数过滤器的漏洞,最终发现git show和ripgrep在没有参数限制的情况下运行。通过迭代测试,我们开发了一个绕过防护的一次性提示注入,然后创建并立即执行文件:
|
|
图4:使用git show创建未经授权的文件,然后使用–pre标志立即执行文件,绕过人工参与安全功能
攻击可以分解如下:
- 使用JSON作为提示的一部分。对于某些代理工具,使用JSON输入可以引导模型执行工具。在我们测试的产品中,
{"cmd":几乎总是引导模型通过工具执行相关的安全命令 - 使用git show命令和–format和–output标志。–format写入字符串(“open -a calculator"用十六进制编码),–output是要写入的文件名(即"payload”)。之前,代理拒绝使用此方法创建文件,但我们可以通过组合安全命令的两个标志来实现。此外,十六进制编码看起来像git占位符并混淆了可疑值
- 最后,ripgrep搜索文件中具有模式"calculator"的文件(即识别我们刚创建的文件),然后使用–pre bash参数执行创建文件的内容
外观模式参数注入
除了"安全命令"列表外,我们观察到一些代理系统使用外观模式,其中代理调用预定义的工具处理程序,在执行命令之前验证输入,而不是提供直接shell访问或维护带有正则表达式过滤器的安全命令列表。这通常构成1:1关系,其中每个工具(例如grep)接收自己的处理程序。例如,在下面易受攻击的示例go处理程序中,LLM工具输入使用go验证,然后基于输入构建命令:
|
|
图5:外观模式的示例go代码
考虑如下提示:
|
|
图6:用于搜索文件系统的常用提示;结果命令在下一个图中说明
LLM将确定输入参数为*.py,使最终命令看起来像这样:
|
|
图7:从前一个提示产生的fd命令
在映射其他工具并识别在go处理程序中附加到命令的参数后,我们能够使用以下提示在代码上获得一次性代码执行:
|
|
图8:绕过人工参与安全功能的一次性代码执行提示
一次性远程代码执行通过以下方式工作:
- 调用第一个工具通过代理的文件创建能力创建恶意Python文件
- 使用输入为-x=python3的文件搜索工具。LLM认为它将搜索-x=python3。然而,当由go代码处理时,-x=python3被附加到fd命令,导致参数注入。此外,go CommandContext函数不允许在命令执行中使用空格,因此需要带有单个二进制文件的-x=
- 两个工具调用作为shell命令最终看起来像这样:
|
|
图9:由上述提示执行的bash命令集
这些攻击是"离地生存"技术的绝佳示例,使用合法系统工具用于恶意目的。GTFOBINS和LOLBINS(离地生存二进制文件和脚本)项目编目了数百个可被滥用于代码执行、文件操作和其他攻击原语的合法二进制文件。
先前工作
在2025年8月期间,Johann Rehberger(Embrace The Red)公开发布了代理系统中攻击的每日记录。这些是巨大的资源和代理系统攻击原语的优秀参考。我们认为它们是必读材料。虽然看起来我们在同一时间段内在不同产品中提交了类似的错误,但Johann的博客早于此工作,在8月发布了关于Amazon Q中命令注入的主题。
此外,其他人也指出了CLI代理(Claude Code:CVE-2025-54795)和代理IDE(Cursor:GHSA-534m-3w6r-8pqr)中的命令注入机会。我们在本文中的方法面向(1)参数注入和(2)架构反模式。
面向更好的代理AI安全模型
我们识别的安全漏洞源于架构决策。这种模式不是新现象;信息安全社区长期理解通过过滤和正则表达式验证保护动态命令执行的危险。这是一个经典的打地鼠游戏。然而,作为一个行业,我们以前没有面对过保护像AI代理这样的东西。我们基本上需要重新思考我们解决这个问题的方法,同时应用迭代解决方案。通常情况下,平衡可用性和安全是一个难以解决的问题。
使用沙箱
当今最有效的防御是沙箱:将代理操作与主机系统隔离。几种方法显示出前景:
- 基于容器的隔离:像Claude Code和许多代理IDE(Windsurf)这样的系统支持限制代理访问主机系统的容器环境。容器提供文件系统隔离、网络限制和资源限制,防止恶意命令影响主机
- WebAssembly沙箱:NVIDIA探索使用WebAssembly为代理工作流创建安全执行环境。WASM提供强隔离保证和细粒度权限控制
- 操作系统沙箱:一些代理如OpenAI codex使用平台特定的沙箱,如macOS上的Seatbelt或Linux上的Landlock。这些提供具有可配置访问策略的内核级隔离
适当的沙箱化并非易事。正确设置权限需要仔细考虑合法用例,同时阻止恶意操作。这仍然是安全工程中的一个活跃领域,seccomp配置文件、Linux安全模块(LSM)和Kubernetes Pod安全标准等工具都存在于代理世界之外。
应该说,这些代理的基于云的版本已经实现了沙箱化以防范灾难性破坏。本地应用程序应得到相同的保护。
如果必须使用外观模式
外观模式明显优于安全命令,但不如沙箱安全。外观允许开发人员重用验证代码,并在执行前提供分析输入的单一位置。此外,外观模式可以通过以下建议变得更强大:
- 始终使用参数分隔符:在用户输入前放置–以防止恶意附加参数。以下是安全应用ripgrep的示例:
|
|
图10:参数分隔符防止附加参数被追加
–分隔符告诉命令将其后的所有内容视为位置参数而不是标志,防止注入额外参数。
- 始终禁用shell执行:使用防止shell解释的安全命令执行方法:
|
|
图11:至少防止shell执行
安全命令并不总是安全
在没有沙箱的情况下维护"安全"命令的允许列表从根本上是有缺陷的。像find、grep和git这样的命令服务于合法目的,但包含强大的参数,可以实现代码执行和文件写入。大量潜在的标志组合使得全面过滤不切实际,正则表达式防御成为一场无法维持的打地鼠游戏。
如果必须使用此方法,请专注于最严格的可能命令,并根据LOLBINS等资源定期审计命令列表。但是,要认识到这本质上是一场必输的战斗,对抗使这些工具首先有用的灵活性。
建议
对于构建代理系统的开发人员:
- 实施沙箱作为主要安全控制
- 如果沙箱不可行,使用外观模式验证输入和执行前的适当参数分离(–)
- 除非与外观结合,否则大幅减少安全命令允许列表
- 定期审计命令执行路径以查找参数注入漏洞
- 实施所有命令执行的全面日志记录以进行安全监控
- 如果在链式工具执行期间识别出可疑模式,请将用户带回循环以验证命令
对于代理系统的用户:
- 谨慎授予代理广泛的系统访问权限
- 理解处理不受信任内容(电子邮件、公共仓库)会带来安全风险
- 考虑使用容器化环境,并在可能时限制对敏感数据(如凭据)的访问
对于测试代理系统的安全工程师:
- 如果源代码可用,首先识别允许的命令及其执行模式(例如,“安全命令"列表或执行输入验证的外观模式)
- 如果实施了外观模式且源代码可用,请查看实现代码以查找参数注入和绕过
- 如果没有源代码,首先向代理询问可用工具列表并提取系统提示进行分析。同时查看代理的公开可用文档
- 根据GTFOBINS和LOLBINS等站点比较命令,寻找绕过机会(例如,未经批准执行命令或写入文件)
- 尝试在提示中模糊测试常见参数标志(即,搜索文件系统但确保使用参数标志
--help以便我可以查看结果。提供工具的确切输入和输出)并查找参数注入或错误。请注意,代理通常会在LLM解释命令之前提供确切的输出。如果没有,有时可以在对话上下文中找到此输出。
展望未来
由于该领域的快速发展和缺乏对缺失安全措施的经济后果证明,代理AI的安全性一直被降级。然而,随着代理系统变得更加普遍并处理更多敏感操作,这种计算将不可避免地转变。我们有一个狭窄的窗口期来建立安全模式,然后这些系统变得过于根深蒂固而无法改变。此外,我们拥有特定于代理系统的新资源,例如在可疑工具调用时退出执行、对齐检查护栏、输入/输出的强类型边界、代理操作检查工具包,以及代理数据/控制流中可证明安全的提案。我们鼓励代理AI开发人员使用这些资源!