runningoffcode/agent-wormhole

GitHub: runningoffcode/agent-wormhole

一个用于检测和阻止自复制 prompt payload 写入 AI agent 配置文件的完整性监控与防护工具。

Stars: 0 | Forks: 0

# Agent Wormhole [![ci](https://static.pigsec.cn/wp-content/uploads/repos/cas/99/993938d8ce5e902ccfb9d6747725c320d855dea3235ed9a304cedf0d94c9321f.svg)](https://github.com/runningoffcode/agent-wormhole/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/wormhole-guard)](https://pypi.org/project/wormhole-guard/) [![Python](https://img.shields.io/pypi/pyversions/wormhole-guard)](https://pypi.org/project/wormhole-guard/) [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE) [![Dependencies](https://img.shields.io/badge/dependencies-none-brightgreen)](pyproject.toml) [![Telemetry](https://img.shields.io/badge/telemetry-none-brightgreen)](#no-telemetry) [![Corpus](https://img.shields.io/badge/corpus-15%2F15%20·%2014%2F14-informational)](corpus/) **你的 agent 们正在互相通信。确保它们没有夹带私货。** Agent 会生成 agent、交接工作、评论 issue 并读取彼此的输出。一个被攻破的 agent 不再仅仅是受害者,而会变成携带者——而另一端的助手对你的指令同样 言听计从。 这背后隐藏着一个无人认领的不对称性:你的供应商会保护它自己的 `settings.json`,但没有任何东西保护你的 `CLAUDE.md`、`AGENTS.md` 或 `.cursor/rules`,而且默认情况下你的 agent 可以随意写入所有这些文件。 ``` $ wormhole scan . CRITICAL SessionStart hook executes a script from an unusual path [AUTOSTART-002] .claude/settings.json `node .github/setup.js` runs unprompted on SessionStart. This survives uninstalling the package that planted it. HIGH Agent config not in baseline [BASELINE-003] .cursor/rules/setup.mdc This file was not present when the baseline was taken. ``` 除 Python 3.8+ 外无任何依赖。无需账户、无需 API token、无网络调用。 你的 `CLAUDE.md` 永远不会离开本地机器。 ## 为什么 **2026 年 6 月:Miasma 蠕虫瘫痪了 73 个 Microsoft GitHub 仓库。** 它 并未利用内存漏洞,而是写入了 agent 配置: | 文件 | 机制 | |---|---| | `.claude/settings.json` | `SessionStart` hook → `node .github/setup.js` | | `.gemini/settings.json` | 同上 | | `.cursor/rules/setup.mdc` | `alwaysApply: true`,“运行安装脚本” | | `.vscode/tasks.json` | `runOn: folderOpen` | | `package.json` | 被劫持的 `test` 脚本 | 它瞄准了 15 个 AI 编码 agent,并且这种持久化**能在 `npm uninstall` 和 重装 agent 后存活**——配置文件的寿命比这两者都要长。它还在每次写入时 重新加密自身,因此对已知 payload 进行哈希匹配永远找不到它。 这五个锚点中有四个根本不需要模型参与。hook 触发仅仅是因为一个 session 启动了。这就是为什么此工具检查配置,而不仅仅是文本说明。 还有两件事使得这个缺口成为结构性的,而非偶然的: - **Cursor 收到了警告却拒绝承担责任。** Pillar Security 的 Rules File Backdoor(2025 年 2 月至 3 月披露)利用不可见的 Unicode 字符将指令隐藏在 `.cursor/rules` 中。Cursor 的回应是该风险应由用户自己承担。这就是你承担 该责任的方式。 - **沙盒机制未覆盖关键文件。** Claude Code 自己的文档声明 *"Read、Edit 和 Write 直接使用权限系统,而不是 通过沙盒运行"*,且默认对工作目录具有写访问权限。下面的研究 结果——沙盒隔离将攻击成功率降至零——并不适用于默认安装。 机制论文为 [arXiv:2603.15727](https://arxiv.org/abs/2603.15727)(预印本,2026 年 3 月, 2026 年 7 月修订):进行了 2,250 次试验,通过技能供应链 投毒攻击成功率达到 82%(在所有三个向量中总体成功率为 63%),启用沙盒隔离后 攻击成功率为 0%——而且在 **82 个公开索引的 agent 配置中,有 0 个** 启用了沙盒隔离。62% 的配置使用了 gateway 身份验证,但这 并不能阻止传播。 有效的防御方案已经存在,却没人在用。这个缺口是一个工具 问题,而这就是那个工具。参见 [MISSION.md](MISSION.md)。 ## 安装 ``` pipx install wormhole-guard wormhole scan ~/your-project --blast-radius ``` 发布包名为 `wormhole-guard`。PyPI 上的 `agent-wormhole` 是一个无关的 项目。命令和 import 包名都是 `wormhole`。 仅使用标准库,因此它也可以直接从检出的代码运行,完全不需要 安装步骤: ``` git clone https://github.com/runningoffcode/agent-wormhole cd agent-wormhole python3 -m wormhole scan ~/your-project --blast-radius ``` ## 使用 预防——这些在 payload 落地之前运行: ``` wormhole init ~/project # harden + baseline + print the hooks wormhole outbound --install # sends: refuse to pass a payload on wormhole readguard --install # reads: PostToolUse + InstructionsLoaded wormhole guard --install # writes: the PreToolUse hook wormhole harden ~/project --apply # drop the write bit, block creation wormhole harden ~/project --undo --apply # restore write permission ``` 三道大门。`readguard` 覆盖接收的内容——抓取的页面、shell 输出、MCP 响应——这正是 2026 年所有公开披露的 agent 被攻破事件的真实 入侵方式。`outbound` 覆盖你的 agent 传递给子 agent、 对等节点或另一个团队的 bot 会读取的 issue 的内容。`guard` 和 `harden` 覆盖是否有任何东西可以持久化到下一个 session。 `outbound` 是唯一一个默认拦截的。入站内容根据定义是不受信任的,并且数量庞大, 因此这些规则保持保守。出站内容是由你自己的 agent 编写的, 因此 payload 出现在那里已经是异常情况——并且被拒绝的发送会显式报错, 而一旦发送出去,就会到达一个从未同意信任你的操作员那里。 检测与控制——这些在事后运行: ``` wormhole scan ~/project --blast-radius # payloads, posture, blast radius wormhole baseline ~/project # fingerprint configs wormhole verify ~/project # detect modification wormhole watch --limit 20 # injection attempts in tool output wormhole capture ~/project # preview capture (dry run) wormhole capture ~/project --apply # capture, preserving originals wormhole captured # list what has been contained wormhole restore # pull one back out, byte-for-byte wormhole insights # what the capture history reveals wormhole handoffs # payloads in agent-to-agent tasks wormhole corpus ./docs # documents before they are embedded ``` `handoffs` 和 `corpus` 覆盖了该工具最不擅长检测的两个向量, 有必要说明一下其局限性。传递给子 agent 的任务描述没有 拦截点——父 agent 在内存中将其组合——因此 `handoffs` 在 事后读取记录。向量存储没有标准格式,因此 `corpus` 在摄入*之前*扫描文档,这是文本 仍然是文本的最后一个时刻。 `guard` 默认发出警告。Block 模式仅拒绝 WORM-001 和 WORM-003——这 两条规则具有明确的结构特征且无语料库误报——因为拦截工具中的 规则缺陷会阻碍正常工作,而不是仅仅打印噪音。 `scan` 和 `watch` 在达到或超过 `--fail-on`(默认为 `high`)时 以非零状态退出,因此它们可以直接原样接入 CI。 ## Wormhole 捕获的 payload 会进入 Wormhole,而不是被丢弃到回收站。 ``` wormhole capture ~/project --apply # excise payloads, keep originals wormhole captured # what has been captured wormhole restore # pull one back out (false positive) wormhole export ./samples # inert fixtures for rule development ``` 删除 payload 会破坏回答感染后唯一重要问题所需的 证据:是什么写入了它,什么时候,以及它是否传播。它 还会将误报转化为数据丢失,这正是安全工具失去 用户的原因。因此,原始文件被逐字节保留,并附带完整的 来源记录,并且每次捕获都是可逆的。 清理工作迭代运行——清理、重新扫描、重复——因为 payload 可能 占据多个独立的块。AgentWorm 的双锚设计恰好使用 两个锚点,如果只移除第一个锚点,第二个锚点将保持活动状态,同时使 文件看起来已被处理。如果文件无法完全清理,运行将报告 `INCOMPLETE`,而不是宣称成功。 Wormhole 权限为 `0700`;payload 以 `0400` 权限存储并带有 `.quarantined` 后缀,因此其中的任何内容都不会被加载为 agent 配置或被执行。 ## 它确切做了什么 这是一个**针对你的 agent 作为指令读取的文件的完整性监控器**。 关键部分并不关心 payload 说了什么。 | | | 在重写措辞后依然有效? | |---|---|---| | **预防** | `harden` 移除写入权限,并预先创建缺失的配置路径,使得 payload 也无法创建它们 | 是——不涉及规则 | | **通知** | `baseline`/`verify` 对每个配置进行哈希处理;更改或未记录的文件即为一个发现 | 是——哈希与措辞无关 | | **拒绝** | `guard` 通过 PreToolUse hook 检查挂起的写入,并可以拒绝该写入 | 部分——基于规则 | | **检测** | 用于识别 payload 形状的内容规则、用于无人值守执行的 autostart 规则、用于评估能力的 posture 规则 | 否——可规避,用作分诊 | | **控制** | `capture` 清理 payload,逐字节保留原始文件以供恢复 | 不适用 | 这种排序是刻意的。预防和完整性是持久的一半; 规则只是在其之上的便利。一个以规则数量为卖点的工具是在 最容易衰退的轴线上竞争。 将感染率降至零的控制手段是沙盒隔离,它存在于 你的 agent 框架中,而不是这里——并且根据 Anthropic 自己的文档,它默认不覆盖 Read/Edit/Write。该工具让这一缺口无法被忽视。 ## 检测规则 **内容**——看起来像自我复制 payload 的文本: | | | |---|---| | `WORM-001` | 自我复制指令(自我引用 + 复制动词 + 目标) | | `WORM-002` | 指令覆盖措辞 | | `WORM-003` | 凭据泄漏到外部目标 | | `WORM-004` | 隐藏在 HTML 注释中的指令 | | `WORM-005` | 零宽字符 | | `WORM-006` | Unicode tag 块走私(不可见,模型可读) | | `WORM-007` | 隐瞒指令(“不要告诉用户”) | **Autostart**——在无提示且无模型参与的情况下执行的配置。这 是在实际传播中真正起作用的形状: | | | |---|---| | `AUTOSTART-001` | 无人值守的 hook 下载并执行(`curl … \| sh`) | | `AUTOSTART-002` | 无人值守的 hook 运行来自配置目录的脚本 | | `AUTOSTART-003` | 无人值守的 hook 运行解释器 | | `AUTOSTART-004` | 指示 agent 运行命令的总是应用(Always-applied)的 Cursor 规则 | `PreToolUse` 被刻意不视为无人值守——它是因为 agent 已经在行动时才触发的,并且它是 `guard` 本身的发布方式。 **MCP 工具完整性**——这里唯一不接触磁盘的 artifact。服务器 在连接时响应 `tools/list`,它返回的名称、描述和 schema 被注入到模型的上下文中,在那里它们被读取为 指令。协议中没有任何内容对该答案进行签名,也没有任何内容要求 客户端重新检查它,因此服务器在你审查时可能是良性的,而一周后 即使没有任何文件更改,它也可能变得不同。 | | | |---|---| | `MCP-001` | 工具定义不再匹配记录的内容 | | `MCP-002` | 获取基线时不存在的工具 | | `MCP-003` | 记录的工具不再被声明 | | `MCP-WORM-*` | 内容规则,应用于工具描述 | `wormhole baseline` 与你的配置文件一起对它们进行指纹记录;`verify` 报告偏差。重新格式化的 JSON 不算作更改——只有名称、描述 和 schema 会被哈希。 **Posture**——如果 payload 到达,它能完成什么操作:不受限制的 shell (`POSTURE-001`)、网络出口(`-002`)、缺少拒绝规则(`-003`)、可写的 配置(`-004`)、远程 MCP 服务器(`-005`)、已安装的技能(`-006`)。 **完整性**——`BASELINE-001/002/003`:已更改、缺失或未跟踪的配置。 这是能够捕获任何规则都未预料到的 payload 的一半。 **运行时**——`RUNTIME-*`:应用于 session 记录中工具输出的 相同内容规则,覆盖了从不接触磁盘的 供应链通道。 ## 回归测试套件 ``` $ ./loop/replay.sh detected 15/15 clean 14/14 FN=0 FP=0 ``` **这是一个回归测试套件,而不是检出率。** 它是在 为本仓库编写的 fixture 上测量的,因此它证明规则在更改后仍然按照 预期运行——仅此而已。任何人(包括我们自己)将其作为针对 真实攻击者的准确率进行引用,都是在夸大其词。基于规则的检测在构建上 就是可规避的:Trail of Bits 在不到 一小时内绕过了所有主要的技能扫描器,而仅仅改写措辞就足以击败已发布的分类器。 该套件确实强制执行了使规则可用的准则: 每个恶意 fixture 都附带一个**良性孪生体**,它保留了 payload 的 定罪表面特征,并且仅改变规则所依赖的属性。 关键词匹配器会在两个方向上对该对测试失败。如果规则在 孪生体上触发,它就不会被发布。这在发布前捕获了两个真正的 漏报和一个严重的误报。 良性部分是刻意设计的对抗性测试:一份威胁模型文档、一份写着 “在你创建的每个源文件中包含许可证头”的配置、一份 `.env` 指南、一个写入 `CHANGELOG.md` 的合法技能、一个运行 `git fetch` 的 `SessionStart` hook,以及本项目自己的 guard hook。 此外还针对 7 个真实项目进行了验证:0 发现。 ## 免责声明 Apache 2.0,其中的责任条款值得阅读而不是想当然: — [LICENSE](LICENSE),第 7 和 8 节 通俗地说:这是一个检测和加固工具,而不是保证。它可能会 漏掉它从未见过的 payload,它也可能对它见过 payload 判断失误,并且它 无法使 agent 变得安全。在你依赖它进行任何操作之前,请阅读[局限性](#limits),并且 无论何时都要将真正有效的控制手段——沙盒隔离——保留在 你的清单上。 Apache 2.0 还包含明确的专利授权(第 3 节),这就是为什么本项目 坚持使用它,而不是转向更宽松的简化许可协议。 ## 无遥测 此工具读取你环境中最敏感的部分:prompt、权限、 与凭据相关的配置,以及你的 agent 视为 指令的文件内容。因此,它不会将其中任何内容发送到任何地方。 - 无需账户、无需 API token、任何时候都没有网络调用。 - 除 Python 标准库外无任何依赖,因此以后不会引入 可能改变这一情况的任何东西。 - 基线和捕获存储位于你机器上的 `~/.wormhole` 中。 - `wormhole insights` 在本地分析你自己的捕获历史。没有 全局数据流,这是刻意的——构建一个数据流恰好需要 此承诺所禁止的数据。 向 upstream 贡献 fixture 是一项独立的、刻意的操作(`wormhole export`),并且根据策略,导出的内容是无害的:没有实时的 endpoint,没有 有效的 payload。 请验证它,而不是盲目相信它。整个工具大约有 4,000 行 无依赖的 Python 代码: ``` # 没有在任何地方导入网络客户端。这不会打印任何内容。 grep -rnE "^\s*(import|from)\s+(socket|urllib|http|requests|aiohttp)" wormhole/ ``` ## 支持的配置格式 `AGENTS.md`、`CLAUDE.md`、`GEMINI.md`、`.cursorrules`、`.cursor/rules/*.mdc`、 `.windsurfrules`、`.windsurf/rules/*.md`、 `.github/copilot-instructions.md`、`.github/instructions/*.instructions.md`。 权限分析目前最能理解 Claude Code 的 `settings.json`。 ## 持续审计 ``` loop/install-cron.sh # every 6h; --remove to uninstall ``` 没有任何变化时保持静默;记录到 `~/.wormhole/logs/` 并在 被跟踪的配置被修改或出现 payload 时发出通知。 [loop/RESEARCH.md](loop/RESEARCH.md) 记录了如何在不降低 误报率的情况下添加新规则。 ## CI ``` - uses: runningoffcode/agent-wormhole@v1 with: fail-on: high ``` ## MCP 服务器 允许 agent 审计自己的 posture。根据设计为只读——它只报告且从不 写入,因为 agent 可以要求修改其配置的安全工具 本身就是注入的目标。 ``` { "mcpServers": { "wormhole": { "command": "python3", "args": ["-m", "wormhole.mcp_server"], "cwd": "/path/to/agent-wormhole" } } } ``` 工具:`scan_agent_configs`、`check_integrity`、`blast_radius`、 `scan_session_history`。 ## 局限性 坦诚地声明,因为夸大其词的安全工具比没有更糟糕: - 正则表达式规则捕捉的是*形状*,而不是含义。新颖的措辞可以规避它们——这 就是为什么 `baseline`/`verify` 存在并且比规则覆盖率更重要的原因。 - `watch` 在事后读取记录。它告诉你注入尝试 已经到达了你的 agent;它不会拦截它。 - 这里没有任何东西可以从正在运行的 agent 中清除感染。`wormhole` 只清理文件。 - 目前尚未公开确认有蠕虫在传播中。其先决条件 如今已经存在,并且无论情况如何,posture 发现都是真实的。 ## 贡献 每个新的检测规则都附带一个良性孪生体——一个讨论相同 攻击但不构成攻击的文件。如果规则在孪生体上触发,它就不会被发布。参见 [CONTRIBUTING.md](CONTRIBUTING.md) 和 [SECURITY.md](SECURITY.md)。 ## 许可证 Apache 2.0。
标签:AI安全, API接口, Blue Team, Chat Copilot, DLL 劫持, DNS 反向解析, Python, 人工智能, 大语言模型, 无后门, 用户模式Hook绕过, 逆向工具, 静态扫描