Aller au contenu

Accueil / Sécurité et identité

Entra ID: le flux device code et son détournement

Flux device code dans Entra ID: requêtes /devicecode et /token, hameçonnage par code, traces dans les journaux et blocage par l'accès conditionnel.

Rémi Vernier
11 novembre 2026 · 9 min de lecture

Un utilisateur peut-il donner à un attaquant l’accès à sa boîte aux lettres sans jamais saisir son mot de passe ailleurs que sur une vraie page Microsoft, MFA comprise? Oui. Il lui suffit de taper sur microsoft.com/devicelogin un code à 9 caractères que l’attaquant lui a envoyé. Le flux OAuth 2.0 device authorization grant fait ensuite le reste, et c’est le poste de l’attaquant qui reçoit les jetons.

Le mécanisme est connu depuis longtemps, mais il revient régulièrement dans des campagnes réelles, et la parade principale se règle dans l’accès conditionnel. Sur ce sujet j’ai voulu être exhaustif et descendre dans le détail: les échanges eux-mêmes, ce que l’attaquant en tire, ce que les journaux montrent et la manière de fermer la porte.

Device code flow?

Le flux est normalisé par la RFC 8628, OAuth 2.0 Device Authorization Grant. Il a été pensé pour les appareils qui n’ont pas de navigateur ou qui ont un clavier très limité: télévision connectée, outil en ligne de commande, salle de réunion Teams, etc… L’appareil ne fait pas d’authentification lui-même. Il demande un code au serveur d’autorisation, l’affiche, et l’utilisateur va s’authentifier sur un autre appareil, en général son PC ou son téléphone.

Contrairement au flux authorization code, il n’y a ni redirect_uri ni navigateur entre l’appareil et le serveur d’autorisation. Le seul lien entre la session de l’utilisateur et l’appareil qui recevra les jetons est le user_code. Toute la question de sécurité vient de là: rien dans le protocole ne garantit que l’appareil qui a demandé le code se trouve physiquement à côté de la personne qui le saisit.

La RFC décrit elle-même ce scénario dans sa section 5.4 « Remote Phishing ».

Les échanges /devicecode et /token

Pour suivre les échanges, nous utilisons une petite maquette:

  • Tenant de test: test.onmicrosoft.com (identifiant rangé dans la variable TENANT)
  • Application inscrite: app-devicecode-test, avec le paramètre « Allow public client flows » à « Yes » dans l’onglet « Authentication » (sans ce paramètre une application que nous avons inscrite nous-mêmes ne peut pas utiliser le flux)
  • Utilisateur: alice@test.onmicrosoft.com, soumise à la MFA
  • Poste client: srv01, sur lequel nous lançons les requêtes avec curl

Demande du code

Le client envoie une requête POST au point de terminaison /devicecode, sans aucun secret: c’est un client public. La documentation Microsoft du flux décrit les trois paramètres: le tenant dans l’URL, le client_id et les scopes demandés.

shell
curl -s -X POST "https://login.microsoftonline.com/$TENANT/oauth2/v2.0/devicecode" \
  -d "client_id=$CLIENT_ID" \
  --data-urlencode "scope=openid profile offline_access User.Read"

Nous obtenons une réponse de ce type:

json
{
  "user_code": "F7KQ2N8XM",
  "device_code": "DAQABIQEAAABVrSpeuWamRam2jAF1XRQE...",
  "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 F7KQ2N8XM to authenticate."
}

-> device_code: la valeur longue et opaque, qui reste sur le poste qui a fait la demande. C’est elle qui permettra de récupérer les jetons.

-> expires_in et interval: la durée de validité du couple de codes (ici 15 minutes) et le délai minimal entre deux interrogations du point /token.

Le scope offline_access fait délivrer un refresh token en plus de l’access token: entre les mains d’un attaquant, c’est lui qui transforme quinze minutes de fenêtre en accès durable.

Interrogation du point /token

Pendant que l’utilisateur s’authentifie, le client interroge le point /token toutes les interval secondes avec le grant type propre au flux:

shell
curl -s -X POST "https://login.microsoftonline.com/$TENANT/oauth2/v2.0/token" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
  -d "client_id=$CLIENT_ID" \
  -d "device_code=$DEVICE_CODE"

Tant qu’Alice n’a rien saisi, la réponse est une erreur authorization_pending.

