ClawHavoc 供应链投毒之后,我们重新审视了 AI Agent 沙箱安全——20 项测试的发现与反思
AI Agent生态面临的全新威胁模型需要重新设计安全策略,单一隔离层难以应对复杂攻击。文章详细分享了SkillLite的三层防御架构(安装扫描+执行授权+运行时沙箱)及工程实践中的经验教训,同时坦诚现有方案的不足(如内存炸弹防护缺失)。所有测试数据与复现方法均已开源,旨在推动行业共同提升AIAgent生态的安全水位。
春节期间,我持续提交了140+ commit 到github,在研究openclaw的rust版本重构的时候,也对skills执行沙箱的进行了思考和升级
25 年末至 2026 年初,OpenClaw 的 ClawHub 插件市场遭遇大规模供应链投毒(ClawHavoc),超过 340 个恶意 Skills 被植入,攻击者通过 SKILL.md 中的虚假"安装步骤"诱导用户在终端执行恶意命令,窃取 SSH 私钥、API Key、加密货币钱包等敏感信息。
这个事件促使我们重新思考一个问题:当 AI Agent 需要执行来自第三方的代码时,各种沙箱方案在面对真实攻击场景时的防御能力到底怎么样?
我们对四种开源沙箱方案做了 20 项安全测试(测试用例覆盖了 ClawHavoc 中出现的攻击手法),本文分享测试数据和我们在 SkillLite 中的工程思考,欢迎大家指正和讨论。
一、ClawHavoc 告诉我们什么
ClawHavoc 攻击链的关键步骤是:恶意 Skill → os.system("curl ... | bash") 或诱导用户执行 → 从攻击者服务器拉取第二阶段载荷 → 读取 SSH 私钥、环境变量、Keychain 等敏感信息。
如果我们把这个攻击链拆解成测试用例,会发现它精确命中了沙箱安全的几个核心维度:进程执行控制(能否阻止 os.system)、网络隔离(能否阻止 curl 外连)、文件系统隔离(能否阻止读 SSH 私钥)、信息泄漏防护(能否阻止读环境变量)。
带着这个思路,我们对四种方案做了 20 项测试。
二、测试设计与结果
测试覆盖文件系统、网络、进程、资源限制、代码注入、信息泄漏共 20 项攻击场景(其中多项直接对应 ClawHavoc 的攻击手法),每项记录"拦截 / 部分拦截 / 放行"。
|
方案 |
拦截数 |
评分 |
定位 |
|---|---|---|---|
|
SkillLite (Level 3) |
18/20 |
90.0% |
Rust 原生沙箱 + 静态扫描 |
|
Pyodide |
7/20 |
35.0% |
WebAssembly 沙箱(LangChain 采用) |
|
Claude SRT |
6.5/20 |
32.5% |
Anthropic 开源沙箱 |
|
Docker |
2/20 |
10.0% |
容器隔离 |
逐项对比
|
测试项 |
SkillLite (L3) |
Docker |
Pyodide |
Claude SRT |
|---|---|---|---|---|
|
文件系统 |
||||
|
读 /etc/passwd |
✅ |
❌ |
✅ |
❌ |
|
读 SSH 私钥 |
✅ |
✅ |
✅ |
❌ |
|
写 /tmp |
✅ |
❌ |
❌ |
✅ |
|
目录穿越 |
✅ |
❌ |
✅ |
❌ |
|
列根目录 |
✅ |
❌ |
❌ |
❌ |
|
网络 |
||||
|
HTTP 请求 |
✅ |
❌ |
✅ |
✅ |
|
DNS 查询 |
✅ |
❌ |
❌ |
✅ |
|
端口监听 |
✅ |
❌ |
❌ |
✅ |
|
进程 |
||||
|
os.system() |
✅ |
❌ |
❌ |
❌ |
|
subprocess |
✅ |
❌ |
✅ |
❌ |
|
进程枚举 |
✅ |
❌ |
❌ |
✅ |
|
信号发送 |
✅ |
❌ |
✅ |
⚠️ |
|
资源限制 |
||||
|
内存炸弹 |
❌ |
❌ |
❌ |
❌ |
|
Fork 炸弹 |
✅ |
❌ |
✅ |
❌ |
|
CPU 炸弹 |
✅ |
✅ |
❌ |
✅ |
|
代码注入 |
||||
|
动态 import os |
✅ |
❌ |
❌ |
❌ |
|
eval/exec |
✅ |
❌ |
❌ |
❌ |
|
修改内建函数 |
❌ |
❌ |
❌ |
❌ |
|
信息泄漏 |
||||
|
读环境变量 |
✅ |
❌ |
❌ |
❌ |
|
获取系统信息 |
✅ |
❌ |
❌ |
❌ |
评分公式:(拦截数 + 部分拦截 × 0.5) / 总测试数 × 100%
对照 ClawHavoc 攻击链:如果一个恶意 Skill 在 main.py 里尝试执行 os.system("curl ... | bash")(对应表中的"os.system()"和"HTTP 请求"行),再读取 ~/.ssh/id_rsa(对应"读 SSH 私钥")和环境变量(对应"读环境变量")——SkillLite 在这四个环节全部拦截,而多数方案在其中至少有两个环节放行。
需要说明的是,这些结果基于各方案的默认配置。经过定制化加固(如 Docker 的 --cap-drop、--security-opt、自定义 seccomp profile 等),各方案的表现都可以显著提升。
三、设计目标不同,结果自然不同
Docker 在我们的测试中拦截率较低,但这完全不意味着 Docker "不安全"。它的设计目标是应用部署隔离,而非防御恶意代码执行——这是两个不同的问题域。
Docker 在应用部署场景下是成熟可靠的标准方案。但在 AI Agent Skills 场景中,威胁模型发生了根本变化:
|
传统部署场景 |
AI Agent Skills 场景 |
|---|---|
|
代码来自可信开发团队 |
代码来自不可信第三方 |
|
目标是资源隔离 |
目标是防御恶意行为 |
|
容器内网络/文件访问是功能需求 |
容器内网络/文件访问可能是攻击路径 |
类似地,Pyodide 的 WASM 沙箱擅长浏览器端隔离,Claude SRT 专注于提供快速可用的运行时。每个方案都在自己的设计目标下做了合理的取舍。
四、我们的思路:全链路防御
SkillLite 能拦截 18/20 项测试(持续优化中),核心原因不在于某一层做得特别强,而是我们尝试在安装前、执行前、执行中三个阶段都做拦截。这个思路是否合理,欢迎大家一起讨论。
3.1 三层防线架构
┌─────────────────────────────────────────────────┐
│ 第一层:安装时扫描 │
│ ├─ 静态规则扫描(regex 匹配恶意模式) │
│ ├─ LLM 智能分析(可疑代码由大模型二次判定) │
│ └─ 供应链审计(PyPI/OSV 漏洞库比对) │
├─────────────────────────────────────────────────┤
│ 第二层:执行前授权 │
│ ├─ 两阶段确认(扫描 → 用户确认 → 执行) │
│ └─ scan_id 绑定(防止绕过扫描直接执行) │
├─────────────────────────────────────────────────┤
│ 第三层:运行时沙箱 │
│ ├─ OS 原生隔离(macOS Seatbelt / Linux bwrap) │
│ ├─ 进程执行白名单(仅允许解释器本身) │
│ ├─ 文件系统隔离(deny 敏感路径 + 移动保护) │
│ ├─ 网络隔离(Seatbelt deny + 代理过滤) │
│ ├─ 资源限制(rlimit CPU/内存/文件/进程数) │
│ └─ IPC 封堵(deny mach-register/iokit-open) │
└─────────────────────────────────────────────────┘
3.2 几个我们踩过坑的工程决策
从黑名单到白名单
早期我们也用黑名单拦截危险命令(/bin/sh、/usr/bin/curl 等),上线后发现攻击者可以通过路径变体(/usr/local/bin/bash、符号链接)绕过。
后来改为严格白名单:Seatbelt 的 process-exec 仅允许解释器路径本身。
;; 仅允许解释器执行
(allow process-exec (literal "/path/to/python3"))
(allow process-exec (literal "/path/to/canonical/python3"))
;; macOS Python.framework 兼容
(allow process-exec (subpath "/path/to/Python.framework/Versions/3.x"))
;; 拒绝一切其他进程执行
(deny process-exec)
这样即使恶意代码调用 os.system("curl ..."),Seatbelt 在内核层就会拦截。不过这也带来了兼容性挑战——比如 macOS Homebrew 的 Python 通过 posix_spawn 在 framework 内部重新执行,我们需要额外白名单整个 framework 版本目录才解决。
沙箱失败不降级
我们早期犯过一个错误:沙箱启动失败时静默退回无沙箱执行。后来意识到这等于给了攻击者一个绕过通道——只需构造让沙箱初始化失败的 payload 即可。
现在的策略是:沙箱失败 → 直接报错终止,绝不降级。
match execute_with_sandbox(skill_dir, runtime, config, input_json, limits) {
Ok(result) => Ok(result),
Err(e) => {
Err(e.context(
"Sandbox execution failed. Refusing to fall back to \
unsandboxed execution."
))
}
}
静态扫描前置
运行时沙箱再完善,代码进沙箱之前多一道过滤总是好的。eval()、exec()、__import__('os') 这些模式在安装时就会被规则引擎标记。这也是 SkillLite 能拦截"动态 import os"和"eval/exec"的原因——不过说实话,纯 regex 扫描容易被混淆绕过,这是我们后续要用 AST 分析加强的地方。
3.3 各方案的安全覆盖层次
|
安全能力 |
SkillLite |
Docker |
Pyodide |
SRT |
|---|---|---|---|---|
|
安装时判毒 |
✅ |
— |
— |
— |
|
静态代码扫描 |
✅ |
— |
— |
— |
|
供应链审计 |
✅ |
— |
— |
— |
|
进程执行白名单 |
✅ |
— |
— |
— |
|
IPC/内核操作封堵 |
✅ |
— |
— |
— |
|
文件系统隔离 |
✅ |
部分 |
✅ |
部分 |
|
网络隔离 |
✅ |
— |
✅ |
✅ |
|
资源限制 |
✅ |
部分 |
部分 |
部分 |
不同方案侧重不同层次。Docker 的容器隔离在部署场景强大,Pyodide 的 WASM 隔离在浏览器场景有独特优势,SRT 与 SkillLite 用了相同的底层技术(Seatbelt)但配置策略不同。SkillLite 尝试在多个阶段都做拦截,但代价是实现复杂度更高。
五、安全与性能的平衡
通常安全和性能是对立的——更多检查意味着更多开销。我们的经验是:如果安全机制本身就是 OS 内核能力,开销可以趋近于零。
|
维度 |
SkillLite |
Docker |
Pyodide |
SRT |
|---|---|---|---|---|
|
热启动 |
40ms |
194ms |
672ms |
596ms |
|
冷启动 |
492ms |
120s |
~5s |
~1s |
|
内存占用 |
~10MB |
~100MB |
~50MB |
84MB |
|
Binary 大小 |
~2MB |
200MB+ daemon |
50MB+ WASM |
需安装 |
性能差异的主要原因是架构选择不同:
-
Seatbelt / bwrap 是 OS 内核能力:
setrlimit是一次 syscall,零运行时开销;Seatbelt profile 在进程启动时一次性加载 -
静态扫描编译进 binary:regex 规则在编译时嵌入,扫描一个 skill 只需毫秒
-
单 binary 零依赖:不需要 Docker daemon、Node.js runtime、WASM 引擎
当然,Docker 和 E2B 等方案的架构优势在于更强的隔离边界(内核级别的 namespace/cgroup 或 VM 隔离),这是进程级沙箱无法完全替代的。具体选择取决于场景对安全深度和性能延迟的权衡。
六、更广泛的生态现状
AI Agent 框架的安全隔离整体上还处于早期阶段,这也是我们做这个方向的原因之一:
|
方案 |
沙箱方式 |
安装时扫描 |
代码审计 |
供应链防护 |
|---|---|---|---|---|
|
SkillLite |
Seatbelt/bwrap 原生沙箱 |
✅ |
✅ |
✅ |
|
Open Interpreter |
无沙箱 |
— |
— |
— |
|
AutoGPT / CrewAI |
Docker 或无 |
— |
— |
— |
|
LangChain |
Pyodide 或无 |
— |
— |
— |
|
E2B |
云端 microVM |
— |
— |
— |
|
Claude SRT |
Seatbelt |
— |
— |
— |
可以看到,多数框架目前主要依赖运行时隔离这单一层面。我们认为全链路防御的思路值得探索,但也不确定这是不是最优解——也许有更好的方法我们还没想到。
七、老实说,我们还有不少不足
SkillLite 目前还有不少明显的短板,这里坦诚列出来,也希望大家帮忙出出主意:
|
短板 |
说明 |
我们的计划 |
|---|---|---|
|
Memory bomb 未拦截 |
|
降至 256MB,补 fallback 路径 |
|
内建函数修改未拦截 |
regex 模式未覆盖所有 case |
加强规则 + AST 扫描 |
|
Linux seccomp 不完整 |
仅阻止 AF_UNIX |
扩展 ptrace/mount/clone 等 |
|
签名验证是字符串比较 |
非密码学验证 |
引入 Ed25519 |
|
|
macOS Seatbelt 未完全 deny default |
渐进式迁移中 |
|
静态扫描易被绕过 |
纯 regex 无法防混淆 |
考虑 tree-sitter AST 分析 |
特别是 (allow default) 这个问题——理想情况下应该用 (deny default) + 显式白名单,但兼容性测试成本非常高。如果有做过类似迁移的朋友,特别想听听你们的经验。
八、如何复现测试
所有测试脚本和数据都是开源的,欢迎在自己的环境跑跑看,也帮我们验证一下结果:
git clone https://github.com/EXboys/skilllite
cd skilllite
# 构建
cargo build --release
# 运行安全对比测试
python3 benchmark/security_vs.py
# 运行性能对比测试
python3 benchmark/benchmark_runner.py --compare-levels -n 100 -c 10
测试脚本在 benchmark/ 目录下,支持对比 Docker、Pyodide、Claude SRT。如果你发现测试方法有问题或者结果与你的环境差异较大,非常欢迎提 Issue 告诉我们。
写在最后
ClawHavoc 给整个 AI Agent 生态敲了一个警钟:当用户执行的代码来自不可信的第三方,传统的应用部署隔离方案可能不够用。这不是哪个方案的问题,而是威胁模型变了,安全策略需要跟着变。
这篇文章的目的不是说"SkillLite 最好"——每个方案都有自己的设计初衷和适用场景。我们想分享的是在 ClawHavoc 之后,我们重新审视沙箱安全的思考过程和工程实践。全链路防御只是一种尝试,肯定还有很多不足和可以改进的地方。
如果你也在做 AI Agent 安全相关的工作,很希望能交流一下思路。ClawHavoc 不会是最后一次供应链攻击,行业需要一起把这个问题想清楚。
SkillLite 项目地址:https://github.com/EXboys/skilllite
关于 ClawHavoc 供应链攻击的详细分析,参见我们的另一篇文章:《AI Agent Skills 供应链攻击启示:从 OpenClaw ClawHavoc 看纵深防御》
欢迎 Star、提 Issue、或者直接在评论区聊。感谢阅读!
更多推荐
所有评论(0)