Aller au contenu

Abus du device code flow OAuth & détournement de jetons

Parmi l’ensemble des vecteurs d’accès initial ciblant Microsoft 365, l’abus du flux d’autorisation d’équipement OAuth 2.0 (Device Authorization Grant - RFC 8628) — communément appelé phishing par Device Code Flow — occupe une place à part et particulièrement redoutable. Contrairement au phishing classique ou aux attaques Adversary-in-the-Middle (AiTM), cette attaque dirige la victime vers une URL 100 % légitime de Microsoft (https://microsoft.com/devicelogin), exploite des identifiants d’applications officielles de premier rang et ne requiert aucune infrastructure de domaine malveillant.

Dès lors qu’un collaborateur saisit sur le portail officiel de Microsoft un code généré par l’attaquant et valide son authentification multi-facteurs (MFA), le Security Token Service (STS) de Microsoft délivre des jetons d’accès et de rafraîchissement à hauts privilèges directement dans le terminal de commande du cybercriminel.

Ce guide détaille les mécanismes architecturaux du Device Code Flow, analyse la militarisation des identifiants d’applications Microsoft, décortique la télémétrie dans Entra ID et propose des requêtes KQL de détection ainsi que les politiques de neutralisation.


1. Architecture RFC 8628 vs détournement cybercriminel

Section intitulée « 1. Architecture RFC 8628 vs détournement cybercriminel »

Le flux OAuth 2.0 Device Authorization Grant (RFC 8628) a été spécifié pour des équipements connectés dépourvus de navigateur web ou dotés de capacités de saisie limitées (téléviseurs connectés, consoles IoT, terminaux en ligne de commande) :

sequenceDiagram
autonumber
participant Attacker as Terminal Attaquant (TokenTactics / AADInternals)
participant Entra as Microsoft Entra ID (STS)
participant Victim as Collaborateur Ciblé (Navigateur)
Attacker->>Entra: POST /common/oauth2/v2.0/devicecode (Client ID: Azure CLI)
Entra-->>Attacker: Renvoie user_code (ex: "ABCD-EFGH") + device_code + verification_uri
Note over Attacker: L'attaquant lance une boucle d'interrogation sur /token en tâche de fond
Attacker->>Victim: Leurre de Phishing : « Saisissez le code ABCD-EFGH sur https://microsoft.com/devicelogin »
Victim->>Entra: Navigation vers l'URL officielle https://microsoft.com/devicelogin
Note over Victim: Saisie du code "ABCD-EFGH" + Authentification Mot de Passe & MFA
Entra-->>Victim: Message : « Tentez-vous de vous connecter à Microsoft Azure CLI ? »
Victim->>Entra: Clic sur « Continuer » (MFA validé)
Note over Entra: Négociation validée côté Microsoft !
Entra-->>Attacker: L'interrogation réussit ! Renvoi de l'Access Token + Refresh Token
Note over Attacker: Prise de contrôle programmatique immédiate des ressources du tenant !

Pourquoi cette attaque est particulièrement redoutable :

Section intitulée « Pourquoi cette attaque est particulièrement redoutable : »
  1. Aucun risque de réputation de domaine : le message renvoie vers https://microsoft.com/devicelogin. Aucune passerelle de messagerie (SEG) ni proxy web ne bloque le domaine racine de Microsoft.
  2. Pré-consentement des applications officielles : les attaquants utilisent les identifiants d’applications Microsoft natives (Azure CLI, Microsoft Office, PowerShell). Ces outils étant nativement approuvés dans le tenant, ils ne déclenchent aucun écran d’alerte de consentement administrateur.
  3. Satisfaction complète du MFA : la victime ayant satisfait le challenge MFA, les jetons délivrés encapsulent la preuve de cette validation et déverrouillent l’accès aux ressources protégées par l’Accès Conditionnel.

2. Identifiants d’applications Microsoft détournés (first-party client IDs)

Section intitulée « 2. Identifiants d’applications Microsoft détournés (first-party client IDs) »

Les attaquants initient la demande de code en spécifiant les identifiants d’applications officielles Microsoft :

Nom de l’ApplicationIdentifiant Client (AppId GUID)Privilèges & Capacités par Défaut
Microsoft azure CLI04b07795-8ddb-461a-bbee-02f9e1bf7b46Gestion complète Azure Resource Manager (ARM), API Graph, Azure Key Vault, Stockage Azure.
Azure PowerShell1950a258-227b-4e31-a9cf-717495945fc2Administration complète du tenant et des souscriptions Azure.
Microsoft officed3590ed6-52b3-4102-aeff-aad2292ab01cAccès étendu aux APIs Exchange Online, SharePoint Online, OneDrive et Teams.
Microsoft graph CLI14d82eec-204b-4c2f-b354-c2419e407076Interrogation exhaustive de l’annuaire Graph et attribution de rôles.
Visual Studio872cd9fa-d31f-45e0-9eab-6e460a02d1f1Accès aux dépôts Azure DevOps et aux environnements de développement cloud.

3. Empreinte forensique dans la télémétrie entra ID

Section intitulée « 3. Empreinte forensique dans la télémétrie entra ID »

L’abus du Device Code Flow génère une signature spécifique dans les journaux de connexion. L’analyste doit articuler son analyse autour de deux phases temporelles :

graph TD
subgraph "Phase 1 : Validation du Code (IP de la Victime)"
V_LOG["Enregistrement SigninLogs<br/>AppDisplayName : Microsoft Azure CLI<br/>AuthenticationProtocol : deviceCode<br/>IPAddress : IP Entreprise Victime<br/>ResultType : 0 (Succès)<br/>MFA Satisfied : True"]
end
subgraph "Phase 2 : Exploitation du Jeton (IP de l'Attaquant)"
A_LOG["Enregistrement NonInteractiveUserSignInLogs<br/>AppDisplayName : Microsoft Azure CLI<br/>IPAddress : VPS / Proxy Attaquant<br/>ResourceDisplayName : Windows Azure Service Management API<br/>Jeton émis via Refresh Token"]
end
V_LOG -->|Jeton délivré au pirate| A_LOG

Dans la table SigninLogs sous Log Analytics ou Microsoft Sentinel :

  • AuthenticationProtocol : vaut impérativement deviceCode.
  • AppDisplayName : révèle l’outil ciblé (ex. "Microsoft Azure CLI", "Azure PowerShell").
  • ClientAppUsed : vaut "Mobile Apps and Desktop clients".
  • DeviceDetail : attributs souvent vides ou disparates. L’autorisation s’opère dans le navigateur de la victime, mais le terminal n’est pas lié en tant qu’équipement Azure CLI managé.
  • Discordance d’adresses IP : l’authentification interactive initiale (SigninLogs) émane du réseau légitime de la victime, tandis que les requêtes applicatives suivantes (NonInteractiveUserSignInLogs) proviennent d’ASNs d’hébergeurs (DigitalOcean, AWS, Linode) ou de proxys résidentiels.

4.1 détection de toutes les connexions réussies par device code

Section intitulée « 4.1 détection de toutes les connexions réussies par device code »

Établir la ligne de base et lister toutes les authentifications par Device Code sur le tenant :

SigninLogs
| where TimeGenerated >= ago(14d)
| where ResultType == 0
| where AuthenticationProtocol == "deviceCode"
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress, Location,
ClientAppUsed, DeviceDetail, UserAgent, CorrelationId
| sort by TimeGenerated desc

4.2 détection de rejeu de jeton multi-IP post-device code

Section intitulée « 4.2 détection de rejeu de jeton multi-IP post-device code »

Repérer les cas où une validation Device Code a lieu depuis une IP, suivie dans un délai de 2 heures d’un rafraîchissement de jeton non interactif depuis une adresse IP ou un pays différent pour la même application :

let DeviceCodeAuths = SigninLogs
| where TimeGenerated >= ago(7d)
| where ResultType == 0
| where AuthenticationProtocol == "deviceCode"
| project AuthTime=TimeGenerated, UserPrincipalName, AuthIP=IPAddress,
AuthCountry=Location, AppDisplayName, CorrelationId;
let TokenRefreshes = NonInteractiveUserSignInLogs
| where TimeGenerated >= ago(7d)
| where ResultType == 0
| project RefreshTime=TimeGenerated, UserPrincipalName, RefreshIP=IPAddress,
RefreshCountry=Location, AppDisplayName, ResourceDisplayName;
DeviceCodeAuths
| join kind=inner (TokenRefreshes) on UserPrincipalName, AppDisplayName
| where RefreshTime between (AuthTime .. (AuthTime + 2h))
| where AuthIP != RefreshIP and AuthCountry != RefreshCountry
| project UserPrincipalName, AppDisplayName, AuthTime, AuthIP, AuthCountry,
RefreshTime, RefreshIP, RefreshCountry, ResourceDisplayName
| sort by AuthTime desc

4.3 détection de profils non techniques utilisant des outils développeurs

Section intitulée « 4.3 détection de profils non techniques utilisant des outils développeurs »

Identifier des collaborateurs des fonctions Finance, RH, Juridique ou Direction générale validant des outils en ligne de commande :

let DeveloperApps = dynamic([
"04b07795-8ddb-461a-bbee-02f9e1bf7b46", // Azure CLI
"1950a258-227b-4e31-a9cf-717495945fc2", // Azure PowerShell
"14d82eec-204b-4c2f-b354-c2419e407076" // Graph CLI
]);
SigninLogs
| where TimeGenerated >= ago(14d)
| where ResultType == 0
| where AppId in (DeveloperApps)
| where AuthenticationProtocol == "deviceCode"
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress, Location, UserAgent
| sort by TimeGenerated desc

Pour neutraliser durablement les attaques par Device Code Flow, les organisations doivent déployer des règles d’Accès Conditionnel ciblées :

graph TD
DEF[Architecture Anti-Device Code] --> L1[1. Conformité Matérielle Obligatoire<br/>Exiger un poste Hybride Entra ID ou conforme Intune]
DEF --> L2[2. Forces d'Authentification Avancées<br/>Imposer le MFA résistant au phishing pour les outils admin]
DEF --> L3[3. Ciblage des Groupes Non Techniques<br/>Bloquer explicitement Azure Management pour les profils métiers]
DEF --> L4[4. Alerte SOC & Révocation Instantanée<br/>Règle Sentinel déclenchant Revoke-MgUserSignSession]

5.1 imposer la conformité matérielle sur l’administration azure

Section intitulée « 5.1 imposer la conformité matérielle sur l’administration azure »

Même si un collaborateur valide le code d’authentification, l’attaquant ne pourra pas exploiter le jeton sur sa propre machine si l’Accès Conditionnel exige un terminal conforme pour accéder à "Gestion des ressources Microsoft Azure" :

  • Ressource cible : Gestion des ressources Microsoft Azure (Azure Resource Manager)
  • Utilisateurs : tous les utilisateurs (hors comptes d’urgence / Break-Glass)
  • Contrôle d’accès : Exiger que le périphérique soit marqué comme conforme OU Exiger un périphérique joint à Microsoft Entra hybride

Dès confirmation qu’un collaborateur a validé un code frauduleux :

Fenêtre de terminal
# Prérequis : Module Microsoft.Graph.Users
# Connect-MgGraph -Scopes "User.ReadWrite.All"
$compromisedUPN = "victim@target.com"
Write-Host "[!] RÉVOCATION D'URGENCE POUR LA VICTIME DU DEVICE CODE : $compromisedUPN" -ForegroundColor Red
# Révocation immédiate de tous les refresh tokens
Revoke-MgUserSignSession -UserId $compromisedUPN
Write-Host "[+] Tous les jetons de rafraîchissement et sessions ont été invalidés." -ForegroundColor Green