orpc 的 OpenAPI 文档生成中存储型 XSS 的概念验证实验室
oRPC 根据 OpenAPI 规范构建 API 文档页面。在 v1.13.9 之前,它使用 JSON.stringify() 将规范直接转储到 <script> 标签中,且不进行任何 HTML 编码。攻击者控制的任何字段(如 info.description)都可以提前关闭该标签并运行脚本。
此仓库为您提供了一个本地实验室来复现该漏洞,并附带一个已修补版本以供对比。
| 字段 | 值 |
|---|---|
| CVE ID | CVE-2026-33331 |
| GHSA | GHSA-7f6v-3gx7-27q8 |
| CWE | CWE-79(Web 页面生成期间输入的不当中和) |
| 产品 | middleapi/orpc |
| 受影响版本 | 所有低于 v1.13.9 的版本 |
| 已修补版本 | v1.13.9 |
| 发现者 | Abhay Kumar (@abhayclasher) |
packages/openapi/src/plugins/openapi-reference.ts 中的易受攻击代码以如下方式渲染规范:
<script id="spec" type="application/json">${JSON.stringify(spec)}</script>
JSON.stringify() 会生成有效的 JSON,但它不会转义 < 或 >。浏览器仍然会将 </script> 视为 HTML 结束标签,即使它位于 <script type="application/json"> 内部。
info.description 中触发执行的载荷:
"</script><script>alert('XSS')</script>"
浏览器看到的是:
<script id="spec" type="application/json">{"info":{"description":"
</script><script>alert('XSS')</script>"}}
</script>
第一个 </script> 关闭了该代码块。其后的所有内容都作为内联 JavaScript 运行。
当应用程序根据用户控制的输入构建 OpenAPI 规范时,此漏洞即适用,而这正是 oRPC 通过其路由器定义所支持的确切场景。
git clone https://github.com/abhayclasher/CVE-2026-33331.git
cd CVE-2026-33331/app
npm install
node server.js
git clone https://github.com/abhayclasher/CVE-2026-33331.git
cd CVE-2026-33331
docker compose up -d
服务器启动后,访问以下 URL:
| URL | 描述 |
|---|---|
http://localhost:3000/docs | 易受攻击的文档页面 — XSS 载荷在加载时执行 |
http://localhost:3000/docs/safe | 应用了 v1.13.9 补丁的相同页面 |
http://localhost:3000/spec.json | 原始 OpenAPI 规范 JSON |
易受攻击的版本会在页面加载时触发 alert(),从而确认脚本执行。已修补版本会安全地渲染相同的恶意规范 — 载荷以纯文本形式显示。
您还可以运行独立的 PoC:
cd poc
node exploit.js
这会在 3000 端口启动一个最小服务器,在无需完整应用程序上下文的情况下演示相同的漏洞。
1. 攻击者控制 OpenAPI 规范中的某个字段(例如 description)
2. oRPC 使用 JSON.stringify(spec) 生成文档 HTML
3. 载荷中的 </script> 提前关闭 <script> 标签
4. 浏览器将后续内容作为内联 JavaScript 执行
┌─────────────────────────────────────────────────────┐
│ 1. 构造恶意规范: │
│ │
│ info.description = │
│ "</script><script>alert('XSS')</script>" │
│ │
│ 2. orpc 将规范嵌入 HTML: │
│ │
│ <script id="spec" type="application/json"> │
│ {"info":{"description":"</script> <-- 标签 │
│ <script>alert('XSS')</script> <-- 载荷 │
│ "}} │
│ </script> │
│ │
│ 3. 浏览器执行 alert('XSS') │
└─────────────────────────────────────────────────────┘
1.13.9 版本引入了 escapeJsonForHtml(),在将 JSON 嵌入 script 标签之前,它会将 HTML 敏感字符替换为 Unicode 转义序列:
const escapeJsonForHtml = (obj) => JSON.stringify(obj)
.replace(/&/g, '\\u0026')
.replace(/'/g, '\\u0027')
.replace(/</g, '\\u003C')
.replace(/>/g, '\\u003E')
.replace(/\//g, '\\u002F');
Unicode 转义之所以有效,是因为它们仍然是有效的 JSON — JSON.parse() 可以重建原始字符串 — 但它们在浏览器的解析器看来永远不会像 HTML。
该补丁还将从 DOM 的 dataset.config 属性中读取 JSON(当值包含括号时会出错)改为直接将转义后的 JSON 赋值给内联变量。
查看完整提交:4f0efa8
CVE-2026-33331/
├── README.md # 本文件
├── docker-compose.yml # Docker 实验室配置
├── app/
│ ├── Dockerfile # 容器构建配置
│ ├── package.json # Node.js 依赖
│ └── server.js # 易受攻击 + 已修补的文档渲染器
└── poc/
└── exploit.js # 独立的最小概念验证
由 Abhay Kumar 报告。此仓库仅供本地教育用途。请勿在公共网络上部署易受攻击的服务器。
仅供教育目的 — 请在隔离环境中使用