
Strix发现Better Auth Expo集成零点击账户接管漏洞
来源标题:0-click account takeover in Better Auth
安全团队Strix在Better Auth的Expo集成包@better-auth/expo中发现一个零点击账户接管漏洞,未认证攻击者可诱使应用签名伪造令牌,进而修改受害者邮箱并获取其会话,无需密码或受害者交互。该漏洞已在Better Auth 11月3日的安全版本中修复(GHSA-4wq4-2ppv-q3hg)。仅10月,better-auth在npm的下载量达5080万次,@better-auth/expo为540万次。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
简而言之:Strix 在 @better-auth/expo(Better Auth 的 Expo 集成)中发现了一个严重的零点击账户接管漏洞。未认证的攻击者可以让应用签署一个伪造的 token,然后用它更改受害者的邮箱并获取其会话。无需密码、无需访问收件箱,也不需要受害者进行任何交互。我们已向 Better Auth 报告该问题,并协调披露,配合其 11 月 3 日的安全版本发布(GHSA-4wq4-2ppv-q3hg)。
Better Auth 是一个开源 TypeScript 框架,开发者将其部署在后端以处理注册、登录和会话。Vercel 于 2026 年 7 月收购了该公司。其 Expo 集成将这套认证能力带到了 React Native 应用中。
仅 10 月一个月,npm 就记录到 better-auth 的下载量为 5080 万次,@better-auth/expo 为 540 万次。
对于邮箱更改攻击,只要知道受害者的邮箱地址就足够了,无需任何用户交互。
这一切始于我们把仓库直接交给 Strix。随后它的一个 agent 跟踪了一个 cookie 签名辅助函数进入该移动集成,并发现了一个公开端点,该端点会签署调用者提供的任意 state 值。
下一步是寻找另一个会接受该签名的地方。
使用服务端 @better-auth/expo 插件的应用会暴露一个名为 /expo-authorization-proxy 的端点。它的职责是协助 OAuth 登录:设置 state cookie,然后将用户重定向到身份提供方。
OAuth state 将登录响应与发起该流程的请求绑定在一起。在这里,代理从调用者处获取授权 URL,提取其 state 参数,并用应用的密钥对该值进行签名。它检查了 state 是否存在,但从未检查该 state 是否由 Better Auth 签发。
以下是 Strix 审查的代码中的相关行,省略了 cookie 设置部分: 1const state = url.searchParams.get("state");2// ...3await ctx.setSignedCookie(4 stateCookie.name,5 state,6 ctx.context.secret,7 stateCookie.attributes,8);
调用此端点无需登录。签名会直接通过响应的 Set-Cookie 头返回。
这是一个签名预言机:攻击者选择数据,服务器提供有效签名。密钥始终留在服务器上。
该 agent 在结题报告中记录了这次连接:
Agent 分析 · 结题报告state 仅检查了非空性,因此这是一个使用原始应用密钥对任意字节进行签名的签名预言机——该密钥与 HS256 JWT 使用的是同一个密钥(crypto/jwt.ts)[…]
根 agent 曾专门要求其加密专家调查签名是否可能在不同功能之间交叉使用:
调查计划JWT 签名密钥是否从应用密钥派生,并带有独立的用途/上下文(HKDF info 字符串),从而确保由另一子系统(会话 cookie 签名、OAuth state、邮件 token)生成的 token 不能作为 JWT 使用,反之亦然?
Strix 追踪该密钥进入了 Better Auth 的邮箱验证流程。该流程接受的 token 是 HS256 JWT:一个 header、一个 payload 和一个 HMAC-SHA256 签名。cookie 辅助函数同样使用 HMAC-SHA256,并使用相同的应用密钥。
这给了 Strix 一种方法,让两种操作签署完全相同的字节: 1Cookie: HMAC-SHA256(app_secret, state)2JWT: HMAC-SHA256(app_secret, encoded_header + "." + encoded_payload)
将 JWT 的 header 和 payload 放入 state,请求代理对其进行签名,然后将返回的签名转换为 JWT 所期望的 base64url 编码。结果就是一个邮箱验证端点会接受的 token。
对于代理而言,这些字节是一个 OAuth state 值。对于邮箱处理器而言,它们是更改某人账户的许可。
Strix 构造了一个 token,命名 victim@corp.example 并请求将邮箱更改为 attacker@evil.example。然后它发出了两个请求:一个用于获取签名,另一个将完整的 token 提交到 /verify-email。
以下是该 agent 记录的输出:
签名预言机 → 邮箱验证"oracle status": 302, "state cookie found": true, "oracle signed value equals signing input": true, "verify-email status": 200 邮箱已更改;会话已签发{ "status": true, "user": {
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。