Consentement OAuth, applications furtives & service principals
Tandis que le phishing par consentement utilisateur (étudié dans la Fiche 23) abuse un collaborateur pour obtenir des permissions déléguées, la manipulation d’applications au niveau administratif représente l’échelon suprême de la persistance cloud. En compromettant ou en usurpant des rôles d’administration applicative (Application Administrator ou Cloud Application Administrator), les attaquants passent de sessions utilisateurs éphémères à de véritables backdoors programmatiques autonomes à l’échelle du tenant.
Les cybercriminels déploient cette persistance selon deux vecteurs majeurs :
- L’usurpation de service principals existants : l’injection de nouveaux secrets clients (client secrets) ou de certificats asymétriques sur des applications légitimes déjà pourvues de privilèges étendus (outils de sauvegarde, scanners de vulnérabilités, passerelles CI/CD).
- Le déploiement d’applications fantômes (rogue apps) : la création de nouvelles inscriptions d’applications dotées de permissions applicatives globales (
Directory.ReadWrite.All,RoleManagement.ReadWrite.Directory,Mail.ReadWrite) approuvées par consentement administrateur généralisé.
Ces backdoors s’appuient sur le flux OAuth 2.0 Client Credentials Grant (RFC 6749 Section 4.4). Elles fonctionnent sans aucune présence humaine, n’émettent aucune sollicitation MFA, contournent l’Accès Conditionnel interactif et n’apparaissent que dans les journaux spécifiques des Service Principals.
Ce guide détaille l’architecture des backdoors applicatives, analyse les journaux d’audit et les ServicePrincipalSignInLogs, et fournit des requêtes de détection KQL ainsi que des scripts PowerShell d’éradication.
1. Vecteurs architecturaux : injection de secret vs déploiement d’app fantôme
Section intitulée « 1. Vecteurs architecturaux : injection de secret vs déploiement d’app fantôme »graph TD ADMIN[L'Attaquant Détient les Droits Application Admin] --> VECTOR{Stratégie de Persistance}
VECTOR -->|Stratégie A : Cheval de Troie Applicatif| TROJAN[Injection d'un Nouveau Secret sur un SP Existant<br/>Cible : App de Sauvegarde / Monitoring Légitime<br/>Permissions Préexistantes : Directory.ReadWrite.All] VECTOR -->|Stratégie B : Création d'App Fantôme| ROGUE[Création d'une Nouvelle App & Service Principal<br/>New-MgApplication / New-MgServicePrincipal<br/>Octroi du Consentement Administrateur Global]
TROJAN --> AUTH_FLOW[Flux OAuth 2.0 Client Credentials<br/>POST /token avec client_id + client_secret] ROGUE --> AUTH_FLOW
AUTH_FLOW --> TOKEN_ISSUE[Émission d'un Jeton JWT Applicatif par le STS<br/>AppId : GUID Légitime ou Pirate<br/>Roles : Permissions Applicatives Globales]
TOKEN_ISSUE --> DIRECT_ACCESS[Accès Direct à l'API Microsoft Graph<br/>Aucun Contexte Utilisateur / Pas de MFA / Totalement Autonome]1.1 stratégie a : le cheval de troie sur application légitime
Section intitulée « 1.1 stratégie a : le cheval de troie sur application légitime »C’est la méthode de persistance la plus furtive du cloud. Un tenant d’entreprise héberge en moyenne plusieurs dizaines d’applications d’entreprise approuvées au fil des années, dont beaucoup disposent de privilèges applicatifs considérables.
Plutôt que de créer une nouvelle application visible au SOC, l’attaquant :
- Repère les applications existantes détenant des droits applicatifs critiques.
- Ajoute un mot de passe d’application (client secret) ou téléverse un certificat RSA sur l’objet application légitime.
- Exploite ce secret depuis son infrastructure pour s’authentifier au nom de l’application.
1.2 stratégie b : déploiement d’applications d’entreprise fantômes
Section intitulée « 1.2 stratégie b : déploiement d’applications d’entreprise fantômes »Si aucune application préexistante ne convient, l’attaquant enregistre une nouvelle application, lui assigne des rôles applicatifs et s’accorde le consentement administrateur :
- Attribution de
RoleManagement.ReadWrite.Directory: permet à l’application d’élever n’importe quel compte au rang de Global Administrator par simple appel d’API. - Attribution de
Mail.ReadWrite(de type Application) : permet de lire, modifier ou purger les emails de l’intégralité des boîtes aux lettres du tenant, sans restriction à un utilisateur précis.
2. Mécanismes d’authentification : le client credentials grant
Section intitulée « 2. Mécanismes d’authentification : le client credentials grant »L’exploitation des backdoors de Service Principal s’opère via le flux OAuth 2.0 Client Credentials :
POST /<tenant-id>/oauth2/v2.0/token HTTP/1.1Host: login.microsoftonline.comContent-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=11111111-2222-3333-4444-555555555555&client_secret=VALEUR_DU_SECRET_INJECTE_PAR_L_ATTAQUANT&scope=https://graph.microsoft.com/.defaultStructure du jeton applicatif obtenu (JWT) :
Section intitulée « Structure du jeton applicatif obtenu (JWT) : »{ "aud": "https://graph.microsoft.com", "iss": "https://sts.windows.net/8f3b6a9c-2d1e-4b5a-9f8e-7c6b5a4d3e2f/", "appid": "11111111-2222-3333-4444-555555555555", "app_displayname": "Service de Sauvegarde Cloud Entreprise", "roles": [ "Directory.ReadWrite.All", "Mail.ReadWrite", "Files.ReadWrite.All" ], "idtyp": "app", "tid": "8f3b6a9c-2d1e-4b5a-9f8e-7c6b5a4d3e2f"}Constats forensiques fondamentaux :
idtyp: app: confirme qu’il s’agit d’un jeton purement applicatif. Aucun contexte d’utilisateur (upnousubutilisateur) n’est présent.roles: contient l’ensemble des permissions applicatives autorisées sur tout le tenant.- Ce jeton peut être exploité depuis n’importe quelle adresse IP publique sans validation MFA.
3. Télémétrie et artefacts dans entra ID
Section intitulée « 3. Télémétrie et artefacts dans entra ID »L’investigation impose de corréler deux tables d’audit sous Microsoft Entra ID :
graph TD BACKDOOR[L'Attaquant Injecte un Secret ou Crée une App] --> AUDIT[Table AuditLogs Entra ID] EXPLOIT[L'Attaquant se Connecte avec le Secret] --> SP_SIGNIN[Table ServicePrincipalSignInLogs Entra ID]
AUDIT --> A1[Opération : Update application - Certificates and secrets management<br/>InitiatedBy : UPN Admin / IP<br/>Cible : Nom de l'App & AppId] AUDIT --> A2[Opération : Add app role assignment to service principal<br/>Cible : Nom du rôle e.g. Directory.ReadWrite.All]
SP_SIGNIN --> S1[ServicePrincipalName / AppId<br/>IPAddress : VPS / Proxy Attaquant<br/>AuthenticationProcessingDetails : Client Secret / Certificate]3.1 trace dans le journal d’audit : injection de secret
Section intitulée « 3.1 trace dans le journal d’audit : injection de secret »Lors de l’ajout d’un secret client par script ou portail, AuditLogs enregistre :
OperationName:"Update application - Certificates and secrets management"OU"Add service principal credentials"Category:ApplicationManagementInitiatedBy: l’identité ou l’application ayant opéré l’injection.TargetResources[0].modifiedProperties: dévoile le nom de la clé, la date de début, d’expiration et son identifiantKeyIdentifier.
4. Requêtes de chasse KQL en production
Section intitulée « 4. Requêtes de chasse KQL en production »4.1 détection de l’ajout de nouveaux secrets OU certificats
Section intitulée « 4.1 détection de l’ajout de nouveaux secrets OU certificats »Identifier l’injection de tout nouveau secret ou certificat sur une application existante :
AuditLogs| where TimeGenerated >= ago(30d)| where OperationName in ( "Update application - Certificates and secrets management", "Add service principal credentials", "Add service principal", "Add application")| extend InitiatorUPN = tostring(InitiatedBy.user.userPrincipalName), InitiatorIP = tostring(InitiatedBy.user.ipAddress), InitiatorApp = tostring(InitiatedBy.app.displayName)| extend TargetAppName = tostring(TargetResources[0].displayName), AppId = tostring(TargetResources[0].id)| extend KeyDetails = tostring(TargetResources[0].modifiedProperties)| project TimeGenerated, OperationName, InitiatorUPN, InitiatorApp, InitiatorIP, TargetAppName, AppId, KeyDetails| sort by TimeGenerated desc4.2 détection de l’octroi de rôles applicatifs à haut risque
Section intitulée « 4.2 détection de l’octroi de rôles applicatifs à haut risque »Repérer lorsqu’une application se voit accorder des privilèges d’administration sensibles :
let HighRiskRoles = dynamic([ "Directory.ReadWrite.All", "RoleManagement.ReadWrite.Directory", "AppRoleAssignment.ReadWrite.All", "Mail.ReadWrite", "Files.ReadWrite.All", "User.ReadWrite.All"]);AuditLogs| where TimeGenerated >= ago(30d)| where OperationName == "Add app role assignment to service principal"| extend Initiator = tostring(InitiatedBy.user.userPrincipalName)| extend TargetApp = tostring(TargetResources[0].displayName)| extend ModifiedProps = TargetResources[0].modifiedProperties| mv-expand ModifiedProps| where ModifiedProps.displayName == "AppRole.Value"| extend GrantedRole = tostring(ModifiedProps.newValue)| where GrantedRole has_any (HighRiskRoles)| project TimeGenerated, OperationName, Initiator, TargetApp, GrantedRole| sort by TimeGenerated desc4.3 chasse aux connexions anormales de service principals
Section intitulée « 4.3 chasse aux connexions anormales de service principals »Repérer les authentifications de Service Principals émanant d’hébergeurs VPS ou d’adresses IP inhabituelles :
let HostingASNs = dynamic([14061, 24940, 63949, 16276, 51167]); // DigitalOcean, Hetzner, Linode, OVH, ContaboServicePrincipalSignInLogs| where TimeGenerated >= ago(14d)| where ResultType == 0| where AutonomousSystemNumber in (HostingASNs) or Location != "FR" // Ajuster selon le pays légitime| project TimeGenerated, ServicePrincipalName, AppId, IPAddress, Location, AutonomousSystemNumber, ResourceDisplayName| summarize Signins = count(), Resources = make_set(ResourceDisplayName), IPs = make_set(IPAddress) by ServicePrincipalName, AppId, Location, AutonomousSystemNumber| sort by Signins desc5. Playbook forensique d’inventaire et remédiation PowerShell
Section intitulée « 5. Playbook forensique d’inventaire et remédiation PowerShell »5.1 détection des secrets récemment créés sur tout le tenant
Section intitulée « 5.1 détection des secrets récemment créés sur tout le tenant »Lister tous les secrets et certificats générés au cours des 30 derniers jours :
# Prérequis : Module Microsoft.Graph.Applications# Connect-MgGraph -Scopes "Application.Read.All","AppRoleAssignment.ReadWrite.All"
Write-Host "[*] Examen des secrets et certificats applicatifs sur le tenant..." -ForegroundColor Cyan
$recentThreshold = (Get-Date).AddDays(-30)$allApps = Get-MgApplication -All$compromisedCandidates = @()
foreach ($app in $allApps) { # Vérification des secrets (PasswordCredentials) foreach ($pwd in $app.PasswordCredentials) { if ($pwd.StartDateTime -ge $recentThreshold) { $compromisedCandidates += [PSCustomObject]@{ AppDisplayName = $app.DisplayName AppId = $app.AppId ObjectId = $app.Id CredentialType = "Client Secret" KeyId = $pwd.KeyId CreatedDate = $pwd.StartDateTime ExpirationDate = $pwd.EndDateTime Hint = $pwd.Hint } } }
# Vérification des certificats (KeyCredentials) foreach ($cert in $app.KeyCredentials) { if ($cert.StartDateTime -ge $recentThreshold) { $compromisedCandidates += [PSCustomObject]@{ AppDisplayName = $app.DisplayName AppId = $app.AppId ObjectId = $app.Id CredentialType = "Certificat" KeyId = $cert.KeyId CreatedDate = $cert.StartDateTime ExpirationDate = $cert.EndDateTime Hint = "Empreinte : $($cert.CustomKeyIdentifier)" } } }}
if ($compromisedCandidates) { Write-Warning "[!] $($compromisedCandidates.Count) secret(s) créés depuis moins de 30 jours identifiés :" $compromisedCandidates | Format-Table AppDisplayName, CredentialType, CreatedDate, ExpirationDate, KeyId} else { Write-Host "[+] Aucun secret applicatif suspect récemment créé." -ForegroundColor Green}5.2 révocation immédiate d’un secret illégitime
Section intitulée « 5.2 révocation immédiate d’un secret illégitime »# Révocation d'un secret spécifique sur une application compromise :$appObjectId = "11111111-2222-3333-4444-555555555555"$rogueKeyId = "99999999-8888-7777-6666-555555555555"
Remove-MgApplicationPassword -ApplicationId $appObjectId -KeyId $rogueKeyIdWrite-Host "[+] Secret client malveillant révoqué avec succès." -ForegroundColor Green6. Maillage interne & navigation
Section intitulée « 6. Maillage interne & navigation »- Fiche précédente : 28. Redirections de Mail, Règles de Transport & Abus de Connecteurs
- Fiche suivante : 30. Manipulation des Méthodes d’Authentification comme Persistance
- Fiches complémentaires :
- 02. Microsoft Entra ID : le plan d’identité
- 10. Dissection approfondie des audit logs entra ID
- 19. Kill chain de compromission de compte M365
- 23. Attaques par consentement OAuth & phishing d’application
- 26. Taxonomie des techniques de persistance cloud M365
- 31. Rôles entra, abus de PIM & élévation de privilèges