Attaques par consentement OAuth & phishing d'application
Dans l’écosystème de sécurité du cloud d’entreprise, les attaques par consentement illicite OAuth (ou Application Phishing) représentent l’un des mécanismes de persistance les plus discrets et les plus pérennes à disposition des cybercriminels. Contrairement au vol d’identifiants ou à l’interception de cookies de session, un consentement illicite n’exige pas de dérober le mot de passe de l’utilisateur, ne déclenche aucune invite MFA en phase d’exploitation, survit aux réinitialisations de mot de passe et n’est pas affecté par la non-conformité du terminal physique.
En détournant le framework d’autorisation OAuth 2.0 et le modèle d’application multi-tenant de Microsoft Entra ID, l’attaquant incite un collaborateur à accorder des permissions d’API déléguées très étendues (Mail.ReadWrite, Files.ReadWrite.All, offline_access) à une application cloud malveillante. Une fois l’autorisation validée, l’adversaire interagit directement avec les APIs Microsoft Graph au moyen de jetons porteurs (bearer tokens) émis de manière tout à fait légitime par le Security Token Service de Microsoft.
Ce guide propose une dissection technique approfondie des consentements illicites, analyse le rôle critique des étendues à haut risque, détaille la télémétrie d’audit dans Entra ID et Purview, et présente des requêtes KQL de détection ainsi que les scripts d’urgence de révocation.
1. Architecture du consentement OAuth 2.0 dans Microsoft Entra ID
Section intitulée « 1. Architecture du consentement OAuth 2.0 dans Microsoft Entra ID »Microsoft Entra ID applique le flux d’autorisation par code OAuth 2.0 (RFC 6749) pour permettre à des applications tierces d’accéder aux ressources cloud au nom des utilisateurs :
sequenceDiagram autonumber participant Attacker as Infrastructure Attaquant participant Victim as Navigateur Collaborateur participant Entra as Microsoft Entra ID (STS) participant Graph as API Microsoft Graph / M365
Attacker->>Victim: Leurre de Phishing avec URL d'Autorisation OAuth Note over Victim: Clic sur le lien officiel : https://login.microsoftonline.com/common/oauth2/v2.0/authorize... Victim->>Entra: GET /authorize?client_id=APP_PIRATE&scope=Mail.ReadWrite+offline_access Entra-->>Victim: Affichage de la Mire de Consentement Officielle Microsoft Note over Victim: Fenêtre : « L'application demande l'accès en lecture/écriture à vos emails »<br/>La victime clique sur « Accepter » Entra->>Entra: 1. Instanciation du Service Principal dans le tenant victime<br/>2. Création de l'objet OAuth2PermissionGrant<br/>3. Émission du log Entra Audit : « Consent to application » Entra-->>Victim: Redirection HTTP 302 vers l'URI de l'attaquant avec ?code=AUTH_CODE Victim->>Attacker: Transmission involontaire du code d'autorisation Attacker->>Entra: POST /token (Échange code + secret d'application) Entra-->>Attacker: Renvoi de l'Access Token (JWT) + Refresh Token Attacker->>Graph: Requêtes API Programmatiques (GET /v1.0/me/messages) Note over Attacker: Exfiltration directe sans sollicitation de la victime ni invite MFA !1.1 la relation applicative multi-tenant
Section intitulée « 1.1 la relation applicative multi-tenant »- Tenant de l’attaquant (home tenant) : le pirate enregistre une application dans son propre tenant Microsoft 365, configurée sous l’option « Comptes dans n’importe quel annuaire organisationnel (Tout annuaire Microsoft Entra - Multitenant) ».
- Tenant de la victime (resource tenant) : lorsque la victime clique sur « Accepter », Entra ID instancie automatiquement un objet Service Principal représentant cette application externe locale au tenant cible.
- L’objet de délégation (OAuth2PermissionGrant) : entra ID consigne une entité liant l’identifiant de l’utilisateur (
ObjectId) à celui de l’application, assortie de la liste textuelle des permissions consenties.
2. Permissions critiques & la puissance d’offline_access
Section intitulée « 2. Permissions critiques & la puissance d’offline_access »Lors de l’examen des événements de consentement, les analystes doivent scruter les étendues demandées. Les attaquants ciblent en priorité des autorisations leur permettant une extraction autonome :
| Portée OAuth (Scope) | Catégorie | Capacité Opérationnelle Cybercriminelle |
|---|---|---|
offline_access | OpenID / Socle | Persistance autonome illimitée. Impose à Entra ID la délivrance d’un Refresh Token OAuth 2.0. L’attaquant peut régénérer de nouveaux jetons d’accès pendant des mois sans aucune présence de la victime ni ré-authentification. |
Mail.Read / Mail.ReadWrite | Exchange / Graph | Accès programmatique total pour lire, rechercher, modifier et supprimer l’intégralité des emails et pièces jointes de la boîte mail. |
Mail.Send | Exchange / Graph | Émission de courriels au nom de la victime via l’API Graph, contournant la télémétrie du client Outlook et les journaux locaux. |
Files.Read.All / Files.ReadWrite.All | SharePoint / OneDrive | Téléchargement et chiffrement potentiel de tous les fichiers et bibliothèques accessibles par la victime sur SharePoint et OneDrive. |
Contacts.Read / People.Read | Annuaire / Graph | Récolte de l’organigramme interne de l’entreprise pour préparer des attaques de fraude au président (BEC) ultra-ciblées. |
Directory.Read.All | Annuaire (Admin) | Reconnaissance exhaustive de l’annuaire (utilisateurs, groupes, rôles, terminaux). |
3. Artefacts forensiques dans entra audit & purview UAL
Section intitulée « 3. Artefacts forensiques dans entra audit & purview UAL »Une attaque par consentement illicite laisse des traces explicites dans les journaux d’audit d’Entra ID et dans le Unified Audit Log (UAL) de Purview.
graph TD CONSENT[La Victime Accepte l'Invite de Consentement] --> AUDIT_ENTRA[Table AuditLogs Entra ID] CONSENT --> AUDIT_UAL[Unified Audit Log Purview (UAL)]
AUDIT_ENTRA --> EVT1[Activité : Consent to application<br/>InitiatedBy : victim@target.com<br/>Cible : Nom du ServicePrincipal & AppId] AUDIT_ENTRA --> EVT2[Activité : Add service principal<br/>TargetResources : AppId, ServicePrincipalId] AUDIT_ENTRA --> EVT3[Activité : Add OAuth2PermissionGrant<br/>Scope : Mail.ReadWrite offline_access]
AUDIT_UAL --> UAL_REC[RecordType : AzureActiveDirectory (15)<br/>Opération : Consent to application]
ATT_USE[L'Attaquant Utilise le Jeton via Graph API] --> UAL_GRAPH[Workload : Exchange / SharePoint<br/>AuditData.AppId = AppId Malveillant<br/>AuditData.UserId = victim@target.com]3.1 données du journal d’audit entra ID (Consent to application)
Section intitulée « 3.1 données du journal d’audit entra ID (Consent to application) »L’enregistrement JSON extrait de Microsoft Sentinel ou de l’API Graph se structure ainsi :
{ "activityDateTime": "2026-03-22T10:15:30Z", "activityDisplayName": "Consent to application", "category": "ApplicationManagement", "result": "success", "initiatedBy": { "user": { "id": "11111111-2222-3333-4444-555555555555", "userPrincipalName": "victim@target.com", "ipAddress": "198.51.100.45" } }, "targetResources": [ { "id": "77777777-8888-9999-aaaa-bbbbbbbbbbbb", "displayName": "eSignature Cloud Validator", "type": "ServicePrincipal", "modifiedProperties": [ { "displayName": "ConsentAction.Permissions", "oldValue": "[]", "newValue": "[\"Mail.ReadWrite\",\"Files.ReadWrite.All\",\"offline_access\"]" }, { "displayName": "TargetId.ServicePrincipalNames", "oldValue": "[]", "newValue": "[\"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d\"]" } ] } ]}Points d’investigation clés :
initiatedBy.user.userPrincipalName: l’utilisateur abusé ayant validé l’autorisation.targetResources[0].displayName: le nom trompeur choisi par l’attaquant (ex."eSignature Cloud Validator").modifiedProperties[ConsentAction.Permissions]: la liste exacte des privilèges d’accès cédés à l’application externe.modifiedProperties[TargetId.ServicePrincipalNames]: le GUID de l’application cliente multi-tenant (AppId).
4. Requêtes de chasse KQL en production
Section intitulée « 4. Requêtes de chasse KQL en production »4.1 détection des consentements avec permissions à haut risque
Section intitulée « 4.1 détection des consentements avec permissions à haut risque »Identifier toute opération de consentement où un utilisateur a validé des privilèges de lecture/écriture de boîte mail, de fichiers ou un accès hors-ligne :
let SensitiveScopes = dynamic(["mail.read", "mail.readwrite", "mail.send", "files.read.all", "files.readwrite.all", "offline_access"]);AuditLogs| where TimeGenerated >= ago(30d)| where OperationName == "Consent to application"| extend InitiatedByUser = tostring(InitiatedBy.user.userPrincipalName), InitiatedByIP = tostring(InitiatedBy.user.ipAddress)| extend TargetApp = tostring(TargetResources[0].displayName), AppId = tostring(TargetResources[0].id)| extend ModifiedProps = TargetResources[0].modifiedProperties| mv-expand ModifiedProps| where ModifiedProps.displayName == "ConsentAction.Permissions"| extend GrantedPermissions = tostring(ModifiedProps.newValue)| where GrantedPermissions has_any (SensitiveScopes)| project TimeGenerated, InitiatedByUser, InitiatedByIP, TargetApp, AppId, GrantedPermissions| sort by TimeGenerated desc4.2 corrélation des applications consenties avec l’activité graph API
Section intitulée « 4.2 corrélation des applications consenties avec l’activité graph API »Repérer l’exploitation immédiate d’une application récemment approuvée effectuant des requêtes sur les boîtes mail ou les fichiers SharePoint :
let ConsentedApps = AuditLogs| where TimeGenerated >= ago(14d)| where OperationName in ("Consent to application", "Add service principal")| extend AppId = tostring(TargetResources[0].id)| distinct AppId;CloudAppEvents| where TimeGenerated >= ago(14d)| where ActionType in ("MailItemsAccessed", "FileDownloaded", "SearchQueryInitiatedExchange")| extend Raw = parse_json(RawEventData)| extend UsedAppId = tostring(Raw.AppId)| where UsedAppId in (ConsentedApps)| project TimeGenerated, AccountDisplayName, ActionType, UsedAppId, IPAddress, Raw| sort by TimeGenerated desc5. Protocole de remédiation d’urgence et durcissement
Section intitulée « 5. Protocole de remédiation d’urgence et durcissement »Lorsqu’un consentement illicite est avéré, l’équipe DFIR doit révoquer immédiatement les droits applicatifs et supprimer le Service Principal du tenant.
graph TD DISCOVERY[Consentement Illicite Confirmé] --> STEP1[1. Localiser & Supprimer OAuth2PermissionGrant<br/>Remove-MgOauth2PermissionGrant] STEP1 --> STEP2[2. Supprimer le Service Principal Local<br/>Remove-MgServicePrincipal] STEP2 --> STEP3[3. Révoquer les Sessions de l'Utilisateur<br/>Revoke-MgUserSignSession] STEP3 --> STEP4[4. Verrouiller la Politique de Consentement du Tenant<br/>Bloquer l'auto-consentement utilisateur] STEP4 --> STEP5[5. Activer l'Admin Consent Workflow<br/>Soumettre toute nouvelle application à l'approbation du RSSI]5.1 script PowerShell de révocation et suppression d’urgence
Section intitulée « 5.1 script PowerShell de révocation et suppression d’urgence »# Prérequis : Modules Microsoft.Graph.Applications, Microsoft.Graph.Identity.SignIns# Connect-MgGraph -Scopes "Application.ReadWrite.All","DelegatedPermissionGrant.ReadWrite.All","User.ReadWrite.All"
$targetAppId = "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"$victimUPN = "victim@target.com"
Write-Host "[*] DÉBUT DE LA REMÉDIATION POUR L'APPLICATION OAUTH ILLICITE : $targetAppId" -ForegroundColor Red
# 1. Identifier le Service Principal dans le tenant$sp = Get-MgServicePrincipal -Filter "appId eq '$targetAppId'"if (-not $sp) { Write-Error "Aucun Service Principal trouvé pour l'AppId : $targetAppId" return}Write-Host "[+] Service Principal identifié : $($sp.DisplayName) (Id: $($sp.Id))" -ForegroundColor Yellow
# 2. Identifier et révoquer tous les octrois de permissions déléguées liés à cette application$grants = Get-MgOauth2PermissionGrant -Filter "clientId eq '$($sp.Id)'"foreach ($grant in $grants) { Write-Host "[!] Révocation de l'octroi de permission : $($grant.Id) (Scopes: $($grant.Scope))" -ForegroundColor Yellow Remove-MgOauth2PermissionGrant -OAuth2PermissionGrantId $grant.Id}
# 3. Supprimer définitivement le Service Principal de l'annuaire du tenantRemove-MgServicePrincipal -ServicePrincipalId $sp.IdWrite-Host "[+] Service Principal supprimé avec succès de l'annuaire." -ForegroundColor Green
# 4. Révoquer les sessions de l'utilisateur par précautionRevoke-MgUserSignSession -UserId $victimUPNWrite-Host "[+] Sessions actives de la victime révoquées : $victimUPN" -ForegroundColor Green5.2 durcissement global : bloquer l’auto-consentement utilisateur
Section intitulée « 5.2 durcissement global : bloquer l’auto-consentement utilisateur »Pour interdire définitivement aux collaborateurs d’accorder des droits à des applications multi-tenant non vérifiées :
# Mettre à jour la politique d'autorisation pour interdire le consentement individuelUpdate-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{ AllowedToCreateApps = $false PermissionGrantPoliciesAssigned = @("ManagePermissionGrantsForSelf.microsoft-user-default-low")}Write-Host "[+] Configuration appliquée : Consentement autonome restreint aux seules permissions vérifiées à faible risque." -ForegroundColor Green6. Maillage interne & navigation
Section intitulée « 6. Maillage interne & navigation »- Fiche précédente : 22. Détection Forensique & Investigation des Attaques AiTM
- Fiche suivante : 24. Analyse Télémétrique du Password Spraying & Brute-Force
- Fiches complémentaires :