Home

Home » My Security Research

CVE-2024-9956 - 所有移动浏览器中的 PassKey 账户劫持漏洞

利用浏览器 intents 钓鱼 PassKey 凭据 February 24, 2025 · 11 min · 2244 words · Me | Suggest Changes 目录

在这篇博文中,我将介绍我在所有主流移动浏览器中发现的一个漏洞,该漏洞允许蓝牙范围内的攻击者通过触发 FIDO:/ intents 来劫持 PassKey 帐户。

TLDR: 蓝牙范围内的攻击者能够从移动浏览器上受攻击者控制的页面触发导航到 FIDO:/ URI,从而启动合法的 PassKey 身份验证 intent,该 intent 将在攻击者的设备上收到。 这导致攻击者能够“钓鱼” PassKey 凭据,彻底打破了 PassKey 不可能被钓鱼的假设。

Prelude#

当我在完成利用 BankID 身份验证 和其他跨设备身份验证协议(我希望很快也能发布)的研究时,一个想法一直困扰着我:“当 PassKey 显然支持他们的用例时,这些公司为什么要费力地实现所有这些东西?”。 我只是这样认为,因为在我看来,如果一个人想要一种安全的方式来向他们的用户提供跨设备身份验证,那么最明显和最安全的选择将是 PassKey。 我仍然认为这是真的,但这让我意识到也许 PassKey 的安全性值得研究。

PassKey 研究思路#

我想让这篇博文相对简短,只关注漏洞本身,但我认为如果我分享一些我认为有趣的、我在研究过程中调查过的安全密钥概念,这对社区可能会很有趣。

这些只是我在研究期间调查过的一些要点,这些要点让我深入研究了 PassKey 的兔子洞。我认为应该进一步探索其中很多内容,特别是通过查看不同 Web 应用程序和常见 WebAuthn 库中的具体实现。

这些要点有一个共同点:绕过 origin。 简而言之,当 Web 应用程序想要使用 PassKey 来验证用户身份时,它必须告诉浏览器允许哪些 origins(或 RP)注册和请求该站点的凭据。 否则,任何 origin 都能够告诉你的浏览器“嘿,为我获取你的bankthattotallydoesnotsupportpasskeys.com 的凭据,并验证此用户 pls”,这在我看来应该是一个明确的安全边界,我应该尝试打破它。

尽管这些都是通过利用 PassKey 实现来完成帐户接管的相当有效的方法,但它们依赖于 Webapp 实现中的错误配置(大多数时候需要多个错误配置)。 因此,我决定我的目标是证明 PassKey 是可以被钓鱼的

通过 BLE 实现客户端到认证器的协议#

WebAuthn CTAP 标准描述了客户端(在我们的例子中是浏览器)和认证器应该如何安全地通信以验证用户身份。 认证器可以有不同的形状和大小,但是最常用的设备是 USB 密钥(如 Yubikey)和手机。 这些可以通过 USB、串口、NFC、WiFi、BLE 以及随着浏览器支持的不断扩展,可能会有越来越多的方式进行通信。

因为我没有聪明到可以向你准确地解释这是如何工作的,所以我将给你一句(我不记得在哪里读到的)话,这句话帮助我理解了所有这些:

WebAuthn 只是 Web 的 SSH(privkey-pubkey)。 - 互联网上最聪明的人

真正引起我兴趣的是 BLE 传输方式,这是因为为了实现安全的跨设备身份验证流程(即使用驻留在另一个设备中的凭据来验证你在主设备上使用的服务),确保与请求设备的物理接近是使此流程“防钓鱼”的关键。 这是因为否则页面代理攻击(将合法页面代理到钓鱼站点)和类似 CSRF 的攻击是可能的,这会将我们带回原点,并 使你花哨的身份验证流程仅仅是一个美化过的用户名 + 密码页面

无论如何,我离题了。BLE 是一种确保物理接近性的好方法,因为你的认证器和客户端必须在彼此的 BLE 范围内才能完成身份验证,从而使BankID 劫持等攻击成为不可能。

