Jonathan Elkabas et Tomer Nahum

Note de la rédaction

Ce scénario fait partie d'une série d'exemples démontrant l'utilisation d'EntraGoat, notre environnement de simulation Entra ID. EntraGoat, notre environnement de simulation Entra ID. Vous pouvez lire un aperçu d'EntraGoat et de sa valeur ici.


Mal acquis et dangereux : Manuel du propriétaire pour l'administration mondiale

Nous commençons nos exemples d'utilisation d'EntraGoat avec le scénario 1, que nous avons nommé Misowned and Dangerous : Un manuel du propriétaire pour Global Admin. Cet exercice pratique montre comment la propriété légitime d'une application dans Microsoft Entra ID peut être utilisée pour escalader les privilèges et compromettre un compte d'administrateur global, permettant ainsi une prise de contrôle complète du locataire.

À partir d'un compte d'utilisateur à faible privilège compromis, l'attaquant découvre qu'il est propriétaire d'une application d'entreprise (service principal) à laquelle est attribué un rôle privilégié.

En ajoutant un secret client au principal du service, l'attaquant pivote dans l'identité de l'application, puis utilise son rôle pour réinitialiser le mot de passe de l'administrateur et délivrer un laissez-passer temporaire (TAP) pour obtenir un accès interactif complet au portail Azure.

Ce scénario met l'accent sur les conséquences réelles d'une mauvaise gestion des applications et illustre la différence de comportement, les limites d'accès et l'exposition au risque entre les flux d'authentification délégués et les flux d'authentification propres à l'application.


Vue d'ensemble du chemin d'attaque

  1. L'histoire d'une première prise de pied : L'attaquant s'authentifie en tant qu'utilisateur financier compromis (david.martinez) avec des informations d'identification volées.
  2. Enumération : En utilisant les cmdlets PowerShell et Microsoft Graph, l'attaquant découvre que l'utilisateur possède un principal de service nommé Finance Analytics Dashboard qui se voit attribuer le Privileged Authentication Administrator rôle.
  3. Augmentation des privilèges : L'attaquant ajoute un secret client au principal de service détenu et s'authentifie à l'aide d'informations d'identification propres à l'application, ce qui lui permet d'accéder à l'identité d'un principal de service.
  4. Prise de contrôle du compte : À partir du contexte de l'application seule avec le rôle privilégié, l'attaquant réinitialise le mot de passe de l'administrateur global ou ajoute un TAP pour contourner l'authentification multifactorielle (MFA), puis se connecte interactivement et récupère le drapeau, confirmant ainsi la compromission totale du locataire.

Flux d'attaque

La figure 1 illustre le déroulement de cette attaque.

Figure 1. Flux d'attaque compromettant un compte administrateur global d'EntraGoat

Pourquoi est-il important de comprendre la propriété de l'application ?

Chaque application d'Entra ID possède deux identités distinctes :

  1. Enregistrement de l'application, un objet de définition global dans le locataire d'origine où l'application a été créée à l'origine. Cet objet contient les autorisations blueprint-OAuth2 de l'application (scopes) que l'application peut demander, les URI de redirection pour les flux de connexion, les métadonnées de marque et d'éditeur, et plus encore. Cette identité ne représente pas en soi un principal de sécurité actif dans le locataire.
  2. Principal de service, une instance locale de l'application dans le locataire qui agit comme son identité de sécurité et applique le contrôle d'accès. Un principal de service est automatiquement créé lorsqu'un utilisateur du locataire enregistre une nouvelle application, donne son consentement à une application externe, etc. L'identité du principal de service est ce qu'Entra ID utilise pour assigner des rôles, appliquer des politiques et faire respecter les permissions. C'est aussi l'identité authentifiée lorsque l'application agit seule (contexte d'application seulement).

La propriété des applications est une fonction administrative légitime. Cette conception prend en charge l'administration décentralisée au sein des grandes organisations, permettant aux équipes de gérer leurs propres applications d'entreprise. Elle permet aux développeurs, aux processus d'automatisation et aux propriétaires de services de gérer le cycle de vie et la configuration de leurs applications. Toutefois, cette fonctionnalité devient un handicap lorsque

  • Les applications se voient attribuer des rôles privilégiés
  • La propriété est attribuée à des utilisateurs réguliers sans gouvernance
  • L'hygiène des justificatifs est faible ou non contrôlée

Des principes de service mal gouvernés permettent l'ajout d'informations d'identification et l'accès aux applications uniquement, en contournant les contrôles d'identité tels que l'AMF et l'accès conditionnel.

Cette présentation explique pourquoi les attaquants privilégient les mouvements latéraux en utilisant des principes de service dans les environnements modernes de cloud computing et pourquoi les défenseurs doivent comprendre la différence entre les contextes de sécurité délégués et les contextes de sécurité propres à l'application.


Comment détecter et se défendre contre l'utilisation abusive de la propriété d'un principal de service ?

Comprendre et surveiller la propriété des applications n'est pas optionnel ; il s'agit d'un contrôle de sécurité fondamental dans tout environnement Entra ID. La propriété de l'application permet de gérer les informations d'identification, de configurer les autorisations et de contrôler efficacement l'identité de l'application. Sans visibilité sur la propriété de l'application et l'attribution des permissions, les voies d'escalade des privilèges restent cachées aux défenseurs et aux auditeurs.

Les défenseurs doivent surveiller et établir des corrélations :

  • Quelles sont les applications présentes dans le locataire ?
  • Qui les possède ?
  • Quelles sont les autorisations ou les rôles qui leur sont attribués ?
  • Des utilisateurs non privilégiés possèdent-ils des mandants de service avec un accès élevé ?
  • Quelles sont les applications qui ne sont plus utilisées et qui peuvent être mises hors service ?

Les solutions Semperis permettent de combler les lacunes dans la compréhension de ces questions grâce à plusieurs niveaux de défense, à commencer par les indicateurs d'exposition (IOE) et les indicateurs de compromission (IOC).

Ces indicateurs détectent et alertent automatiquement sur les défauts dangereux et les mauvaises configurations, tels que les politiques d'autorisation trop permissives pour la configuration des applications, les principes de service privilégiés détenus par des non-administrateurs, et les lignes de base de sécurité des applications faibles.


Approfondissement du scénario : Présentation de la solution étape par étape

Examinons les étapes spécifiques à suivre pour simuler une utilisation abusive de la propriété d'une application et comprendre dans quelles conditions elle permet la compromission de l'administrateur global.


Étape 1 : Prise d'ancrage initiale

Nous commençons par le contexte de sécurité d'un analyste financier, david.martinez (Figure 2) qui a saisi ses identifiants d'entreprise lors d'une campagne de phishing.

Figure 2. Informations d'identification compromises d'un utilisateur à faibles privilèges

L'utilisation de la Connect-MgGraph cmdlet, nous nous authentifions en tant qu'utilisateur compromis (Figure 3).

Figure 3. Connexion avec nos informations d'identification volées

Pour comprendre nos capacités actuelles, nous commençons par inspecter le contexte de sécurité de la session authentifiée(figure 4).

Figure 4. Contexte de sécurité de la session authentifiée

Une méthode alternative et bien connue pour s'authentifier en tant qu'utilisateur Entra ID et obtenir un jeton d'accès JWT est d'utiliser des outils d'automatisation tels que BARK1. L'extrait PowerShell suivant démontre comment acquérir et utiliser un jeton d'accès via l'authentification par nom d'utilisateur/mot de passe à partir de la CLI :

      $userToken = Get-MSGraphTokenWithUsernamePassword -Username $UPN -Password $password -TenantID $tenantId
$userAccessToken = $userToken.access_token
$SecureToken = ConvertTo-SecureString $userAccessToken -AsPlainText -Force
Connect-MgGraph -AccessToken $SecureToken

BARK comprend également une fonctionnalité permettant de décoder le JWT résultant, exposant des métadonnées de session détaillées telles que les autorisations déléguées (scp), les rôles de l'annuaire (wids), la méthode d'authentification (amr), et les informations sur les clients (app_displayname), comme Figure 5 montre.

Figure 5. Métadonnées de session décodées

Cette étape de décodage est particulièrement utile pour le triage rapide des privilèges. Par exemple, si le jeton a été délivré à un utilisateur ayant des rôles dans l'annuaire, la fonction wids énumérerait directement les GUID des rôles attribués, tels que le rôle d'administrateur global qui a été attribué à l'entreprise. Figure 6 sans qu'il soit nécessaire de procéder à une énumération supplémentaire de l'API graphique.

Figure 6. L'UID de l'administrateur global révélé

Cependant, ce guide évite de s'appuyer sur des outils ou des abstractions tiers. Toutes les étapes d'énumération et d'exploitation sont exécutées à l'aide de PowerShell natif et d'appels directs à l'API Graph afin de garantir que chaque phase de l'attaque est entièrement transparente et expliquée sur le plan technique.

NOTE : Par défaut, tous les utilisateurs authentifiés dans Entra ID peuvent interroger les données de base du profil d'autres utilisateurs, et des attributs tels que extensionAttribute1–15 ne sont pas classés comme sensibles. Par conséquent, l'indicateur stocké dans le profil de l'administrateur peut techniquement être récupéré immédiatement avec la requête API suivante :

      $uri = "https://graph.microsoft.com/v1.0/users/EntraGoat-admin-s1@334brf.onmicrosoft.com?`$select=onPremisesExtensionAttributes"
$response = Invoke-MgGraphRequest -Uri $uri -Method GET
$response.onPremisesExtensionAttributes.extensionAttribute1

Ce comportement est intentionnel. Le but d'EntraGoat n'est pas de cacher le drapeau mais d'enseigner des techniques réalistes d'escalade de privilèges dans les environnements Entra ID. Il est possible de récupérer le drapeau via un appel API direct, mais l'objectif réel est d'escalader les privilèges, d'accéder au portail Azure en tant qu'utilisateur administrateur, et de voir le drapeau dans leur interface utilisateur - démontrant ainsi le contrôle total sur une identité d'administrateur global. Le drapeau est un artefact gamifié, pas l'objectif principal.

      Invoke-MgGraphRequest -Uri 'https://graph.microsoft.com/v1.0/me?$select=id,userPrincipalName,onPremisesExtensionAttributes' |
    Select-Object @{n='UPN';e={$_.userPrincipalName}},
                  @{n='Id';e={$_.id}},
                  @{n='Flag';e={$_.onPremisesExtensionAttributes.extensionAttribute1}}

Étape 2 : Dénombrement

Comme il s'agit du premier scénario de la série EntraGoat, nous passerons en revue le processus d'énumération et mettrons en évidence les techniques fondamentales d'escalade des privilèges. Dans nos autres scénarios, nous partirons de cette base et nous nous concentrerons directement sur le chemin d'attaque principal, en sautant les étapes de reconnaissance de type CTF.

Avec un accès initial à david.martinezEn cas d'attaque, nous commençons à chercher des moyens d'escalade des privilèges. Notre première tâche consiste à comprendre quels sont les accès dont dispose l'identité compromise.

Nous commençons par vérifier si l'utilisateur s'est vu attribuer des rôles dans l'annuaire(figure 7).

Figure 7. Vérification des rôles des utilisateurs

Les Get-MgRoleManagementDirectoryRoleAssignment La cmdlet ne révèle aucun rôle privilégié, ce qui confirme que l'option david.martinez n'a pas d'accès élevé par défaut. Ensuite, nous examinons l'appartenance à un groupe (Figure 8).

Figure 8. Visualisation de l'appartenance de l'utilisateur à un groupe

L'utilisateur fait seulement partie du groupe de locataires par défaut, auquel chaque utilisateur d'Entra ID est assigné pour les services de collaboration comme SharePoint ou Teams. Pas de chemin d'escalade des privilèges ici non plus.

Nous vérifions également si david.martinez ne possède aucun groupe, puisque les propriétaires de groupes peuvent s'ajouter comme membres et hériter des privilèges du groupe (tels que les rôles attribués). Cette vérification renvoie également un résultat vide (Figure 9).

Figure 9. Vérification de la propriété du groupe de notre utilisateur

Toutefois, la recherche de la propriété du principal responsable du service donne un seul résultat(figure 10).

Figure 10. Découverte que notre utilisateur possède un principal de service

david.martinez possède le principal du service Finance Analytics Dashboard. Ceci est important car les propriétaires des principaux services peuvent gérer les informations d'identification, y compris l'ajout de nouveaux secrets pour s'authentifier en tant que principal service.

Vérifions si le Finance Analytics Dashboard a des permissions assignées (Figure 11).

Figure 11. Vérification des autorisations attribuées au principal de service

Aucun n'est configuré. Ensuite, nous vérifions si des rôles d'annuaire lui ont été attribués(figure 12).

Figure 12. Vérification des rôles de l'annuaire du principal de service

Nous voyons une attribution de rôle, mais elle n'est représentée que par un GUID. Chaque rôle intégré à Entra ID est représenté par un GUID, qui est global et identique dans tous les locataires d'Entra ID. Vous pouvez voir tous les rôles intégrés officiels et leurs GUIDs ici.

Pour transformer le GUID en un nom de rôle lisible par l'homme, nous pouvons utiliser la sortie de la commande Get-MgRoleManagementDirectoryRoleAssignment à Get-MgRoleManagementDirectoryRoleDefinition (Figure 13), qui traduira chaque identifiant de rôle attribué en son nom d'affichage correspondant.

Figure 13. Révélation du nom d'affichage d'un rôle

Privileged Authentication Administrator est un rôle très sensible dans Entra ID. Il permet à son détenteur de gérer les méthodes d'authentification pour tous les utilisateurs, y compris la réinitialisation des paramètres MFA, la configuration de FIDO2 et des options d'ouverture de session sans mot de passe, et la modification des politiques clés qui régissent la façon dont les utilisateurs s'authentifient. C'est pourquoi un pirate, agissant en tant que propriétaire, voudrait certainement y ajouter un secret client et l'utiliser pour influencer le compte Global Administrator.

NOTE : Si vous avez suivi de près la phase d'énumération, vous avez probablement remarqué la verbosité et l'effort manuel requis lors de l'utilisation des cmdlets natifs de Microsoft Graph. (Et si nous possédions 5[0] applications Entra au lieu d'une ?) Il s'agit de l'une des principales limites de l'utilisation exclusive d'outils natifs. Les commandes renvoient souvent des données de bas niveau (telles que les GUID) qui nécessitent des requêtes supplémentaires pour les transformer en informations significatives.

C'est précisément la raison pour laquelle de nombreux cadres d'énumération automatisent ces processus. Ils font abstraction des requêtes répétitives et rationalisent les résultats, ce qui permet d'accélérer l'analyse des privilèges et la prise de décision. Par exemple, pour améliorer la convivialité, nous pouvons écrire des fonctions d'enveloppe simples telles que Find-OwnedServicePrincipals et Get-ServicePrincipalRoles qui sont conçus pour automatiser la découverte de la propriété des PS et résoudre plus efficacement les attributions de rôles dans les répertoires :

      function Find-OwnedServicePrincipals {
    param([string]$UserId)
    
    # Get all service principals in tenant
    $allSPs = Get-MgServicePrincipal -All
    Write-Host "Found $($allSPs.Count) service principals in tenant"
    
    $ownedSPs = @()
    $checkCount = 0
    
    # Check ownership of each service principal
    foreach ($sp in $allSPs) {
        $checkCount++
        if ($checkCount % 50 -eq 0) {
            Write-Host "Checked $checkCount/$($allSPs.Count) service principals..."
        }
        
        try {
            $owners = Get-MgServicePrincipalOwner -ServicePrincipalId $sp.Id -ErrorAction SilentlyContinue
            if ($owners) {
                foreach ($owner in $owners) {
                    if ($owner.Id -eq $UserId) {
                        $ownedSPs += $sp
                        Write-Host "OWNED SERVICE PRINCIPAL FOUND!" -ForegroundColor Red
                        Write-Host "   Name: $($sp.DisplayName)" -ForegroundColor Yellow
                        Write-Host "   SP ID: $($sp.Id)" -ForegroundColor Yellow
                        Write-Host "   App ID: $($sp.AppId)" -ForegroundColor Yellow
                        break
                    }
                }
            }
        } catch {
            continue
        }
    }
    return $ownedSPs
}

function Get-ServicePrincipalRoles {
    param([object]$ServicePrincipal)
    
    Write-Host "Checking roles for: $($ServicePrincipal.DisplayName)"
    
    # Check directory role assignments for the SP
    $roleAssignments = Get-MgRoleManagementDirectoryRoleAssignment -Filter "principalId eq '$($ServicePrincipal.Id)'" -ErrorAction SilentlyContinue
    $roles = @()
    
    if ($roleAssignments) {
        foreach ($assignment in $roleAssignments) {
            $roleDefinition = Get-MgRoleManagementDirectoryRoleDefinition -UnifiedRoleDefinitionId $assignment.RoleDefinitionId
            $roles += $roleDefinition
            Write-Host "   Role: $($roleDefinition.DisplayName)" -ForegroundColor Cyan
        }
    } else {
        Write-Host "   No directory roles assigned"
    }
    
    return $roles
}

Maintenant, pour découvrir tous les principaux services appartenant à l'utilisateur compromis et énumérer les rôles qui leur sont attribués, il suffit d'exécuter la commande Find-OwnedService Principals, comme Figure 14 montre.

Figure 14. Découverte des principaux services possédés

Étape 3 : Pivoter dans le contexte du principal responsable du service

Cette étape met en évidence la raison principale pour laquelle nous avons créé ce scénario et l'avons positionné en premier dans la série : Les principaux services façonnent le paysage de l'escalade des privilèges dans Entra ID. Le mouvement latéral dans le contexte de sécurité d'un service principal et son exploitation pour effectuer des opérations privilégiées est une technique fondamentale que tout défenseur, attaquant et chercheur travaillant avec Entra ID devrait connaître.

Ajoutons un secret client à l'élément Finance Analytics Dashboard pour établir un accès dérobé et s'authentifier en tant que prestataire de services :

      $secretDescription = "EntraGoat-Secret-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
$passwordCredential = @{
    DisplayName = $secretDescription
    EndDateTime = (Get-Date).AddYears(1)
}

$newSecret = Add-MgServicePrincipalPassword -ServicePrincipalId $SP.Id -PasswordCredential $passwordCredential

# Sauvegarder les détails du secret ajouté
$clientSecret = $newSecret.SecretText

Ensuite, déconnectez la session de l'utilisateur actuel avec Disconnect-MgGraph, construire les informations d'identification du client et s'authentifier à l'aide de l'identité du principal du service par le biais de l'option Connect-MgGraph:

      $secureSecret = ConvertTo-SecureString -String $clientSecret -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $SP.AppId, $secureSecret
Connect-MgGraph -TenantId $tenantId -ClientSecretCredential $credential

Et en effet, nous sommes maintenant authentifiés comme la Finance Analytics Dashboard directeur de service (Figure 15).

Figure 15. Démonstration de l'authentification en tant que principal de service

Les Get-MgContext révèle des informations sur le mode d'authentification de la session Microsoft Graph en cours, qui détermine directement le contexte de sécurité, le niveau d'accès et le modèle de permission appliqués. Concentrons-nous sur les champs AuthType et TokenCredentialType car ils exposent la sémantique centrale de l'identité dans Entra ID.

Après s'être authentifié en tant que david.martinezCes valeurs apparaissent comme suit :

      AuthType : Délégué
TokenCredentialType : Navigateur interactif

Il s'agit d'une session déléguée, où le jeton émis représente l'identité de l'utilisateur. L'accès est régi par les rôles attribués à l'utilisateur et les autorisations déléguées, et toutes les opérations sont effectuées au nom de l'utilisateur. Le contexte de la session est limité à ce à quoi l'identité de l'utilisateur a accès, car il hérite des politiques d'accès conditionnel, de l'application de l'AMF et des contraintes PIM.

En revanche, un contexte d'application uniquement, tel qu'un contexte initié par un service principal à l'aide des informations d'identification du client (AppId + Secret + TenantId) fonctionne comme une identité non humaine. Tout client (script, tâche d'arrière-plan, démon) utilisant ces informations d'identification peut s'authentifier en tant qu'application et invoquer les API Microsoft Graph en fonction de ses autorisations d'application, quel que soit le contexte de l'utilisateur. Les politiques d'accès conditionnel, MFA et PIM ne sont pas applicables ou appliquées dans ce flux.

Cette distinction est fondamentale dans la conception de la sécurité de l'identité et explique pourquoi le comportement de l'API, les portées d'accès et les privilèges des jetons diffèrent pour les mêmes cmdlets, en fonction du contexte de la session.

Comme on pouvait s'y attendre, cette distinction a un impact important sur la surface d'attaque et le potentiel d'abus. Les flux délégués sont limités par le contexte de l'utilisateur, tandis que les flux d'applications fonctionnent avec une confiance au niveau du service et souvent avec des limites de privilèges plus larges et moins contrôlées. Pour les attaquants, la compromission d'un secret ou d'un certificat d'application se traduit souvent par un accès persistant à des privilèges élevés qui contourne les mécanismes d'application centrés sur l'identité.


Étape 4 : Prise en charge du compte - connexion en tant qu'administrateur global

Maintenant que nous nous sommes authentifiés en tant qu'identité avec la fonction Privileged Authentication Administrator nous avons la possibilité de réinitialiser les mots de passe (Figure 16) pour tout utilisateur du locataire, y compris l'administrateur global.

Figure 16 : Réinitialisation du mot de passe administrateur

Avec ce niveau d'accès, nous pouvons également assigner un TAP(Figure 17) comme méthode d'authentification, ce qui nous permet de contourner le MFA et de nous connecter directement au portail Azure :

Figure 17. Attribution d'un TAP pour l'authentification

Ensuite, nous nous connectons avec le TAP(Figure 18).

Figure 18. Connexion avec le nouveau TAP

Nous récupérons ensuite le drapeau du scénario(figure 19).

Figure 19. Le drapeau est capturé

Une fois le scénario terminé, nous exécutons le script de nettoyage pour restaurer le locataire EntraGoat à son état d'origine(Figure 20).

Figure 20. Le nettoyage d'EntraGoat nous prépare à notre prochain scénario

Ce scénario montre comment des configurations apparemment légitimes et courantes, telles que l'attribution de la propriété d'une application à un utilisateur standard, peuvent ouvrir la porte à une compromission totale du locataire lorsqu'elles sont associées à des rôles privilégiés.

En s'appuyant sur la propriété d'un principal de service, les attaquants peuvent escalader les privilèges, contourner les défenses centrées sur l'utilisateur telles que le MFA et l'accès conditionnel, et finalement obtenir un accès administrateur global par le biais d'un flux d'application uniquement.

Cet exercice souligne l'importance cruciale d'une gouvernance solide en ce qui concerne les autorisations d'application, la propriété du principal de service et l'attribution des rôles. Dans les environnements cloud modernes, les principaux services sont souvent le maillon faible, et il est essentiel pour les attaquants comme pour les défenseurs de comprendre leur modèle d'identité double, leurs limites d'accès et leur potentiel d'abus.

Le scénario 1 d'EntraGoat jette les bases d'une exploration plus poussée et de nos prochains scénarios. Alors, continuez à pirater !


Relevez le prochain défi d'EntraGoat

Note de bas de page

  1. https://github.com/BloodHoundAD/BARK

Clause de non-responsabilité

Ce contenu est fourni à des fins éducatives et informatives uniquement. Il vise à promouvoir la prise de conscience et la remédiation responsable des vulnérabilités de sécurité qui peuvent exister sur les systèmes que vous possédez ou que vous êtes autorisé à tester. L'utilisation non autorisée de ces informations à des fins malveillantes, d'exploitation ou d'accès illégal est strictement interdite. Semperis n'approuve ni ne tolère aucune activité illégale et décline toute responsabilité découlant d'une mauvaise utilisation du matériel. En outre, la Semperis ne garantit pas l'exactitude ou l'exhaustivité du contenu et n'assume aucune responsabilité pour les dommages résultant de son utilisation.