WPMgr邀请链接绕过2FA:一条旁路如何吃掉整条不变量

admin 2026-11-13 10:49:50 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

WPMgr 邀请链接绕过 2FA:一条旁路如何吃掉整条不变量

信益安研究院
信益安研究院

信益安信息安全研究院

2026年11月11日 19:04
湖北

去阅读

一句话结论:被邀请链接的密码验证接管了会话签发,绕过了 2FA 挑战门。

阅读说明:本文保留源码路径、关键代码、架构判断、检测与修复细节;为避免把公众号写成操作手册,具体复现命令、自动化脚本和攻击步骤移至受控技术附录。


0. 漏洞定位

WPMgr 的组织邀请接受流程会验证受邀账户的密码。在所审计的代码版本中,该流程随后直接创建登录会话,没有经过用于常规登录的 2FA 挑战门。

关键信息

  • • 缺陷类别:认证流程不一致 / 会话签发旁路
  • • 关键模块:apps/api/internal/invitation/service.go
  • • 根因位置:service.go:322 的 s.sessions.Login(...)
  • • 对照门禁:apps/api/internal/auth/handler.go 的 issueSessionOrChallenge
  • • 影响前提:账户启用 2FA 且存在可用密码;邀请流程可被触发
  • • 风险本质:密码被当作认证终点,第二因子没有成为所有会话签发的共同前置条件

这不是“2FA 被破解”

常规登录流程仍会在密码正确后要求第二因子。问题在于,邀请流程没有复用该认证出口。因此,安全边界被定义在“登录页”,而不是更准确的“会话签发点”。


1. 两条流程的关键差异

正常认证路径

密码验证成功
    ↓
issueSessionOrChallenge
    ↓
读取 TwoFactorEnabled
    ↓
已启用 2FA:返回 challenge,不签发会话
未启用 2FA:创建会话

存在问题的邀请路径

验证邀请令牌 + 验证账户密码
    ↓
认领邀请并写入成员关系
    ↓
SessionManager.Login
    ↓
创建会话

两个流程最终都会使用同一个会话组件,但它们在抵达会话组件前没有汇合:auth 包走统一门禁,invitation 包直接调用会话管理器。

关键判断:底层调用本身没有错;错误在于调用方绕过了“是否必须完成第二因子”的决策。


2. 源码级证据:同一个能力,两种调用语境

会话签发旁路

// apps/api/internal/invitation/service.go:322
if err := s.sessions.Login(ctx, u.ID, tenantID); err != nil {
    // ...
}

这段逻辑位于邀请接受服务中。在密码验证、令牌认领和成员授权完成后,它直接创建会话;在该调用之前没有 TwoFactorEnabled 判断,也没有挑战完成状态。

受保护的对照实现

// apps/api/internal/auth/handler.go(认证门内)
if res.User.TwoFactorEnabled {
    // 返回挑战,暂不创建会话
}

if err := h.sessions.Login(ctx, res.User.ID, res.ActiveTenant); err != nil {
    // ...
}

对照代码说明,系统并非没有 2FA 会话门;而是邀请流程没有被纳入这个门。

应优先检查的调用点

  • • auth/handler.go:常规登录与可信设备流程,受认证门控制。
  • • auth/twofa_handler.go:TOTP、恢复码、WebAuthn 完成后才创建会话。
  • • invitation/service.go:322:邀请接受后创建会话,未进入门禁。

对这类问题,最有价值的审计动作并不是只看登录页,而是枚举全部 sessions.Login、CreateSession、SetCookie 或 token minting 调用,并逐一问:此处如何证明第二因子已经完成?


3. 架构问题:不变量写了,但没有被系统强制执行

原始设计文档把目标描述为“所有会话签发路径都通过一个 2FA 门”。这是一个正确的不变量,但实现上仍有两个薄弱点。

3.1 签发能力跨包暴露

invitation 包可以直接持有 SessionManager 并调用 Login。这使业务模块拥有了本应由认证域集中掌控的高权限能力。

更稳妥的依赖方向应是:

业务流程(邀请 / 邮箱验证 / OIDC)
          ↓
统一认证编排层:决定“挑战”或“签发”
          ↓
仅认证域可调用的会话创建能力

3.2 测试覆盖“已知入口”,而不是“所有签发点”

如果测试只覆盖密码登录、邮箱验证、OIDC 回调和首用户初始化,那么后来加入的邀请流程即使绕开门禁,也未必会让测试失败。

更有效的测试策略包括:

  • • 对生产代码中的所有会话签发调用点建立清单;
  • • 在代码审查或静态分析中禁止业务包直接调用会话创建;
  • • 对“2FA 已开启的用户绝不在挑战前得到会话”写跨流程集成测试;
  • • 将新增认证入口作为威胁建模与安全评审的必填项。

4. 这段邀请代码做对了什么,又漏了什么

做对了:单次邀请的原子性

邀请认领与成员授权采用同一事务语义,避免“令牌已消耗但授权未完成”。这一点很重要,因为单次令牌不能以“先查询、后更新”的方式实现。