Côté Alice, sur son PC, la séquence est la suivante:

  1. Elle ouvre https://microsoft.com/devicelogin et saisit F7KQ2N8XM.
  2. Elle choisit son compte, saisit son mot de passe et valide la MFA, comme pour n’importe quelle connexion.
  3. Entra ID lui affiche le nom de l’application et lui demande de confirmer qu’elle essaie bien de s’y connecter.

À l’interrogation suivante, srv01 reçoit enfin un JSON contenant access_token, id_token et refresh_token. Ces jetons n’ont jamais transité par le navigateur d’Alice: ils sont remis à celui qui détient le device_code.

Poste de l’attaquant détient le device_code Entra ID /devicecode et /token Navigateur de la victime microsoft.com/devicelogin 1. POST /devicecode 2. device_code + user_code 3. user_code envoyé par courriel, Teams, SMS… 4. code + mot de passe + MFA 5. POST /token (polling) 6. access + refresh token la victime ne voit aucun jeton
Flux device code détourné: le code est demandé par un poste, validé depuis un autre.

Le code de l’attaquant

Dans un détournement, l’attaquant tient le rôle de la télévision. Il lance le flux depuis sa machine, garde le device_code et fait parvenir le user_code à sa cible avec un prétexte: invitation à une réunion Teams, document partagé, demande de « vérification de compte », etc… La victime arrive sur une page réellement hébergée par Microsoft, sous un nom de domaine Microsoft, avec le certificat attendu. Vérifier l’URL ne sert donc à rien, comme le montre l’analyse de Kaspersky sur une campagne.

La MFA est bel et bien validée, par la victime elle-même, sur son propre appareil. Le jeton émis porte donc la mention d’une authentification forte, et l’accès conditionnel évalué au moment de la connexion voit l’adresse IP et l’appareil de la victime, pas ceux de l’attaquant.

Le délai d’expiration de 15 minutes gêne peu l’attaquant: il génère le code au dernier moment, quand la cible ouvre le message ou rejoint l’échange.

Microsoft a documenté une campagne de ce type en février 2025, attribuée à Storm-2372, active depuis août 2024. Les invitations Teams factices y menaient la victime au flux device code, puis l’attaquant utilisait Microsoft Graph pour fouiller les messages du compte compromis. Dans une mise à jour du lendemain, Microsoft signale que l’acteur est passé au client_id de Microsoft Authentication Broker dans le flux.

Ce dernier point a beaucoup d’importance. Avec un client de première partie, l’attaquant n’a besoin d’aucune inscription d’application dans le tenant de la victime ni d’aucun consentement administrateur: le paramètre « Allow public client flows » de nos propres applications ne le concerne pas. Avec le broker, le refresh token obtenu peut en plus servir à inscrire un appareil contrôlé par l’attaquant dans Entra ID, et c’est la porte vers un Primary Refresh Token, dont nous avions détaillé la structure dans l’article Anatomie du Primary Refresh Token Entra ID. On ne parle plus alors d’un jeton applicatif volé mais d’une identité d’appareil installée dans le tenant.

Le rayon d’impact dépend donc surtout des scopes et du client utilisé:

  • l’access token donne environ une heure d’accès aux ressources demandées (Exchange Online, SharePoint, Graph…);
  • le refresh token, valable 90 jours par défaut et renouvelé à chaque usage, permet d’obtenir de nouveaux access tokens, y compris pour d’autres ressources quand le client le permet;
  • depuis une boîte aux lettres compromise, l’attaquant peut relancer le même scénario vers les contacts internes, avec un message qui vient cette fois d’un collègue.

Le rejeu du refresh token et sa révocation font l’objet de l’article Entra ID: refresh token, rejeu et révocation.

Ce que l’on voit dans les journaux

Dans les journaux de connexion Entra ID, la connexion interactive de la victime porte la propriété « Authentication protocol » égale à « Device code flow ». Microsoft documente aussi une seconde propriété, « Original transfer method », visible dans « Basic info »: elle indique qu’une session a été ouverte par device code même quand l’événement courant, par exemple un rafraîchissement de jeton, ne le montre plus. Entra ID appelle ce suivi le protocol tracking.

Attention: l’adresse IP de la connexion interactive est celle de la victime, puisque c’est elle qui s’authentifie. L’adresse de l’attaquant n’apparaît qu’ensuite, dans les connexions non interactives où le refresh token est utilisé. Un rejeu ressemble beaucoup à une application qui renouvelle ses jetons en tâche de fond, et c’est ce qui rend la détection difficile.

