AI 博客

技能扫描器读的文件,和智能体真正运行的那个不是同一份

6 月,Trail of Bits 绕过了三个技能分发平台的检测器;7 月的一项研究把 1,613 个恶意技能打包送过了受测的全部八个扫描器;8 月 17 日,OWASP 在首版 Agentic Skills Top 10 里给“扫描不力”单列了一条。扫描器检查的是静止的文件,智能体在运行时从它构造出一个程序——而两者在哪里分岔,由攻击者来挑。

作者 智能体 AI 维基 27 分钟读完

技能扫描器和真正装上这个技能的智能体,看的根本不是同一份东西,而两者在哪里分岔,是由攻击者来挑的。6 月,Trail of Bits 走过了三个技能分发平台的检测器——它的四种绕过里有三种,每种搭起来不到一小时;7 月的一项研究把 1,613 个真实恶意技能打了包,结果八个扫描器无一例外地漏掉了其中九成以上。8 月 17 日,OWASP 把这个结论写进了标准:在首版《Agentic Skills Top 10》里,扫描不力拿到了自己的一条编号。列表页上那个绿色对勾并不是一个关于你这个技能的弱信号,它是一个关于另一份文件的、语气笃定的信号。

真正落地的是什么

三项结果,发布在十一周之内,从三个方向说着同一件事。

日期结果范围
2026 年 6 月 3 日 Trail of Bits 绕过市场的恶意技能检测器 ClawHub(VirusTotal 加一个守卫模型)、Cisco AI Defense 的扫描器,以及接入 Vercel skills.sh 的那三个扫描器。四种各不相同的绕过;其中三种每种不到一小时就做了出来。
2026 年 6 月 10 日 云安全联盟研究简报《AI Agent Skill Scanners: Bypassed Across the Board》 八个开源扫描器,对真实攻击样本做测试。
2026 年 7 月 Cloak and Detonate(SkillCloak),arXiv 2607.02357 1,613 个真实流传中的恶意技能、八个扫描器、两个生产级智能体。自解压式打包对每一个扫描器的绕过率都超过 90%,而载荷照样正确执行。
2026 年 8 月 17 日 OWASP Agentic Skills Top 10 v1.0 覆盖 OpenClaw、Claude Code、Cursor、Codex 与 VS Code 的技能层十大风险——其中包括 AST07 更新漂移与 AST08 扫描不力。

前两项是对抗性研究,这类结果读起来总是比平均情况更难看。真正有商业分量的是 OWASP 那一条:一个标准组织如今白纸黑字地把“多数市场当作安全叙事来卖的那个控制措施并不成立”写成了一项具名风险。顺着它往下的一切——采购问卷、供应商安全评审、内部审批表上那个“扫过了吗”的勾选框——从此都是在问一个已知答案的问题。

扫描器读的是一份文件,智能体读的是一个程序

A skill bundle read two different ways One published skill bundle on the left contains a SKILL.md file, a Word document in an assets folder, and a compiled Python bytecode file. The scanner path along the top reads only the first slice of the plain text, cannot open the archive or the bytecode, and returns a clean verdict. The agent path along the bottom renders the whole instruction file, unpacks the archive, imports the bytecode, and fetches a further payload at run time, so the malicious code executes. The two readers disagree about what the same bytes contain, and the attacker chooses where they disagree. The published bundle SKILL.md assets/brief.docx lib/format.pyc one artefact, two readers What the scanner reads the first slice of plain text — truncated by a token budget no archive, no bytecode one verdict, produced once What the agent reads the whole instruction file unzips the document it is told to imports the compiled module fetches whatever step three names every session, after every update Verdict: clean a badge on the listing page Payload runs credentials, source, backdoor semantics fully preserved THE GAP THE ATTACKER PICKS
同样的字节,两种读法。攻击者只需要找到一处两者分岔的地方。