if !claimed {
    return AcceptResult{}, domain.Conflict(
        \"invitation_already_used\",
        \"this invitation has already been accepted\",
    )
}

这种处理把并发失败明确为“本次认领没有发生”,避免半完成状态。

做对了:不区分撤销令牌与未知令牌

对外返回相同的“不存在”结果,可避免接口成为令牌状态探测器。这是典型且正确的 anti-oracle 设计。

漏了:认证完成与密码正确被混为一谈

密码校验只是认证的一步。对于启用 2FA 的账户,“认证完成”应当是:

密码 / 已有登录态成立
      +
第二因子挑战完成
      =
允许签发新会话

邀请流程在第一项成立后直接跳到了最后一步,导致原本正确的事务设计与错误的会话签发顺序并存。


5. 为什么修复不能只加一个 if

最直觉的补丁是在密码校验成功后检查 TwoFactorEnabled。但如果这时邀请已经被认领,用户尚未完成第二因子,单次令牌就可能被提前消耗。

因此,修复要同时满足两条约束:

    1. 启用 2FA 的用户在完成挑战前不能获得会话;
    1. 邀请不会因未完成的挑战而进入不可恢复的已消费状态。

可行的工程方向是:

  • • 邀请流程仅创建“待完成”的接受意图;
  • • 认证编排层发起或恢复 2FA 挑战;
  • • 仅当挑战成功,才在同一最终化流程中认领邀请、写入成员关系并创建会话;
  • • 挑战超时或失败时,待处理状态可安全清理或重试。

这也是此类修复最容易踩坑的地方:修住了 2FA 旁路,却意外制造“用户无法再次接受邀请”的可用性故障。


6. 检测:不要只盯单条日志

应用审计中的“邀请已接受”往往看起来完全合法。因此,检测重点应从单事件转向关联分析。

应收集的数据源

  • • 应用 audit_log:关联邀请创建与接受事件。原稿所述表结构不包含 IP、User-Agent。
  • • 反向代理访问日志:获取请求路径、来源、Cookie / Set-Cookie 元数据;必须确认日志已开启且有保留周期。
  • • 会话存储遥测:将会话创建时间与认证状态关联;需控制访问权限与敏感数据暴露。
  • • 2FA 挑战记录:判断会话前是否完成挑战;需处理时钟偏移与挑战有效期。

三类高价值检测思路

  1. 1. 会话创建与挑战缺失的关联:新会话建立附近没有相应的 2FA 成功事件时,产生高优先级告警。
  2. 2. 邀请者与接受者关系异常:邀请创建者和最终接受账户的审计身份不一致时,结合业务上下文复核。
  3. 3. 邀请接受后的会话异常:接受流程后短时间出现从未见过的组织切换、敏感权限操作或来源突变时,进行风险加权。

这些规则都需要基线与豁免机制:新用户首次接受邀请、多设备登录等正常场景可能相似,不能把简单 HTTP 特征直接当成定罪证据。


7. 防御加固优先级

P0:消除旁路

  • • 把所有会话签发收敛到一个认证编排接口;
  • • 禁止 invitation 等业务包直接调用底层会话管理器;
  • • 增加“启用 2FA 后,所有入口均不得在挑战前创建会话”的回归测试。

P1:降低令牌暴露面

若业务允许,避免在创建成员邀请的 API 响应中长期返回原始邀请链接;至少限制返回场景、记录访问、缩短有效期,并避免其进入不必要的日志链路。

P1:完善可追溯性

审计事件应在隐私与合规许可范围内记录可信来源信息,例如来源 IP、User-Agent 摘要、认证强度、会话创建原因和关联挑战 ID。若审计表带哈希链,字段变更需要同步评估哈希计算与迁移策略。

P2:会话使用侧的纵深防御

对组织管理、密钥操作等高风险动作,可校验近期认证强度或第二因子完成状态,避免一次签发层遗漏立刻扩大为全局权限问题。


结语

2FA 的关键不在于登录页面多了一个验证码,而在于它必须成为所有会话创建行为都绕不过去的共同规则。

这次复盘最值得带走的工程结论是:把“创建会话”当成受控能力,而不是普通业务流程的一个便利函数。 一条未纳入认证门的边缘路径,就足以使整条安全不变量失去意义。

信益安信息安全研究院出品 | XinYiAn Security Research Institute

免责声明

本工具及相关技术内容仅供授权渗透测试、攻防演练及安全研究使用。使用者须在取得目标系统所有者书面授权的前提下开展相关工作,并严格遵守《网络安全法》及适用的法律法规。

未经授权对任何系统实施社会工程攻击或渗透测试,属于违法行为,相关法律责任由使用者自行承担,与工具作者及信益安信息安全研究院无关。

发布本文旨在提升安全从业人员的攻防认知与防御能力,请勿将上述技术用于任何非法用途。

🌟感谢您看到这里,您的支持与关注,是我们持续输出内容的最大动力

🌟欢迎加入我们的交流群

评论:0   参与:  0