优点、缺点和二维码#

Web 应用程序告诉浏览器(客户端)启动 WebAuthn 身份验证的方式是使用 javascript 调用:navigator.credentials.get,传递由后端返回的 RP 的公钥,如下所示:

1
2
3

| ``` navigator.credentials.get({ publicKey: credentialRequestOptions.publicKey })

  
---|---  
copy
在受支持的浏览器上,这将弹出一个浏览器内置的提示(页面无法控制),用户可以在其中选择他的认证器。 在 BLE 的情况下,浏览器将生成一个 QR 码并将其显示在弹出窗口中。
![](https://mastersplinter.work/research/passkey/qr.png)
流程非常简单:
  1. 用户扫描 QR 码
  2. 手机操作系统的 PassKey 管理器处理 QR 码数据
  3. 建立 BLE 连接
  4. 浏览器与认证器通信一个随机的 challenge(由后端生成),以使用私钥对其进行签名
  5. 认证器将签名的 challenge 发送回去
  6. 浏览器将所有内容发送回后端,然后你就登录了

当然,我极度简化了,但是你明白了要点(或 ghist?)。 但是,我故意省略了步骤 1.5,操作系统如何知道打开 PassKey 管理器? 当然是 **intents**(也称为深度链接)!

## FIDO:/ Intents[#](https://mastersplinter.work/research/passkey/<#fido-intents>)

我认为你可能已经知道我要说什么了,本质上,QR 码包含一个 `FIDO:/1308640083810730366733271901259...` URI。 这是一种为 WebAuthn 保留的特殊方案,包含一个 base-10 编码的字符串,解码后会生成一个 CBOR blob(另一种序列化内容的方式,由 WebAuthn 标准使用),其中包含认证器启动处理身份验证请求和与客户端通信所需的数据。 CBOR blob 包含:
  * 依赖方的压缩公钥
  * QR 码密钥(因此我认为你不能只劫持 BLE 连接)
  * 注册域的数量(此 passkey 当前与多少个依赖方 (RP) 域关联或被识别)
  * 时间戳
  * 请求类型(本质上是登录/注册)
  * WebAuthn 非可发现标志(允许“无用户名”登录流程)

当我意识到这一点时,我必须尝试从移动浏览器跨域触发此 intent。 它奏效了!

## PassKey 钓鱼[#](https://mastersplinter.work/research/passkey/<#phishing-passkeys>)

现在所有部分都已组合在一起,让我们看一下攻击者如何从移动浏览器中钓鱼 PassKey。 此攻击要针对移动设备上的受害者起作用,有两个明显的先决条件:
  * 一个受攻击者控制的(邪恶的)设备在 BLE 范围内(< 100m)
  * 受害者访问受攻击者控制的页面

后一个要求几乎不算一个要求,考虑到这项研究的范围,但是让我们把它放在那里。 以下是假设满足这两个要求的攻击步骤,我们将在后面讨论更实际的攻击场景:
  1. 受害者单击攻击者控制页面中的链接
  2. 攻击者的设备访问应该被盗帐户的 RP 的登录页面,并启动 WebAuthn 身份验证
  3. 攻击者的设备从 QR 码中提取新鲜的 `FIDO:/` 链接,并将受害者重定向到该链接
  4. 受害者的 PassKey 管理器弹出,后台页面被重定向到合法的 RP 站点(许多浏览器技巧可以做到这一点)
  5. 受害者的认证器连接到攻击者的客户端
  6. 受害者按下“确定”或“继续”(大多数设备只需单击一下 + 生物识别,如果你使用面部识别,则基本上只需单击一下)
  7. 攻击者的设备现在以受害者的身份登录到目标 RP

以下是这些步骤的图表:
![](https://mastersplinter.work/research/passkey/webauthn-attack.png)
步骤 2 和 3 使用无头浏览器自动执行,可以简单地定义浏览器要采取的操作,触发 WebAuthN 请求,然后提取 `FIDO:/`。 我已经构建了一个用于测试这些类型错误的工具,你可以在 <https://github.com/Splinter0/CrossCheck> 中找到使用 FIDO 所需代码更新的工具。

该工具的作用是设置一个托管恶意页面的 Web 服务器,以及无头浏览器实现。 当受害者访问恶意页面时,无头浏览器将触发并将受害者重定向到提取的 URI。 这适用于许多不同的攻击,所以我鼓励你去查看并看看你能想出什么样的创意攻击。

应该注意的是,整个攻击可以通过像 Raspberry Pi 这样的小设备来完成,允许攻击者随意放置这些设备并远程控制它们,从而在不必在附近的情况下收获会话。

## CVE-2024-9956[#](https://mastersplinter.work/research/passkey/<#cve-2024-9956>)

发现所有主要的移动浏览器都存在漏洞,在这种情况下,漏洞仅仅是允许页面触发 `FIDO:/` intents。 所有修复都包括将此类 URI 列入导航黑名单。 发现 Safari、Chrome 和 Firefox Mobile 均存在漏洞,以下是对测试 RP <https://webauthn.io/> 完成的所有视频 PoC

### Chromium[#](https://mastersplinter.work/research/passkey/<#chromium>)
### Safari[#](https://mastersplinter.work/research/passkey/<#safari>)
### Firefox[#](https://mastersplinter.work/research/passkey/<#firefox>)
  * <https://nvd.nist.gov/vuln/detail/CVE-2024-9956>
  * <https://issues.chromium.org/issues/370482421>

## 攻击场景[#](https://mastersplinter.work/research/passkey/<#attack-scenarios>)

某些 RP 允许用户在输入其用户名/电子邮件之前简单地单击“使用 PassKey 登录”,这对于此攻击来说是理想的,因为它允许恶意行为者大规模地进行此攻击。 每个受害者都会被提示选择他们的 PassKey,而攻击者无需事先了解或钓鱼受害者的电子邮件。 但是,我认为这两种都是有效的攻击场景,下面我将尝试用两个实际的例子来解释它们。

### AirPort Free Wi-Fi[#](https://mastersplinter.work/research/passkey/<#airport-free-wi-fi>)

在这里,攻击者已经为多个社交登录设置了自动化的浏览器步骤,例如 Apple、Google 也许还有 LinkedIn(只是选择使用 PassKey 的随机社交平台,而且这些社交平台会强制你选择电子邮件才能继续)。 攻击者会将他们的恶意设备丢弃在拥挤的区域(例如机场),并宣传一个带有登录屏幕的 Wi-Fi 接入点。

登录屏幕会要求受害者提供他们的电子邮件,之后它会为他们提供一个选项“使用社交网络登录以享受免费 Wi-Fi!”。 在受害者选择一个选项后,攻击将开始,受害者将被重定向到 `FIDO:/` url,如果他们落入网络钓鱼陷阱,会将他们有效的会话交给攻击者。 攻击者将简单地使用在之前的网络钓鱼步骤中输入的电子邮件来在目标 RP 上启动 WebAuthn 身份验证。

### The Crypto Heist[#](https://mastersplinter.work/research/passkey/<#the-crypto-heist>)

此场景描述了一种更具针对性的攻击,其中攻击者会仔细计划针对单个受害者。 让我们想象一下 _Bob_,现在 _Bob_ 是一位加密亿万富翁,并且非常重视安全性,尽管他对它了解不多。 但是,他知道的是 PassKey 是不可钓鱼的(_Bob_ 显然没有阅读这篇博文),因此他为他最喜欢的加密货币交易所设置了一个 PassKey,并始终使用它来登录。

他不知道的是,攻击者已经对他进行了大量的 OSINT,他们知道他的电子邮件地址以及他最喜欢的咖啡馆。 当他不注意时,攻击者(我们也应该给他们起个名字,所以我们称她为 _Alice_)将配备 GSM 和 BLE 模块的 Raspberry Pi Pico 滑入了他的背包。 这将允许攻击工具完全在 pi 上运行,也通过使用类似 Cloudflare Tunnels 的东西隧道传输流量来托管恶意页面(尽管不是必需的)。

_Alice_ 购买了一个看起来像 _Bob_ 的加密货币交易所域名的域名,恶意页面将托管在该域名上。 当 _Bob_ 回家时,她发送了一封具有说服力的网络钓鱼电子邮件,他迅速点击了它,因为它显示“你的帐户正在受到攻击!”,用他的 PassKey 登录……什么? 登录不起作用。

与此同时,_Alice_ 安全地回到了家,远程控制 pi,她从无头浏览器中提取会话,并帮助自己拿走了 _Bob_ 的所有硬币。

结束。

## Conclusion[#](https://mastersplinter.work/research/passkey/<#conclusion>)

好吧,我有点沉迷于这个故事了。

我要特别感谢所有浏览器团队相对较快地修复了此问题。

如果你想了解 PassKey 安全性,我将在此处分享一些帮助过我的资源,希望将来我能够撰写更多关于 WebAuthn 安全性的综合文章。

**Hack the planet!**

## Helpful Resources[#](https://mastersplinter.work/research/passkey/<#helpful-resources>)
  * [All libraries](https://mastersplinter.work/research/passkey/<https:/passkeys.dev/docs/tools-libraries/libraries/>)
  * [API standard](https://mastersplinter.work/research/passkey/<https:/www.w3.org/TR/webauthn-2/>)
  * [Intents Research by NDevTK](https://mastersplinter.work/research/passkey/<https:/ndevtk.github.io/writeups/2024/08/01/awas/>)
  * [Attacks on FIDO2 paper](https://mastersplinter.work/research/passkey/<http:/essay.utwente.nl/98532/1/Chen_MA_EEMCS.pdf>)
    * Page 13, `attestationObject`
    * [Security consideations from standard](https://mastersplinter.work/research/passkey/<https:/www.w3.org/TR/webauthn-2/#sctn-security-considerations>)
    * [What the RPs SHOULD do](https://mastersplinter.work/research/passkey/<https:/www.w3.org/TR/webauthn-2/#sctn-rp-operations>)
    * [Security considerations of FIDO2 itself?](https://mastersplinter.work/research/passkey/<https:/fidoalliance.org/specs/fido-v2.0-id-20180227/fido-security-ref-v2.0-id-20180227.html>)
    * [Related work](https://mastersplinter.work/research/passkey/<https:/ieeexplore.ieee.org/document/10179454>)
  * [Another paper with good testing tool](https://mastersplinter.work/research/passkey/<https:/isyou.info/jowua/papers/jowua-v13n2-4.pdf>)
  * [Directory](https://mastersplinter.work/research/passkey/<https:/passkeys.directory/>) Services that have implemented passkeys
  * [More targets](https://mastersplinter.work/research/passkey/<https:/www.yubico.com/works-with-yubikey/catalog/?protocol=5&sort=popular>)
  * [Conformance testing](https://mastersplinter.work/research/passkey/<https:/fidoalliance.org/certification/functional-certification/conformance/>)
  * [Virtual authenticator for easier testing](https://mastersplinter.work/research/passkey/<https:/developer.chrome.com/docs/devtools/webauthn/>)
  * [Cool github issue with security considerations](https://mastersplinter.work/research/passkey/<https:/github.com/keepassxreboot/keepassxc/issues/10287>)
  * [How FIDO2 url works](https://mastersplinter.work/research/passkey/<https:/stackoverflow.com/questions/73224907/what-information-does-fido2-url-contain-and-how-can-we-decode-it-in-swift>)
  * [PassKey BLE bruteforce](https://mastersplinter.work/research/passkey/<https:/insinuator.net/2021/10/change-your-ble-passkey-like-you-change-your-underwear/>)
    * <https://github.com/ttdennis/bluetooth_smp_pocs>
  * [Cool testing tool](https://mastersplinter.work/research/passkey/<https:/www.passkeys-debugger.io>)
  * [Chromium code for BLE fido](https://mastersplinter.work/research/passkey/<https:/source.chromium.org/chromium/chromium/src/+/main:device/fido/cable/v2_handshake.cc;l=508;drc=c879c659bc5f5bc899d3a1608d5e9155b80bc90c>)
  * [Getting the .well-known endpoints](https://mastersplinter.work/research/passkey/<https:/android-developers.googleblog.com/2023/10/make-passkey-endpoints-well-known-url-part-of-your-passkey-implementation.html>)
  * [More chromium code](https://mastersplinter.work/research/passkey/<https:/github.com/chromium/chromium/blob/59416ca0605cbf582c06204d662124b7e6c2ee4f/device/fido/cable/v2_handshake.cc#L309>)
  * [CVE-2021-38299](https://mastersplinter.work/research/passkey/<https:/github.com/advisories/GHSA-6whf-q6p5-84wg>)
  * [CVE-2022-42731](https://mastersplinter.work/research/passkey/<https:/github.com/advisories/GHSA-vw39-2wj9-4q86>)


[Next »Sec-T presentation on BankID Hijacking and other mis-configuration in Cross-Device protocols](https://mastersplinter.work/research/passkey/<https:/mastersplinter.work/research/sec-t/>)
  * [](https://mastersplinter.work/research/passkey/<https:/x.com/intent/tweet/?text=CVE-2024-9956%20-%20PassKey%20Account%20Takeover%20in%20All%20Mobile%20Browsers&url=https%3a%2f%2fmastersplinter.work%2fresearch%2fpasskey%2f&hashtags=>)
  * [](https://mastersplinter.work/research/passkey/<https:/www.linkedin.com/shareArticle?mini=true&url=https%3a%2f%2fmastersplinter.work%2fresearch%2fpasskey%2f&title=CVE-2024-9956%20-%20PassKey%20Account%20Takeover%20in%20All%20Mobile%20Browsers&summary=CVE-2024-9956%20-%20PassKey%20Account%20Takeover%20in%20All%20Mobile%20Browsers&source=https%3a%2f%2fmastersplinter.work%2fresearch%2fpasskey%2f>)
  * [](https://mastersplinter.work/research/passkey/<https:/reddit.com/submit?url=https%3a%2f%2fmastersplinter.work%2fresearch%2fpasskey%2f&title=CVE-2024-9956%20-%20PassKey%20Account%20Takeover%20in%20All%20Mobile%20Browsers>)
  * [](https://mastersplinter.work/research/passkey/<https:/facebook.com/sharer/sharer.php?u=https%3a%2f%2fmastersplinter.work%2fresearch%2fpasskey%2f>)
  * [](https://mastersplinter.work/research/passkey/<https:/api.whatsapp.com/send?text=CVE-2024-9956%20-%20PassKey%20Account%20Takeover%20in%20All%20Mobile%20Browsers%20-%20https%3a%2f%2fmastersplinter.work%2fresearch%2fpasskey%2f>)
  * [](https://mastersplinter.work/research/passkey/<https:/telegram.me/share/url?text=CVE-2024-9956%20-%20PassKey%20Account%20Takeover%20in%20All%20Mobile%20Browsers&url=https%3a%2f%2fmastersplinter.work%2fresearch%2fpasskey%2f>)
  * [](https://mastersplinter.work/research/passkey/<https:/news.ycombinator.com/submitlink?t=CVE-2024-9956%20-%20PassKey%20Account%20Takeover%20in%20All%20Mobile%20Browsers&u=https%3a%2f%2fmastersplinter.work%2fresearch%2fpasskey%2f>)

© 2025 [Tobia Righi - Security Researcher](https://mastersplinter.work/research/passkey/<https:/mastersplinter.work/>) · Powered by [Hugo](https://mastersplinter.work/research/passkey/<https:/gohugo.io/>) & [PaperMod](https://mastersplinter.work/research/passkey/<https:/github.com/adityatelange/hugo-PaperMod/>)[](https://mastersplinter.work/research/passkey/<#top> "Go to Top \(Alt + G\)")