1402307692/CVE-2026-Mastodon-Streaming-Token-Leakage
GitHub: 1402307692/CVE-2026-Mastodon-Streaming-Token-Leakage
Mastodon Streaming Server OAuth Access Token URL 查询参数泄露漏洞的概念验证,演示了 Token 在多种日志记录面上暴露的风险。
Stars: 0 | Forks: 0
# CVE-2026-XXXXX: Mastodon Streaming Server 中通过 URL 查询参数泄露 OAuth Access Token
## 漏洞概述
| 项目 | 详情 |
|------|---------|
| CVE ID | CVE-2026-XXXXX |
| 受影响产品 | Mastodon (Streaming Server) |
| 受影响版本 | <= 4.5.9 |
| 组件 | streaming/index.js 第 385-401 行 |
| 漏洞类型 | 通过查询字符串暴露敏感信息 |
| CWE | CWE-598 |
| CVSS 3.1 | 7.5 (高危) |
| 攻击向量 | 网络 |
| 报告者 | qitian |
## 漏洞描述
streaming/index.js 第 385-401 行的 accountFromRequest() 函数接受来自 URL 查询字符串的 OAuth access token:
```
// streaming/index.js lines 385-401
const accountFromRequest = (req) => new Promise((resolve, reject) => {
const authorization = req.headers.authorization;
const location = req.url ? url.parse(req.url, true) : undefined;
const accessToken = location?.query.access_token || req.headers['sec-websocket-protocol'];
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// access token accepted from URL query string
if (!authorization && !accessToken) {
reject(new AuthenticationError('Missing access token'));
return;
}
const token = authorization ? authorization.replace(/^Bearer /, '') : accessToken;
// token from query string used as authentication credential
resolve(accountFromToken(token, req));
});
```
当 WebSocket 客户端在 URL 中使用 ?access_token=... 进行连接时,OAuth token 会出现在多个日志记录面上:
1. **浏览器历史记录** — URL 存储在地址栏、书签、历史记录中
2. **反向代理日志** — Nginx、Apache 默认的访问日志会记录包含查询字符串的完整 URL
3. **CDN 日志** — Cloudflare、CloudFront 及其他 CDN 提供商会记录完整的 URL
4. **Referer 头** — 当用户导航到其他页面时,浏览器会在 Referer 头中发送完整的 URL(包含 token)
5. **服务器应用程序日志** — Mastodon 会记录包含查询字符串的完整请求 URL
6. **SIEM/监控系统** — 链接跟踪工具、UTM 网关、安全扫描器会记录完整的 URL
## 影响
- 任何有权访问服务器日志、代理日志、CDN 日志或浏览器历史记录的人都可以提取 URL 中的 OAuth token
- Token 窃取会导致**完全的账户接管** — 读取时间线、通知、私信、执行关注/取关操作
- 由于广泛的日志记录面,此风险显著高于基于请求头的 token 传输方式
## 概念验证
### PoC 1 - 从 URL 查询中接受 Token (WebSocket)
```
# 使用 wscat 或 websocat
wscat -c "ws://TARGET:4000/api/v1/streaming/user?access_token=YOUR_OAUTH_TOKEN"
# 或者使用 Python
python3 -c "import websocket; ws = websocket.create_connection('ws://TARGET:4000/api/v1/streaming/user?access_token=YOUR_OAUTH_TOKEN'); print(ws.recv())"
```
预期结果:服务器接受使用 URL 查询参数中 token 的连接请求。
### PoC 2 - URL 中包含 Token 的 HTTP 请求
```
# Token 位于 URL 中 — 会被记录到所有日志中
curl -i "http://TARGET:4000/api/v1/streaming/user?access_token=YOUR_OAUTH_TOKEN"
```
### PoC 3 - 演示日志暴露
```
# 这是出现在 Nginx 访问日志中的内容:
# "GET /api/v1/streaming/user?access_token=YOUR_OAUTH_TOKEN HTTP/1.1"
# 任何拥有日志访问权限的攻击者都可以提取并重用该 token
```
### PoC 4 - 与 Authorization 头对比(安全方式)
```
# 正确方法 — token 位于 header 中,默认不会被记录
curl -i -H "Authorization: Bearer YOUR_OAUTH_TOKEN" "http://TARGET:4000/api/v1/streaming/user"
```
## 修复方案
**完全移除对 URL 查询 token 的接受:**
```
const accountFromRequest = (req) => new Promise((resolve, reject) => {
const authorization = req.headers.authorization;
// REMOVE: accessToken from URL query string
if (!authorization) {
reject(new AuthenticationError('Missing access token'));
return;
}
const token = authorization.replace(/^Bearer /, '');
resolve(accountFromToken(token, req));
});
```
对于 WebSocket 客户端:请改用 Sec-WebSocket-Protocol 头(在大多数反向代理中默认不记录此头)。
## 时间线
| 日期 | 事件 |
|------|-------|
| 2026-04-22 | 发现漏洞并创建 PoC |
| 2026-04-22 | 向 MITRE / VDB 提交 CVE 报告 |
## 参考文献
- https://github.com/mastodon/mastodon
- https://cwe.mitre.org/data/definitions/598.html
- OWASP: https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
免责声明:此 PoC 仅用于教育和安全研究目的。
标签:Access Token, CISA项目, CVE, CWE-598, GNU通用公共许可证, Mastodon, MITM代理, Node.js, OAuth, PoC, Referer泄露, URL查询参数, WebSocket, Web安全, 中间人攻击, 云资产清单, 令牌泄露, 依赖分析, 分布式拒绝服务攻击防御, 数字签名, 日志泄露, 暴力破解, 流媒体服务器, 漏洞分析, 网络安全, 蓝队分析, 路径探测, 身份验证绕过, 逆向工程, 隐私保护