obsidian-tc:伪造一个 Origin,为什么能读走整个笔记库
原创
云梦DC
云梦DC
云梦安全
2026年10月30日 08:44
河南
去阅读
2026 年 10 月披露的 CVE-2026-77363(GHSA-pqh8-4pj7-32xx,CVSS 9.6)影响 obsidian-tc 的 Streamable HTTP 传输。这个工具把 Obsidian 笔记库包装成 MCP 服务,既能走 stdio,也能在本机开 HTTP 端口供 AI 客户端调用。1.3.1 之前的版本在 HTTP 开启、auth.mode 使用默认值 \"none\" 时,不检查 Origin,也不检查 Host。
本地起了一个测试 vault,里面只放一行虚构的 vault-secret: SK-DEMO-7788。向 1.2.1 的 /mcp 发送一个带 Origin: http://evil.example 的 read_note 请求,HTTP 状态是 200,密钥原样出现在响应里。
漏洞速览
| 项目 | 内容 |
|---|---|
| 漏洞编号 | CVE-2026-77363 / GHSA-pqh8-4pj7-32xx |
| 影响组件 | obsidian-tc < 1.3.1 |
| 修复版本 | 1.3.1 |
| 漏洞类型 | 缺失来源校验导致的 DNS rebinding / 跨站请求(CWE-346) |
| 危害等级 | CVSS 9.6 Critical |
| 触发前提 | HTTP 传输开启,auth.mode 为 none,攻击者能诱导本机浏览器访问恶意页面 |
请求从 127.0.0.1 来,不代表可以信
MCP 的 Streamable HTTP 端点是一个 JSON-RPC 接口。客户端向 /mcp POST initialize,之后可以调用 tools/list、tools/call。obsidian-tc 暴露的工具包括 read_note、write_note、delete_note,以及搜索和目录遍历。
auth.mode 为 none 时,服务端会给调用方一个内置身份 http-local,权限是 [\"*\"]。原来的假设很直接:端口绑在 127.0.0.1,外面连不进来,本机用户自己负责。
但 HTTP 请求不只有源 IP。浏览器还会带上 Host 和 Origin。Host 表示客户端认为自己连的是哪个主机,Origin 表示发起请求的页面来自哪里。1.2.1 对这两个字段都没有判断,于是“连接来自 loopback”被当成了“调用方可信”。
浏览器为什么能把请求送进本机端口
同源策略限制的是页面和请求目标的“源”,不是最终解析到的 IP。页面在 http://evil.example 上,脚本请求 http://evil.example:8765/mcp,浏览器看起来仍然是在访问同一个域名。
DNS rebinding 利用的正是这一点。用户先访问恶意页面,域名解析到攻击者服务器;页面里的脚本随后请求同一域名的 8765 端口,攻击者把 DNS 的 TTL 调低,再把域名改解析到 127.0.0.1。浏览器发出的请求仍然带着 Host: evil.example:8765 和 Origin: http://evil.example,连接却落到本机服务。
服务端如果只检查监听地址,就会把这次请求当成正常本地调用。绑定 loopback 解决的是网络可达范围,认证和来源校验仍然要单独做。
隔离环境里的三组请求
实验在 Windows 上跑了两个实例:1.2.1 监听 8765,1.3.1 监听 8766,二者指向同一份测试 vault。请求体走标准的 MCP initialize 和 tools/call,没有额外伪造认证头。
第一组发给 1.2.1:
POST /mcp
Origin: http://evil.example
tools/call -> read_note(credentials.md)
返回 200,structuredContent.content 里带着 vault-secret: SK-DEMO-7788。没有 token,没有确认框,也没有权限提示。
第二组把同样的 Origin 发给 1.3.1,返回 403 origin not allowed。再把 Host 改成 evil.example:8766、Origin 保持为 http://127.0.0.1:8766,返回 403 host not allowed。
第三组换成本地同源客户端,Host 和 Origin 都是 127.0.0.1:8766,请求正常返回 200,read_note 仍能读到内容。也就是说,修复拦住的是外部来源,没有把正常本地客户端一起挡掉。
一个 vault 里通常有什么
Obsidian 笔记库不只是日记。工程团队会拿它记会议、客户清单、接口地址、部署步骤,偶尔还会贴临时令牌和私钥。拿到 [\"*\"] 权限后,攻击者可以批量读取 .md 文件并回传,也可以改写笔记,把错误命令混进运维记录里。
写入 .obsidian 下的配置还会影响下次打开时的行为。删除或批量覆盖文件则可能直接造成不可逆的数据损失。机器上如果同时开着其他本地 MCP 服务,恶意页面还可以逐个端口探测,把几个默认无认证的接口串起来用。
怎么查有没有被打过
服务端日志先看 POST /mcp 的 Origin 和 Host。正常客户端通常只发同源请求;出现外部域名 Origin,或者 Host 与监听地址不一致,就值得进一步查。1.3.1 之前没有内置的拒绝日志,可以在反向代理或主机侧抓包补上。
主机侧可以看浏览器进程是否在短时间内连接过 8765 这类端口,vault 目录有没有陌生文件、批量修改或删除。DNS 侧则留意极短 TTL 下反复变化的解析记录,尤其是先指向公网、再指向 127.0.0.1 的域名。
升级和收口
优先升级到 1.3.1 或更高版本。新版本会校验 allowedHosts / allowedOrigins,不符合的来源直接 403。
不能马上升级时,先把 auth.mode: \"none\" 改掉,给 HTTP 调用加真实认证和最小 scope;只用本机 AI 客户端的话,优先走 stdio,把 HTTP 监听关掉。必须保留 HTTP 时,把它放到会检查 Host、Origin 的反向代理后面,不要让浏览器可达的网段直接访问端口。
vault 里的长期令牌、私钥最好迁到密钥管理工具,笔记只留引用。MCP 服务用单独的 vault 和系统账号运行,限制它能读写的目录。这样即使来源校验再出问题,损失也不会直接覆盖整个工作区。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论