Si les journaux sont envoyés dans Log Analytics ou Sentinel, nous pouvons commencer par inventorier l’usage du flux:

code
SigninLogs
| where TimeGenerated > ago(30d)
| where AuthenticationProtocol == "deviceCode"
| project TimeGenerated, UserPrincipalName, AppDisplayName, AppId,
          IPAddress, UserAgent, ResultType, CorrelationId
| order by TimeGenerated desc

Sur la maquette, nous voyons apparaître la connexion d’Alice vers app-devicecode-test, avec le user agent de son navigateur. Pour un tenant de production, plusieurs signaux méritent une corrélation:

  • un utilisateur qui n’a jamais utilisé le flux et qui l’utilise soudain, surtout vers un client de première partie (Azure CLI, Microsoft Authentication Broker, Office…) qui ne correspond pas à son métier;
  • dans AADNonInteractiveUserSignInLogs, des connexions du même utilisateur et de la même application depuis une adresse IP ou un pays différent de la connexion interactive, peu après celle-ci;
  • des user agents très différents dans la même session: le navigateur de la victime d’un côté, un script de l’autre. Une règle de détection Elastic s’appuie sur ce signal;
  • dans les journaux d’audit, l’inscription d’un nouvel appareil pour cet utilisateur juste après la connexion;

Quand la compromission est confirmée, changer le mot de passe ne suffit pas: les refresh tokens déjà émis doivent être révoqués. Microsoft recommande d’appeler revokeSignInSessions:

code
Connect-MgGraph -Scopes "User.RevokeSessions.All"
Revoke-MgUserSignInSession -UserId "alice@test.onmicrosoft.com"

Attention, il faut aussi supprimer tout appareil inscrit par l’attaquant, sinon son identité d’appareil survit à la révocation.

Bloquer le flux avec l’accès conditionnel

La position de Microsoft est claire dans la page Block authentication flows with Conditional Access policy: s’approcher le plus possible d’un blocage total du device code flow, et ne le garder que pour des cas documentés et sécurisés, comme un outil ancien que l’on ne peut pas faire évoluer.

La condition à utiliser s’appelle « Authentication flows ». Elle propose deux valeurs: « Device code flow » et « Authentication transfer » (le passage d’une session du PC vers le mobile par QR code, en préversion à l’heure où j’écris ces lignes). La stratégie se construit ainsi:

  1. Dans le centre d’administration Entra, « Entra ID » puis « Conditional Access » puis « Policies », « New policy ».
  2. « Users »: tous les utilisateurs, en excluant les comptes de secours (break glass) et, si besoin, un groupe d’exception explicite.
  3. « Conditions », « Authentication flows »: « Configure » à « Yes », puis cocher « Device code flow ».
  4. « Grant »: « Block access ».
  5. Activer d’abord la stratégie en « Report-only ».

Le mode « Report-only » sert à vérifier qui utilise encore le flux, en croisant avec la requête KQL vue plus haut. Les cas légitimes que l’on retrouve le plus souvent sont les salles et appareils Teams, certains outils en ligne de commande et des scripts d’administration. Pour les appareils Teams, Microsoft documente une variante de la stratégie qui exclut les groupes de comptes de ressources concernés ainsi que la ressource Device Registration Service.

Attention: la liste d’exclusion devient elle-même une cible. Un compte exclu peut toujours être hameçonné par ce flux. Il faut la garder courte, la réviser régulièrement et, si possible, limiter les exceptions à des emplacements nommés.

Grâce au protocol tracking, les rafraîchissements d’une session ouverte par device code sont aussi évalués par la stratégie. L’outil « What If » de l’accès conditionnel accepte la condition « Device code flow » pour tester avant de passer en mode « On ».

Pour les scripts qui utilisaient ce flux, la sortie se fait en général vers une identité managée ou un principal de service avec certificat. Pour les utilisateurs, la consigne de sensibilisation tient en une phrase: ne jamais saisir sur microsoft.com/devicelogin un code que l’on n’a pas fait afficher soi-même, sur un appareil que l’on a devant soi.

Rémi Vernier

Rémi Vernier

Responsable des opérations cybersécurité dans un grand groupe international. Identité, Active Directory, Entra ID, cloud hybride, et désormais SAP, IA et modèles en local.

Tous les articles → LinkedIn →

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *