开源 AI API 网关 sub2api 日前披露了一个高危账户接管漏洞(CVSS 8.8)。攻击者仅需知道目标用户的注册邮箱,无需密码、验证码或任何用户交互,就能将自己的 OAuth 身份绑定到对方账户上,获取全部 API 密钥、账单余额和订阅配额的控制权。
sub2api 是一个在 GitHub 上获得超过 36000 Star 的开源项目,用于将 Claude、OpenAI、Gemini、Grok 等 AI 产品的订阅配额统一为 API 接口对外分发,支持用户间拼车共享订阅。项目使用 Go 语言开发,受影响版本为 v0.1.171 及之前所有版本,截至漏洞披露时尚无正式修复版本发布。

漏洞核心:三层缺陷叠加
漏洞位于 backend/internal/handler/auth_oauth_pending_flow.go 文件,由三个相互叠加的代码缺陷共同构成攻击链。
缺陷一:状态机拦截不完整
ExchangePendingOAuthCompletion 函数负责处理 OAuth 登录流程中的 pending session 交换。该函数只显式拦截了 email_completion 和 bind_login_required 两个步骤,对于 choose_account_action_required(选择账户操作步骤)没有任何拦截逻辑。
这意味着任何未被显式拦截的步骤都会被隐式视为"可以安全执行绑定操作",直接进入后续的身份绑定流程。这一设计违反了安全工程中的 fail-closed(默认拒绝)原则——状态机应当只允许显式声明的安全状态通过,而不是默认放行所有未知状态。
缺陷二:绑定逻辑无条件执行
applyPendingOAuthAdoption 函数在处理"领养决策"时存在严重逻辑错误。该函数接收的 adopt_display_name 和 adopt_avatar 两个布尔值字段仅控制用户昵称和头像等显示属性,与账户所有权毫无关系。
然而函数内部的实现是:只要这两个字段中任一为 true,就会无条件调用 applyPendingOAuthBinding 执行身份绑定。绑定函数会将攻击者的 OAuth 身份信息写入 auth_identities 表,其中 user_id 字段被设为受害者的 ID。
缺陷三:登录意图绕过验证
shouldBindPendingOAuthIdentity 函数本应作为最后一道安全关卡,判断当前 session 是否满足绑定条件。但该函数对 intent == "login"(登录意图)的 session 无条件返回 true,不检查 session 是否处于可颁发令牌的安全状态,也不验证邮箱是否已完成验证。
这构成了最关键的设计反转:匿名、未验证的 login 状态反而比已登录的 bind_current_user 状态拥有更宽松的权限。在安全设计中,匿名 session 应当受到最严格的限制,这里恰好相反。
完整攻击流程
攻击分为四步,全程自动化,不需要受害者参与:
第一步:攻击者用自己的 OAuth 账号(如 linux.do 账号)发起正常登录流程。由于系统中没有该身份的记录,session 进入 choose_account_action_required 状态,提示选择"创建新账户"或"绑定已有账户"。
第二步:攻击者向 /auth/oauth/pending/create-account 接口提交受害者的注册邮箱。系统检测到该邮箱已存在,自动将 session 的 TargetUserID 设为受害者的用户 ID,并标记 existing_account_bindable = true。
这一步的关键问题在于:系统将"邮箱在数据库中存在"这一中立事实,等同于"提交者拥有该邮箱"的授权声明。正确的所有权验证方式(发送验证码到邮箱)在这一路径上被完全跳过。
第三步:攻击者调用 /auth/oauth/pending/exchange 接口,提交仅包含显示属性的字段:
{"adopt_display_name": true, "adopt_avatar": true}由于缺陷一未拦截该步骤,请求落入绑定流程(缺陷二),身份验证函数对 login 意图返回 true(缺陷三),攻击者的 OAuth 身份被永久写入受害者的账户记录。
第四步:攻击者再次使用同一 OAuth 账号登录时,系统在 auth_identities 表中查到先前写入的记录,将攻击者解析为受害者身份,直接颁发受害者的 access token 和 refresh token。

影响范围
漏洞影响所有通过 pending session 流程的 OAuth 提供商:linux.do、OIDC、企业微信、钉钉。GitHub 和 Google 因走验证邮箱路径可能不受影响,但需要额外审计确认。
攻击者完成账户接管后可以:
- 读取受害者绑定的全部上游 API 密钥(Claude、OpenAI、Gemini、Grok 等)
- 消耗预付费余额和订阅配额
- 修改账户资料、支付绑定和密码
- 若受害者在拼车群组中,还可能波及其他成员的数据和配额
该攻击具有持久性——绑定记录会一直存在直到手动删除,攻击者可随时重新登录。同时具备批量扩展能力,若获得邮箱列表可编写脚本批量接管。
CVSS 8.8 评分依据
| 维度 | 值 | 说明 |
|---|---|---|
| 攻击向量 | 网络 | 通过 HTTP API 远程利用 |
| 攻击复杂度 | 低 | 多步 HTTP 请求序列,无需竞态条件 |
| 所需权限 | 低 | 需要任意 OAuth 账号 |
| 用户交互 | 无 | 不需要受害者参与 |
| 机密性 | 高 | 可读取全部 API 密钥和账户数据 |
| 完整性 | 高 | 可修改账户、绑定新身份 |
| 可用性 | 高 | 可耗尽订阅配额、停用服务 |
修复方案
修复 PR #5345 已提交,包含三层修复:
ExchangePendingOAuthCompletion改为 fail-closed 模式,只有 session 处于终端可验证状态时才执行绑定- 邮箱已存在时强制发送验证码,移除
existing_account_bindable快捷路径 shouldBindPendingOAuthIdentity对 login 意图要求同时满足CanIssueTokenPair和HasVerifiedEmail
此外,安全研究者还建议添加 pending session 单次消费机制(防止重放攻击)、IP 异常告警(单个 IP 在 1 小时内绑定 3 个以上不同用户 ID 时触发)、以及考虑彻底移除 choose_account_action_required 状态,改用需要先登录再绑定的显式流程。
时间线
| 日期 | 事件 |
|---|---|
| 2026-08-04 | sub2api v0.1.171 发布(最新受影响版本) |
| 2026-08-06 | 漏洞通过 PR #5345 披露 |
| 2026-08-07 | 安全公告发布 |