349 个 AI Agent Skill 中招——这个情报传到我这里的时候,我第一反应不是意外,而是"终于来了"。AI Agent 生态这两年跑得太快,Skill 仓库一个接一个上线,大家像以前薅开源库一样批量安装能力插件,但安全审查机制几乎还是空白。占位域名这个切入点,恰好打在绝大多数 Agent 项目的通病上:过度信任外部引用,从不关心那个 URL 背后的主人是否还可靠。
先说清楚"中招"到底是什么情况。它不是传统的"源码里藏木马"那种投毒,而是更隐蔽的资产接管问题:一些 Skill 在开发时引用了占位域名,这些域名当时是无害的测试地址或未上线页面,但项目停更、域名过期后,被不怀好意的人重新注册。Agent 加载 Skill 时根本不知道域名已经换主,依然按指令去访问、去执行、去提交数据,于是一个小小的配置地址,就变成了诈骗跳板。
这篇文章我会把这个事件的链路拆开讲清楚:Skill 生态为什么这么脆弱、占位域名是怎么变成跳板的、更重要的是,你手上已经装了的 Skill 该怎么快速体检,以后装新 Skill 又该怎么避坑。
1. 事件还原:349 个 Skill 中招是怎么发生的
1.1 Skill 生态:从"能力外挂"到信任黑箱
AI Agent 的 Skill,你可以理解为给 Agent 配的"岗位手册+工具包"。Agent 本身是个聪明的执行者,但刚出厂时能力有限,需要装 Skill 才知道怎么发邮件、怎么操作浏览器、怎么做数据分析、怎么跟某个业务系统交互。装上"发送邮件"的 Skill,它就能调邮件接口;装上"GIS 空间分析"的 Skill,它就能处理地理数据。
这个生态的热度不用多说,各大框架都在推自己的 Skill 格式、插件规范和在线仓库,开发者的心态跟当年疯狂安装 npm 包、pip 包差不多:装得越多,Agent 越强。但问题就藏在这里。Skill 本质上是一个"外部代码 + 外部数据 + 外部域名引用"的三明治,代码文件只是其中最表层的部分,真正的信任风险都在看不见的外部引用里。
349 这个数字,放在全球 Skill 存量里不算大,但它足以说明一个危险的趋势:有数百个已被封装成能力模块的项目,可能都带着一个不受原作者控制、也不受使用者控制的外部地址。更关键的是,这 349 个并不是无人问津的冷门项目,而是能被搜索、能被扫描、能被插件市场直接检索到的存量资产。攻击者根本不需要费劲入侵什么大厂,只需要"等"——等你写进 Skill 的那个域名过期。
1.2 占位域名:基础设施里的"僵尸资产"
占位域名,通俗讲就是开发过程中为了"先占个坑"而注册或引用的域名。最常见的有三类:第一类是个人测试域名,比如 xxx-test.dev、tryxxx.xyz,项目做完了但地址忘了从代码里清理;第二类是公司预留域名,注册了品牌或产品域名,但线上一直没有正式内容;第三类是临时页面,解析到一个"站点建设中"或"coming soon"的静态页,业务方以为它"存在但不重要"。
这类域名在 Skill 里出现的位置,基本是固定的三处:SKILL.md 文档里的参考链接、脚本里硬编码的 API 端点、以及 README 里"下载最新配置"的地址。比如一个 Skill 的配置文件这样写:
config_source = "https://demo-config-abc.xyz/settings.json"写这行代码的开发者心里想的是"反正只是个测试配置,后面会换",结果一忙就忘了。Agent 不这么想,Agent 只认这个地址,每次加载都乖乖去请求,把返回内容当真。
占位域名最麻烦的地方在于它"看起来无害"。域名被别人接管之前,它确实无害,甚至你手动浏览器访问,看到的可能还是一个没人维护的空白页。但域名这类资产有一个特性:注册了就要续费。个人开发者注册的便宜后缀域名,第二年忘续费、项目黄了不续费、或者公司业务调整放弃续费,域名一旦进入释放流程,任何人都能抢注。你写在 Skill 里的地址还是那个地址,但域名背后指向的服务器,已经换了主人。
我之前排查过一个现象:把开源项目里出现的所有域名拉出来做 WHOIS 比对,过滤掉 example.com 这类纯保留域名,再交叉核对注册时间和当前解析状态,能发现相当高比例的"地址没变、主人的早已易手"的资产。这次的 349 个,就是这个筛选逻辑下的结果——它们不是全部已经被攻击,而是全部处于"可被攻击"的高度危险状态。
1.3 攻击链:一个过期域名如何变成诈骗跳板
这个攻击链路其实很朴素,一点都不炫技。假设攻击者注册到了那个过期域名,他在域名下放的不是恶意外观明显的文件,而是一份"看上去很正常"的配置或接口。Agent 按 Skill 指令去请求,拿到这份内容,继续执行下游逻辑。整个过程用户看到的只是 Agent 在正常运行,可能在某个环节"卡了一下"或"跳转了一下",然后数据就到了不该去的地方。
诈骗跳板这个词用的很准确,很多被接管的域名并不是终点,而是中转站。第一次访问可能还是一个看似正常的 API 返回,接着通过重定向或者脚本里的二次请求,把流量导向钓鱼页面、恶意下载地址、或者一个仿冒的登录框。人和 Agent 的区别在于,人看到可疑跳转会犹豫,但 Agent 在"按 Skill 指令执行"的框架里,基本不会怀疑这个地址是不是跟预期一致。
值得多说一句的是,这类问题用传统安全扫描很难发现。你把 Skill 源码翻个底朝天,找不到任何恶意代码;你检查脚本里的可疑函数,也没有命中。因为 Skill 本身是干净的,恶意内容全部在外部域名的"最新响应"里。源码提交时域名正常,问题出现在几个月甚至一年之后,时间差让审计变得特别被动。
2. 为什么 Skill 是供应链攻击的重灾区
2.1 Skill 结构天然引入了"外部信任"
一个典型的 Skill 目录结构长这样:
my-skill/ ├── SKILL.md # 自然语言指令:什么场景用、怎么调用、参数说明 ├── script.py # 核心执行逻辑 ├── reference/ # 外部资源引用:文档、数据文件、配置 ├── requirements.txt # Python 依赖 └── config.yaml # 配置,可能包含 URL / API 地址SKILL.md 是给模型看的,模型在执行时会把里面的指导转成具体操作。这就意味着,Skill 里凡是写进指令描述或脚本引用的外部地址,都会被 Agent 当作"可信指令来源"。很多 Agent 框架在设计时明确告诉模型:如果 Skill 文档里写了要访问某个 URL,就应该去访问,因为 Skill 的出发点就是赋予 Agent 自主行动能力。信任机制一旦建立,外部内容的变更就会被无差别吸收。
想象一下,Skill 就像是给一个执行力极强的实习生发了一本操作手册,手册里写"数据在 A 服务器,去拉下来",实习生就去了。他不会去核实"A 服务器"是不是公司的资产、页面是不是可疑、返回内容是否合理。Agent 的问题一模一样,甚至更严重,因为它可以同时、高速、批量地执行这类动作。
2.2 占位域名的隐蔽性:为什么扫描不容易发现
如果攻击者直接改 Skill 源码、插恶意逻辑,那代码审计一查一个准,但占位域名问题恰好绕过了源码审计这条路径。静态扫描工具关注的是恶意函数、异常编码、混淆特征,它不会告诉你"这个域名过期了"。
隐蔽性主要来自四个方面。第一,静态扫描完全无感,代码里的一切都是合法的、合理的、无攻击特征的。第二,时间差跨度大,代码提交时域名还正常,问题在一年后才显现,常规发布流程里的安全检查根本覆盖不到这么久之后的状态。第三,域名信誉"清白",新注册的恶意域名容易被安全情报标记,但被接管的旧域名往往有干净的历史解析记录,甚至出现在大量开源项目的 README 里,信誉分反而很高。第四,不主动访问就不会暴露,你只有让 Agent 真正跑起来、真正发一次请求,才可能发现问题。
打个比方,你租了一套房子,房东换了,但钥匙没换、门锁没换。你不会因为"门还是原来那个门"就觉得安全,但你就是很难发现"房东已经换了"。域名问题也是这样,它比代码投毒更难感知,但危害一点都不小。
2.3 Skill 生态的四大信任误区
误区一:Stars 多等于安全。热度只能说明用的人多,不能说明经过审查。相反,热门 Skill 因为被集成得广,一旦出问题,波及面反而最大,是攻击者最喜欢盯的目标。
误区二:代码没改过就没风险。代码没变不代表供应链没变。外部域名可以换主,第三方接口可以变换行为,引用的远程配置可以随时更新。固定不变的代码,依赖的却是一堆会变的东西。
误区三:占位域名无所谓。很多开发者觉得"反正这域名没业务,没人惦记"。事实恰恰相反,攻击者会批量枚举这类域名,因为成本极低、收益极高。拿到一个活跃 Skill 引用的域名,等于拿到一条通往大量 Agent 的通道。
误区四:有沙箱就不怕。沙箱能隔离执行环境、限制文件系统权限,但它挡不住"合规地做坏事"。Agent 在沙箱里正常访问网络、正常读取配置、正常发送数据,一切行为都是它被设计来做的。沙箱防的是失控,不是防诈骗跳板。
3. 自查实操:把中招的 Skill 揪出来
3.1 第一步:导出已安装 Skill 清单
不同 Agent 框架的 Skill 存放位置不一样,但思路是通用的:先找到 Agent 的配置目录,然后把所有 Skill 子目录列出来。以常见框架为例:
# 找到所有可能的 skill 目录 find ~/.agent -type d -iname "*skill*" 2>/dev/null find /opt/agent -type d -iname "*skill*" 2>/dev/null清单导出后,还要额外检查两个地方。一个是 Agent 运行时加载的"自动装配"清单,通常是一个 JSON 或 YAML 文件,里面记录着哪些 Skill 被启用;另一个是用户级配置目录,比如 ~/.config 下对应框架的目录。很多旧 Skill 不再被主配置引用,但目录还躺在那里,如果某天环境被重新同步或者配置被合并,风险会重新激活。
3.2 第二步:抽取所有外部 URL 与域名
把 Skill 当成一个小项目来做依赖审计。第一步抽取所有 URL:
# 从 skill 目录中提取所有 http(s) 链接,过滤明显保留域名后去重 grep -rhoE "https?://[a-zA-Z0-9._~:/?#@!$&*+,;=%-]+" ./skills | \ grep -vE "example\.com|w3\.org|schemas\.|localhost|127\.0\.0\.1" | \ sed 's|https\?://||' | cut -d '/' -f1 | sort -u光有域名列表还不够,我建议带上上下文信息,也就是每个域名是从哪个 Skill、哪个文件里抓出来的。这样才能保证后面的体检结果能对应回具体资产。
# 保留文件路径与域名对应关系 grep -rnoE "https?://[a-zA-Z0-9._~:/?#@!$&*+,;=%-]+" ./skills | \ grep -viE "example\.com|localhost|127\.0\.0\.1"这一步做到位,你就有了一个"Skill 外部信任清单"。接下来所有检查,都是对着这份清单做体检。
3.3 第三步:批量体检域名资产
拿到域名列表后,重点看四个维度:域名当前是否解析、WHOIS 里的注册时间和过期时间、历史解析记录是否异常、以及证书信息是否有突然变化。
for domain in $(cat domain-list.txt); do echo "=== $domain ===" whois "$domain" | grep -iE "creat|expir|registrar" | head -5 dig +short "$domain" A dig +short "$domain" MX done历史解析记录这块,whois 命令只看得到当前状态,过往数据需要借助公开的 DNS 历史记录服务或威胁情报平台。实用技巧是:如果 Skill 集成时域名解析到 A 服务器的 IP,现在再看变成了完全不同的 IP,而且中间间隔不超过 90 天,这个域名就有被接管的重大嫌疑。
我还会顺手看域名年龄。正常 Skill 生态下的开发域名,注册时间通常在 1 到 3 年之前。如果发现一个 Skill 项目已经存在很久,引用的域名却是最近一个月刚注册的,那就要高度警惕——新注册者很可能就是接管的攻击者。域名注册时间在 whois 输出里通常标注为 Created Date,一眼就能看出来。
3.4 第四步:运行时网络行为审计
静态检查做完了,最好再观察一遍真实运行状态。做法是在 Agent 运行环境挂一个透明的 HTTP 代理,把 Skill 触发的所有请求记录下来。mitmproxy 这类工具可以胜任:
# 记录所有经过代理的流量 mitmproxy --mode transparent -p 8080 -w skill-traffic.log审计时重点看这几个信号:第一,是否有请求访问了脚本里没有写过的域名,说明 Skill 内部存在隐式跳转或动态拼接;第二,请求链是否出现"访问 A 域名后 302 跳到 B 域名"的模式,这是跳板型攻击最常见的特征;第三,是否有 token、cookie、用户输入之类的敏感字段被 POST 到外部;第四,是否存在高频出现但从未在文档里说明的域名。
这一步对"诈骗跳板"类型最有效,因为攻击者接管域名后,通常不会让流量一次性直达恶意页面,而是做多级跳转来隐藏痕迹。只要重定向链一出现,问题基本就藏不住了。
4. 防御与修复:从源头到运行时
4.1 安装前审查:少装、慎装、可审计
给 Skill 做准入审查时,我建议先问四个问题:这个 Skill 到底解决什么问题?有没有不安装也能达到同样效果的办法?作者是谁、项目最近有没有更新?如果出问题,我能不能快速卸载并回滚?
下面是一张可以直接套用的信任评估表:
| 检查项 | 安全 | 警告 | 危险 |
|---|---|---|---|
| 维护活跃度 | 近 3 个月有提交 | 近 1 年无更新 | 2 年以上无更新 |
| 外部域名与项目一致性 | 域名属于项目官方 | 域名是个人博客/短链 | 域名是新注册或未知归属 |
| 外部依赖数量 | 0-2 个 | 3-5 个 | 5 个以上 |
| 权限需求 | 只读 | 可写文件 | 可执行系统命令或涉及敏感数据 |
审查的原则就一条:少装、慎装、装之前想清楚。Skill 生态还在快速发展期,没必要什么流行装什么。你装的每一个 Skill,都是在给 Agent 增加一个数字化的"信任入口",入口越多,风险面越大。
4.2 依赖固定:锁版本、本地缓存、哈希校验
Skill 本质上跟 npm 包、pip 包没什么区别,不能只追求"装上了",要确保"装的是我审查过的那一个"。具体做法是三层。
首先,锁定 Skill 的版本号,不要追踪 main 或者 latest 分支,避免静默更新。其次,把 Skill 引用的远程配置文件、数据源下载到本地缓存,让 Agent 读取本地副本而不是每次去远程拉取。第三,对下载的资源计算 SHA-256 哈希,在配置里维护哈希对照表,每次加载前校验。
# 计算并记录哈希 sha256sum downloaded-config.json > config.hash # 下次加载前校验 echo "expected_hash downloaded-config.json" | sha256sum -c -这套做法不能完全防住"域名被接管",但它能大幅降低"无感知被换内容"的概率。远程内容真的被改了,校验失败会让 Agent 停下来报错,而不是闷头执行。对大多数应用场景来说,让流程失败比让流程带着问题继续跑要安全得多。
4.3 运行时隔离与最小权限
给 Agent 开一个受限账号,或者直接放进容器里跑,是防御体系里最实用的兜底手段。重点配置三件事。
第一,网络白名单。只允许 Agent 访问业务真正需要的域名,重定向出去的流量直接不放行。这对诈骗跳板特别有效,因为攻击者就算能完成域名接管和重定向,也过不了白名单这一关。
# 示例:明确允许的域名,其余全部拒绝 ALLOWED_DOMAINS="api.example.com docs.example.org"第二,文件系统只读。Skill 目录挂载为只读,防止运行中被下载的脚本在本地写恶意文件。第三,密钥最小化。Skill 执行时不应该拿到全局所有的 API Key 和 token,应该按照 Skill 的用途分配最小 scope,比如分析类 Skill 只能读数据,不能碰支付或用户信息。
运行环境还可以在 DNS 层面做文章:把 Skill 引用的域名在内网 DNS 中解析到可控的地址,流量经过镜像和审计节点,既能跑通业务,又能在出口处把每一笔请求记录在案。这种方案初期配置成本高一点,但长期看非常值。
4.4 应急响应:发现中招后怎么办
真发现 Skill 引用的域名有问题时,我建议按这个顺序处理。
第一步,立即禁用或卸载相关 Skill,别让 Agent 继续加载。第二步,撤销该 Skill 使用过的所有凭据,包括 API Key、token、cookie、会话。第三步,检查 Agent 运行环境里有没有异常文件、计划任务、启动项,排除本地被写入后门的可能。第四步,导出流量日志和 Skill 目录快照,保留证据再清理。第五步,顺着这个 Skill 的所有外部引用继续扩大排查范围。
这里有个极易被忽略的细节:不要只查"出事的那个 Skill",要连同它引用的所有域名一起排查。因为攻击者接管域名后,往往会在同一批被接管的资产里批量部署,你另一个 Skill 可能早在几个月前就指向了同一个受控域名,只是一直没被触发。一个域名出事,关联排查范围至少要覆盖到所有引用过这个域名的 Skill。
5. 常见问题速查与排查思路
5.1 高频疑问速查表
| 疑问 | 快速判断 | 操作建议 |
|---|---|---|
| 占位域名一定危险吗? | 不一定,但风险极高 | 能换正式域名就换,换不了就本地化 |
| Stars 多的 Skill 安全吗? | 不保证 | 按信任评估表逐项确认 |
| 沙箱能拦住诈骗跳板吗? | 不能 | 配合网络白名单和出口审计 |
| 所有外部 URL 都要禁用吗? | 过度谨慎会影响功能 | 保留白名单域名,其他替换为本地资源 |
| Skill 源码干净就安全吗? | 不一定 | 重点检查外部引用域名和资源 |
| 公司有多台 Agent 怎么管? | 建议建立资产台账 | 每季度做一次域名体检 |
5.2 速查表的使用思路
这张表的底层逻辑,还是"你信任谁"的问题。Skill 生态之所以踩坑,是因为大家默认"装得越多越强",但没有人想"装得越多,信任面越大"。我见过很多团队,花大把时间调 Prompt、调模型参数,却从没给 Agent 做过一次供应链审计。模型能力再强,底层信任链条是断的,结果就可能被一个域名牵着走。
排查不用追求一次到位。先把已安装的 Skill 清单拉出来,再有侧重地检查外部域名,最后针对高风险项做运行时审计。一个季度做一轮,基本能把风险控制在可接受范围内。
6. 我的实操体会
最后说点个人经验。我给自己维护的几个 Agent 做过一次全面体检,查出来的问题比想象中多:有的 Skill 引用了一个 2021 年就过期的域名,有的引用的"官方文档"地址早就被短链服务接管,还有的配置文件没有锁版本,每次加载都可能拿到不同内容。
后来我养成了几个习惯,也推荐给你。每个 Agent 的 Skill 清单都当成资产台账来维护,记录安装时间、版本号、引用的外部域名;每季度跑一遍域名批量体检,专盯"域名注册时间"和"解析地址是否变化"这两个指标;Skill 里出现的所有外部 URL,能本地化就本地化,不能本地化就进网络白名单,绝不裸奔;安装新 Skill 前先花十五分钟做审查,花小钱省大钱。
踩过几次坑之后,我的感受是:AI Agent 的能力越大,责任边界越要划清楚。Skill 给 Agent 装上了手和脚,但你必须确保这些手脚不会把主人带进沟里。这次的 349 个中招案例,只是给整个生态提了个醒——跑得快的时候,更得检查鞋带有没有松。