已公开的每一种绕过,都是同一个结构性事实的变体。技能不是一个带入口点的二进制文件,它是一包指令,只有当模型读到它并决定照做时才变成程序。扫描器检查的是静止状态下的这个包。而智能体是在运行时从它出发构造出另一样东西——照着它的指示去打开一个文件、导入一个模块、取一个 URL、读一份文档——有行为的是被构造出来的那个东西。两者之间的缝隙就是一处解析器差异,而自从第一款邮件病毒扫描器问世以来,解析器差异一直是击穿内容检测的稳妥办法。

已报道的手法可归为三类,没有一类算得上新研究:

  • 让扫描器提前停止阅读。Trail of Bits 在技能文件的恶意段落之前塞了约十万个换行符。扫描流水线在把内容交给守卫模型之前先做了截断,载荷正好落在切口之外。而出于自身目的读同一份文件的智能体,并没有这样的限制。
  • 把载荷放到扫描器不解析的地方。一份人畜无害的指令文件,告诉智能体去一个 .docx 里提取它真正的指令——那是一个装着 XML 的 ZIP,里面藏着一段 shell 脚本。或者,让合法的 Python 源码与被投毒的编译字节码并排发货。两种情况里扫描器读到的文本都很干净,因为扫描器读到的文本本来就不是载荷。
  • 干脆不带载荷。这就是 SkillCloak 的结果,也是三者中最强的一种:自解压式打包让恶意组件完全不出现在安装时可见的那一面,而在执行过程中把它还原出来。静态阅读者根本无物可寻——这正是它对所有受测扫描器的绕过率都在九成以上的原因。
Scanner bypass rate by evasion technique Horizontal bar chart of measured bypass rates against agent skill scanners. Self-extracting skill packing exceeds ninety per cent against every one of the eight scanners tested. Structural obfuscation reaches ninety-six per cent against the single hybrid scanner and exceeds eighty per cent against most purely static scanners. A separate hand-built study, noted below the chart, bypassed the detectors on all three marketplace platforms it tested, with four distinct bypasses. Bypass rate against agent skill scanners 25% 50% 75% 100% Self-extracting packing every one of 8 scanners >90% Structural obfuscation vs. the hybrid scanner 96% Structural obfuscation vs. most static scanners >80% SEPARATELY, BY HAND: EVERY MARKETPLACE DETECTOR TESTED WAS BYPASSED four bypasses across three platforms; three of the four took under an hour each to build
以 1,613 个真实恶意技能为样本测得。打包这一项,是没有补丁可打的那一项。

SkillCloak 那篇论文里值得反复琢磨的细节,并不是绕过率,而是:这些被伪装过的技能在 Claude Code 与 Codex 里跑起来,做的事情和未伪装版本一模一样。逃逸没有让攻击者付出任何能力上的代价——也就是说,这里没有一个可以指望的、能起到限流作用的取舍,没有所谓“隐蔽版本是弱化版攻击”。均衡态就是:所有恶意的东西都会被打包。

往扫描器里塞一个模型,扫描器本身就成了靶子

面对“静态分析读不懂散文”,业界的答案是往扫描器里塞一个模型。这解决了表达力问题,也引入了一个更糟的问题:扫描器如今成了一个组件,它的输入由对手撰写,它的输出是一个安全决策。这正是提示注入所描述的那种暴露面,只不过这回对准的是你的控制平面,而不是你的智能体。

效果如你所料。在 Trail of Bits 的工作中,有一种绕过只是把一份恶意的软件包仓库配置说成是一项常规的企业要求;LLM 扫描器接受了这个说法,把严重级别下调为低。没有混淆,没有编码——扫描器是被人用与技能同一种朴素英语,把自己的发现讲没了的。

这带来一个相当难受的性质。静态扫描器遇到看不懂的东西时,多少还会往“拦下”的方向失败;而基于模型的扫描器无论如何都会给出一个笃定、措辞漂亮的判定,而这个判定所依据的通道,恰恰就是它正被攻击的那条通道。再加一个模型也堵不上这个洞,SkillCloak 的数字正好印证了这点——被结构性混淆以 96% 的比例击穿的,是那个混合式扫描器,而不是这组里最弱的那个。如果你好奇一段被投毒的描述会对工具选择决策造成什么,同样的形状在 MCP 工具投毒里被拆解过。

就算判定完美无缺,它也会在你用到它之前过期

姑且假设有这么一个扫描器:它读到的正是智能体读到的,而且没法被说服。它告诉你的,仍然只是它读过的那个版本的事。OWASP 的 AST07 直接给这件事起了名字:更新漂移——技能在批准之后发生变化,那次批准所依据的评审随即失效,而多数安装追踪的是一个名字,不是一个修订版本。

这就是普通的“检查时刻与使用时刻不一致”,软件包生态十年前就用锁文件和内容哈希解决了。技能大体上还没有,因为分发这一层是从“把这个粘贴到你的 agents 目录里”长出来的,而不是从包管理器长出来的——这正是Agent Plugins 1.0 格式没有触碰的那一点:五家厂商就一个目录结构达成一致,并明确拒绝就安装、来源与权限达成一致。注册表这一侧有着同样的空洞,四个 MCP 索引那篇按索引逐个检视过。

落到实处:如果你说不出自己批准的是哪些字节,那你什么也没批准。这件事就是每个技能一个哈希、仓库里多一个文件,也是本文里成本最低的一条,低出一大截。

真正的产品缺陷是那个对勾

一个会漏报的扫描器,只是一件带假阴性率的工具,而每件安全工具都有假阴性率。这不是问题所在。问题在于市场拿这个结果做了什么:它把未知转换成了已批准,把这次转换印在下载按钮旁边,于是把风险挪到了最没有能力评估它的那个人身上。

基础比率让这件事变得具体。Koi Security 在 2026 年 2 月 1 日审计 ClawHub 时报告:2,857 个技能中有 341 个是恶意的——大约八分之一——而那场攻势最终累计到一千多个恶意条目。在这样一个总体里,一个对打包载荷假阴性率超过九成的徽章,就不是一道弱过滤器了,它更接近于把一个被严重投毒的注册表随机重新贴一遍标签,然后当作尽职调查端出来。

Three places to put the control on a third-party skill Three columns comparing where a control on an installed skill can sit. An install-time scan is cheap and produces an inventory, but it reads a different artefact than the agent runs and expires the moment the skill updates. A content pin plus a review of what the skill may reach fixes the time-of-check problem and costs one hash and one manifest. Run-time capability and egress limits are the only control that binds what the payload can do rather than what the file appears to say, and cost a real boundary in the runtime. Scan at install reads the file as published verdict expires on first update can itself be talked to WORTH KEEPING AS an inventory, not a gate Pin and re-review install by content hash updates re-enter review closes check-versus-use drift COSTS one hash, one lockfile Constrain at run time binds what the payload can do no filesystem, no egress by default indifferent to how it was packed COSTS a real boundary in the runtime
只有最右一栏约束的是行为,而不是外观。

这就指向了这一行里唯一同时挺过三种失败的控制措施。一道能力边界不在乎载荷是不是被打过包、是不是装在 .docx 里、这份文件是不是周二之后又改过,因为它约束的是代码能够触及什么,而不是这份文件看起来说了什么。工作目录以外没有文件系统,没有任何模型能叫得出名字的凭据(见受限凭据),除白名单之外没有出网——也就是智能体出网管控里的那套纪律。一个够不着你云元数据端点的技能,不会因为扫过而变安全,也不会因为没扫而变危险。

反方最强的说法

把扫描器删掉会是错误的结论,而支持保留它的两条理由都很好。

第一,基础比率是双向切割的。ClawHub 那场攻势并不高明——冗长的文档文件,配一个“前置条件”段落,让用户把一条命令粘进终端。这类东西扫描器抓得住,而在一个被投毒的注册表里抓住那些不高明的大多数,即便面对有备而来的攻击者时表现糟糕,也仍是实打实的价值。第二,扫描器会产出治理层真正需要的那件产物:装了什么、它声明了什么、以及什么变了的清单。那就是智能体清单,而几乎没人有。

