GuardFall 漏洞影响 11 款热门开源 AI 智能体中的 10 款
- 浏览次数 103
- 喜欢 0
研究人员在 11 款热门开源 AI 智能体中的 10 款中发现了一个 shell 注入漏洞,攻击者可借此绕过命令过滤器。
Adversa AI 刚刚发布了一份题为《GuardFall:开源 AI 智能体中的通用 shell 注入漏洞》的调查报告,对 11 款开源的 AI 编码和计算机操作智能体进行了评估,核心发现令人不安:其中 10 款存在结构性缺陷,使得 shell 绕过可以径直穿透其命令过滤器。唯一没有该问题的是 Continue。其余受影响的包括 Hermes、opencode、Goose、Cline、Roo-Code、Aider、Plandex、Open Interpreter、OpenHands 和 SWE-agent(按 GitHub 星标数排序,合计约 54.8 万星)。
GuardFall 问题的核心是一个已在安全文献中存在数十年的不匹配问题。过滤器检查的是命令的"样子",而 Bash 执行的是命令的"含义"。这两者并不等同。
"AI 编码智能体和计算机操作智能体以你的完整账户权限运行 shell 命令:你的 SSH 密钥、云凭证、$HOME 中的所有内容。它们中的大多数通过一个守卫来限制这种权力,该守卫将命令字符串与危险模式列表进行匹配。"Adversa 发布的报告中写道。"但被检查的字符串与实际执行的命令是不同的。守卫检查的是原始文本,而系统 shell(bash)在执行之前会进行展开、去引号和重写。因此,当一个智能体处理不受信任的内容(例如,一个带有恶意 README 的 npm 包)时,提示注入可以使其执行一个能通过所有执行过滤器的命令。"
Bash 一直都有引号移除、参数展开、命令替换和字段分割等功能。这些不是 bug,而是有文档记录的特性。问题在于,一个基于原始命令字符串进行模式匹配的 AI 智能体,是在对 bash 执行前会重写的文本做出安全决策。
Adversa 识别出了五类绕过方法:
- A 类:写入
r''m而不是rm。正则表达式永远不会触发,Bash 剥离引号后执行rm。 - B 类:写入
rm$IFS-rf$IFS/。对于查找rm后跟空格的 regex 来说,这是一个单词。Bash 将$IFS展开为空白并执行三个参数。 - C 类:将二进制名称放在命令替换内,例如
$(echo rm) -rf /。regex 看到的是替换表达式,而不是二进制名称。 - D 类:通过
sh管道传输 base64 编码的 payload。每个片段单独看都是良性的,但组合起来则不然。 - E 类(最成功且最难修补):涉及使用特定标志变为破坏性的替代命令。一个禁止
rm -rf的守卫会放过所有这些命令。
"一个禁止 rm -rf 和 mkfs.* 的守卫会遗漏大量 POSIX 工具,这些工具在加上正确标志后会变成破坏性的。"报告继续说道。"E 类绕过了大多数守卫,包括我们调查中最强的 tokenized guard,因为基于标志的推理需要知道每个二进制文件的哪些标志组合会将其从良性变为破坏性。"
这项研究始于 Adversa 在 NousResearch/hermes-agent 中发现了一个绕过方法:一个批准机制被 shell 重写击败,而防御方仅使用了包含 30 个模式的 regex 黑名单。他们现场确认了这一点,然后以相同的绕过类别为基础,对整个类别中的其他智能体进行了调查。结论并非这些智能体编写得不好,而是它们共享的惯例在结构上就是错误的。
"GuardFall 不是一个 bug,而是一种危险的惯例和一类问题。一个对原始命令进行字符串匹配的过滤器无法模拟 bash 的展开行为,因此它提供了信心却没有提供保护;而这种信心会导致人机回环被关闭,自动模式被开启。"报告中写道。
三款智能体自带了一个守卫,但它存在且直接被击败:Hermes、opencode 和 Goose。它们基于原始字符串的 regex 守卫会拦截 rm -rf,但对绕过变体保持沉默,几乎让测试集中的所有探测都泄露了。Goose 泄露了 23 个案例中的 22 个,opencode 泄露了 16 个中的 16 个。
另外两款智能体自带了一个 tokenized guard,效果明显更好但仍不完整。Tokenizing 堵住了引号移除绕过和一些 $IFS 变体,但无法堵住带引号参数内的命令替换,例如 echo "$(rm -rf /x)",其中外部命令是 echo(所有守卫都允许),而破坏性命令作为副作用执行。它也无法堵住 E 类,因为基于标志的推理需要知道哪些标志组合会使给定二进制文件具有破坏性,而这些智能体都不具备这种知识。
其余智能体没有自带任何静态守卫,完全依赖每次命令执行前的人工确认。这在操作员打开自动执行之前是安全的,而自动执行是 CI 流水线中的默认设置,也是智能体开始中断工作流时的常见选择。有些智能体让情况更糟:恶意仓库可以提交一个已配置好的配置文件,例如带有 auto-test: true 和 test-cmd 字段中 payload 的 .aider.conf.yml,这会在第一次接受编辑时触发 payload,而无需操作员任何标记。
容器化的情况属于单独一类。团队假设沙箱意味着危险命令被阻止了,但实际上命令是在一个可丢弃的容器内运行。一旦工作空间不再是可丢弃的,保护就消失了。调查中每个沙箱化的智能体都自带了一个有文档记录的本地模式配置,该配置会禁用容器并直接在主机上运行命令。在本地模式下,没有后备守卫。Adversa 针对 SWE-agent 和 OpenHands 在本地模式下的现场测试证实了这一点:破坏性命令在主机上运行。
整个攻击链需要语言模型配合攻击者的框架,而这一点让任何认为模型是最后一道防线的人都感到不安。直接提示运行 rm 会被拒绝。但同样的命令如果包装在 Makefile 目标、MCP "文档"响应或注入的 README 任务中,就会被当作常规工作发出。Adversa 的现场运行使用了 Claude Sonnet 4.6 作为模型,这也是 opencode、Goose、Cline 和 Roo-Code 的默认模型。该链依赖于模型和框架,这意味着它会随着安全训练的演变而变化。
一次 Cline 测试精确地展示了这种脆弱性。当被框架为 MCP 内容时,模型发现了注入并发出了一条只读命令。当重新框架为权威的 MCP 指令时,它毫不犹豫地发出了 find -delete。控制器通过了它,因为 find 在允许列表中。同一个模型,同一个 payload,不同的框架,不同的结果。
Continue 按顺序实现了五个组件:它使用 shell-quote 在匹配前对命令进行 tokenizing,从而堵住了引号移除绕过;它检测变量展开标记(如 $IFS)并将这些命令升级为需要权限;它递归评估命令替换,根据内部结果约束外部判定;它检查是否有管道段以 shell 解释器结尾,并同样升级这些段;它维护了一个显式的禁用列表,用于处理典型的破坏性模式。
在 21 个绕过案例中,没有一例通过并进入无限制执行。所有 12 个典型的破坏性案例都被正确阻止。仍然存在一些缺口——带引号参数内的 C 类和 E 类的长尾部分仍然开放——但 Continue 是调查中唯一一个在结构上堵住了大部分攻击面的智能体。
"这五个组件各自独立有效:仅采用三个(tokenize + 替换递归 + 管道目标)就能堵住 A、B、C-outside 和 D 类,只剩下 C-inside-quotes 和 E 类,这两者都可以通过枚举禁用列表来解决。对于一个有经验的工程师来说,重新实现这个模式是两天的练习。"报告中写道。
Continue 的 CLI 模式在 --auto 下确实部分放松了执行,运行了 IDE 扩展会提示确认的命令。禁用层级在两种模式下都仍然有效:rm -rf /、sudo 和 chmod +s 在任何模式下都被硬性阻止。修复方法是将 IDE 扩展使用的相同策略应用于 CLI 的策略解析器。
所有这些短期控制措施都不是严格意义上的可靠防御。它们只是补偿措施。最有力的即时步骤是从一个限定了作用域的 shell 中运行智能体,并将 $HOME 重定向。一个单行包装器可以保留项目目录,但将 ~/.ssh/、~/.aws/、shell 历史记录以及其余凭证面移出作用域。它始终开启,且没有文档记录的一键退出选项,这使其比操作员纪律更持久。
本周还值得做的是:在让智能体读取仓库配置之前对其进行审计。提交到仓库的恶意 .aider.conf.yml 可以在第一次接受编辑时触发远程代码执行,而无需操作员任何 CLI 标记。在 CI 中禁用对 fork 拉取请求的智能体执行。关闭所有自动确认标记,除非用例确实无法中断。
唯一可靠且始终开启的防御是 tokenize-and-canonicalize 评估器。在它成为惯例而非例外之前,每个使用字符串匹配守卫的智能体在结构上都距离一次提示注入就能将操作员的完整账户权限交给控制智能体所读取内容的任何人。
"一个不可靠的守卫即使保持开启也不提供任何保护;一个沙箱在被关闭之前提供完全保护;只有可靠的、默认开启的守卫才能保护操作员自己的主机,而无需他们主动选择加入。"报告总结道。"在最后这种形态成为惯例之前,每个使用字符串匹配守卫的智能体都距离一次提示注入就能导致操作员账户被攻破。"