Aller au contenu

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. 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) ».
  2. 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.
  3. 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égorieCapacité Opérationnelle Cybercriminelle
offline_accessOpenID / SoclePersistance 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.ReadWriteExchange / GraphAccès programmatique total pour lire, rechercher, modifier et supprimer l’intégralité des emails et pièces jointes de la boîte mail.
Mail.SendExchange / 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.AllSharePoint / OneDriveTéléchargement et chiffrement potentiel de tous les fichiers et bibliothèques accessibles par la victime sur SharePoint et OneDrive.
Contacts.Read / People.ReadAnnuaire / GraphRécolte de l’organigramme interne de l’entreprise pour préparer des attaques de fraude au président (BEC) ultra-ciblées.
Directory.Read.AllAnnuaire (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]
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.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 desc

4.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 desc

5. 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 »
Fenêtre de terminal
# 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 tenant
Remove-MgServicePrincipal -ServicePrincipalId $sp.Id
Write-Host "[+] Service Principal supprimé avec succès de l'annuaire." -ForegroundColor Green
# 4. Révoquer les sessions de l'utilisateur par précaution
Revoke-MgUserSignSession -UserId $victimUPN
Write-Host "[+] Sessions actives de la victime révoquées : $victimUPN" -ForegroundColor Green

5.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 :

Fenêtre de terminal
# Mettre à jour la politique d'autorisation pour interdire le consentement individuel
Update-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 Green