扫描留着,改掉它的输出物。一次扫描如果吐出的是清单里的一行、以及与上周版本的差异,那它做的是诚实的工作;同一次扫描如果吐出的是列表页上一个绿色徽章,那它在做一个自己撑不起来的断言。

具体该怎么做

如果你的团队要装第三方技能

按内容哈希锁定,并把更新变成一次评审事件,而不是一次静默拉取。然后问那个扫描回答不了的问题:如果这个技能是恶意的,它够得着什么?把文件系统、凭据、网络分开各答一遍,然后去修其中最糟的那个答案。这是每个技能三十分钟的功课,而它胜过任何数量的扫描——理由在智能体供应链安全里讲透了。

如果你在运营一个智能体平台

把边界放在技能底下,而不是它前面。默认拒绝出网、文件系统限定在工作目录,这会把技能审核从一个检测问题变成一个爆炸半径问题,而爆炸半径是你真能框住的东西——见沙箱与安全执行。把扫描器的输出当作喂给这道边界策略的遥测数据,绝不要让它单独充当准入决策。

如果你在经营一个市场

那个徽章就是你的责任风险。把检查了什么、检查的是哪个修订版本、什么时候检查的都公开出来——一份带日期、带版本、读者能据以推理的范围声明。把来源信息和一个可供安装端锁定的哈希也公开出来。任何把一件可变产物渲染成安全/不安全二元判决的东西,都会一次次地朝着伤害你用户的那个方向出错。

可长期沿用的原则:一个检查产物的控制措施,其上限就是你的解析器与真正解释器之间的一致程度;而对智能体技能来说,这种一致根本不存在——真正的解释器是一个模型,它会照着指令去取、去解包、去执行那些你的扫描器从未见过的东西。扫描是为了清单,也是为了拦住不高明的大多数。锁定是为了让你批准的就是跑起来的。但你真正倚仗的那道控制,要放在运行时,那里约束的是能力而不是外观,因为那是唯一一层攻击者无法替你挑选“你在读什么”的地方。

常见问题

那我们应该停用智能体技能扫描器吗?

不应该——应该停止把它的输出当成准入决策。它们能稳定抓住不高明的恶意技能,而在被投毒的注册表里那正是大多数;它们还能产出你治理层需要的清单与变更差异。它们做不到的,是把一个技能认证为安全;任何把“干净”判定读作“已批准”的流程,倚仗的正是研究所说的那件不成立的事。

为什么给扫描器加个 LLM 解决不了这个问题?

因为那把扫描器从一个解析器变成了一个参与者。模型读的是对手写的文本,吐出的是安全判定,于是扫描器继承了完整的提示注入暴露面——在已公开的工作里,有一种绕过只是把一份恶意配置说成企业要求,扫描器就把它降级为低危。实测证据指向同一处:结构性混淆表现最好的对象是那个混合式扫描器(96%),而不是那些纯静态的。

自解压式打包到底是什么?

恶意组件根本不出现在被安装、被评审的那个形态里。发出去的是一个看着无害的包,它在智能体执行过程中把载荷重建出来。静态阅读者无物可寻,因为在它查看的那一刻,那里什么也没有——这就是它对每个受测扫描器绕过率都超过九成的原因,而被还原出来的载荷,行为与原版一模一样。

这套说法对 MCP 服务器也成立吗?

结构相同,只是角色挪了位置。一个 MCP 服务器既发给你要运行的代码,也发给你模型要读的工具描述,于是安装时的产物和运行时的文本都是受攻击者控制的面;而描述可以在你批准之后被服务器改写。锁定与运行时约束是同一个答案;工具描述那一半在 MCP 工具投毒里另行讨论。

只做内容哈希够不够?

必要,但不充分。哈希保证跑起来的就是你评审过的那份,但它对“你的评审到底靠不靠谱”只字未提,而这里的研究恰恰是说评审并不可靠。先锁定,让判定不再过期,然后把力气花在能力限制上——那才是即便评审出错也依然成立的那一部分。

延伸阅读

本站相关:

来源: