- Scénario 2 : Graphisez-moi la couronne (et les rôles)
- Voie d'attaque
- Flux d'attaque
- Comment cette attaque contourne-t-elle les contrôles d'authentification normaux ?
- Comment détecter et se défendre contre l'utilisation abusive de l'authentification par application uniquement ?
- Présentation de la solution étape par étape
- Étape 1 : Prendre pied avec un certificat compromis
- Étape 2 : Découvrir les autorisations des applications et élaborer notre stratégie d'attaque
- Étape 3 : Attribution de permissions dangereuses
- Étape 4 : Passage à une session d'administration
- Trouvez vos angles morts
- Relevez le prochain défi d'EntraGoat
- Note de bas de page
- Clause de non-responsabilité
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. Vous pouvez lire un aperçu d'EntraGoat et de sa valeur ici.
Graph Me the Crown (et les rôles)
Le scénario 2 d'EntraGoat montre comment l'authentification basée sur un certificat lié à un principal de service existant et à des autorisations d'application surprivilégiées peut conduire à la compromission de l'administrateur global.
L'attaquant commence par avoir accès à une fuite de certificat qui a été exposée par le biais d'artefacts du pipeline CI/CD. Le certificat est valide pour un principal de service qui a la valeur AppRoleAssignment.ReadWrite.All
l'autorisation de la demande.
En s'authentifiant dans un contexte d'application uniquement, l'attaquant abuse de cette permission pour en attribuer une autre, RoleManagement.ReadWrite.Directory
au même principal de service. Cela permet au principal de service d'auto-attribuer n'importe quel rôle d'annuaire (y compris l'administrateur global) à n'importe quel principal de sécurité de son choix. Enfin, l'attaquant réinitialise le mot de passe de l'administrateur et récupère l'indicateur de scénario.
Ce scénario met l'accent sur les risques liés à la prolifération des certificats, à l'étendue des graphes surprivilégiés et à la nature des jetons d'application uniquement. Il met également en évidence la distinction entre l'application des autorisations via les réclamations de jetons et l'évaluation en temps réel des répertoires, que nous expliquerons dans ce guide.
Vue d'ensemble du chemin d'attaque
- Point d'ancrage initial : L'attaquant obtient un certificat codé en base64 et son mot de passe, qui auraient été divulgués lors d'une reconnaissance du pipeline CI/CD.
- Identification : Le certificat correspond à un service principal existant nommé
Corporate Finance Analytics
. - Abus de pouvoir : L'attaquant s'authentifie à l'aide du certificat et découvre que le principal du service a
AppRoleAssignment.ReadWrite.All
l'autorisation de la demande. - Augmentation des privilèges : L'attaquant utilise cette autorisation pour attribuer
RoleManagement.ReadWrite.Directory
au même directeur de service. - Prise de contrôle des rôles : Avec un accès à la gestion des rôles d'annuaire, l'attaquant ajoute le principal de service en tant que membre du rôle d'annuaire Global Administrator.
- Compromission du compte : l'attaquant réinitialise le mot de passe de l'utilisateur Global Admin et s'authentifie pour récupérer le drapeau.
Flux d'attaque
La figure 1 illustre le déroulement de cette attaque.
Comment cette attaque contourne-t-elle les contrôles d'authentification normaux ?
Ce scénario abuse du modèle d'application de Microsoft Entra ID, où les applications se composent d'un enregistrement global et d'un principal de service local. Les autorisations relatives à l'application sont demandées au niveau de l'enregistrement de l'application et accordées au niveau du principal de service. En outre, les mandants de service peuvent s'authentifier indépendamment des utilisateurs à l'aide du flux d'informations d'identification du client OAuth 2.0.
Lorsqu'un certificat valide est enregistré pour un commanditaire de service (au niveau de son keyCredentials.customKeyIdentifier
), il peut être utilisé pour s'authentifier sans entrée interactive ni authentification multifactorielle (MFA). Ce chemin d'authentification propre à l'application fonctionne en dehors des protections centrées sur l'utilisateur, ce qui permet à un certificat compromis d'accéder directement aux API du graphique avec les autorisations ou les rôles attribués au principal du service.
Cette voie d'attaque combine :
- Faible hygiène des certificats (moins pertinent pour Entra ID)
- Autorisations excessives et non contrôlées pour les applications
- La conception intentionnelle des
AppRoleAssignment.ReadWrite.All
pour contourner* l'expérience de consentement de l'administrateur dans des contextes d'application uniquement
En enchaînant ces conditions, les attaquants peuvent s'authentifier en tant que principal d'un service, accéder à des graphes sensibles tels que RoleManagement.ReadWrite.Directory
de s'octroyer un rôle privilégié (jusqu'à celui d'administrateur global) sans jamais toucher à une session d'utilisateur après avoir obtenu les informations d'identification du principal du service.
NOTE : D'autres voies d'attaque peuvent être enchaînées à celle-ci, car toute identité dotée de la mention Application Administrator
ou Cloud Application Administrator
ou tout responsable de service auprès de la Application.ReadWrite.All
Le rôle de l'app ou la propriété sur le principal de service cible - comme démontré dans l'affaire Scénario 1-peut également tirer parti de cette voie d'attaque en ajoutant des informations d'identification permettant d'accéder au principal du service par une porte dérobée.
Enfin, il est important de faire la distinction entre les différentes utilisations de l'authentification par certificat (CBA). Lorsque la configuration de l'ACA est désactivée au niveau du locataire, cette restriction ne s'applique qu'à l'authentification interactive des utilisateurs, et non au flux d'assertions du client OAuth pour les applications. Les mandants de service peuvent toujours s'authentifier à l'aide de certificats si un justificatif d'identité valide est enregistré dans leur dossier d'authentification. KeyCredentials
propriété. Entra ID traite cela comme un mécanisme d'authentification distinct. Ce découplage architectural peut conduire à de fausses hypothèses sur l'application des politiques de certificats à l'échelle du locataire.
*En fait, cette autorisation ne "contourne" pas le consentement de l'administrateur - c' est le consentement de l'administrateur. L'autorisation elle-même exige que le consentement de l'administrateur soit accordé au départ, puis permet à son détenteur (et à ses adversaires, bien sûr) d'accorder par programme le consentement à d'autres applications dans le cadre de l'interaction machine-machine.
Comment détecter et se défendre contre l'utilisation abusive de l'authentification par application uniquement ?
La posture défensive d'Entra ID doit se concentrer sur la restriction de l'utilisation de permissions d'application étendues et sur la surveillance étroite des mandants de service qui détiennent des rôles sensibles tels que AppRoleAssignment.ReadWrite.All
et RoleManagement.ReadWrite.Directory
.
Les équipes de sécurité doivent
- Auditer régulièrement les principes de service pour les rôles d'application à haut privilège
- Suivre la création de nouvelles informations d'identification sur les mandants de service, en particulier lorsqu'elle est effectuée en dehors de l'automatisation approuvée.
- Détecter les changements dans l'attribution des rôles des applications qui pourraient permettre une escalade des privilèges
Les solutions Semperis fournissent des couches de défense, en commençant par des indicateurs d'exposition (IOE) et des indicateurs de compromission (IOC) qui identifient les configurations à risque et alertent sur les défauts dangereux - tels que les mandants de service avec des permissions sensibles, des politiques d'autorisation trop permissives pour la configuration des applications, et des contrôles manquants autour de la sécurité des mandants de service.
Approfondissement du scénario : Présentation de la solution étape par étape
Examinons les étapes permettant de simuler le contournement des contrôles d'authentification et de comprendre comment cela permet de compromettre l'administrateur global.
Étape 1 : Prise de pied initiale avec un certificat compromis
Nous commençons le scénario 2 avec un certificat compromis(Figure 2), prétendument déversé lors de la reconnaissance du pipeline CI/CD. Il est codé en base64, protégé par un mot de passe et laissé sur place comme une cargaison tombant d'un camion DevOps.
Pour identifier le propriétaire du certificat, nous commençons par le décoder en un objet X509Certificate2 utilisable et nous inspectons ses métadonnées :
$certBase64 = "[BASE64_CERTIFICATE_BLOG]" $certPassword = "GoatAccess!123" $certBytes = [System.Convert]::FromBase64String($certBase64) $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certBytes, $certPassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable) $cert | Select-Object Subject, Issuer, Thumbprint, NotBefore, NotAfter | Format-List
Nous pouvons voir que le certificat est auto-signé et émis pour une application appelée Corporate Finance Analytics
(Figure 3).
Il est également possible de parcourir tous les enregistrements d'applications dans le locataire et de rechercher une empreinte de certificat correspondante. Chaque objet d'application dans Entra ID a un KeyCredentials
qui contient des métadonnées sur les certificats ou les clés publiques associés à l'application et utilisés pour l'authentification dans des contextes réservés à l'application. Chaque entrée comprend un attribut CustomKeyIdentifier
qui stocke l'empreinte du certificat (Figure 4) sous forme binaire et permet la recherche ou la corrélation avec des certificats connus.
La fonction suivante permet d'effectuer la recherche :
function Find-AppRegistrationByThumbprint { param([string]$Thumbprint) # Get all application registrations and check for matching certificate thumbprint $allApps = Get-MgApplication -All foreach ($app in $allApps) { if ($app.KeyCredentials) { foreach ($keyCred in $app.KeyCredentials) { # Compare thumbprints (certificate matching) if ($keyCred.CustomKeyIdentifier) { $credThumbprint = [System.Convert]::ToHexString($keyCred.CustomKeyIdentifier) if ($credThumbprint -eq $Thumbprint) { Write-Host "Certificate match found for: $($app.DisplayName)" -ForegroundColor Cyan return $app } } } } } return $null }
Microsoft Graph ne stocke ni ne renvoie jamais le contenu du certificat ou les clés privées. L'interrogation du KeyCredential
à l'aide de l'API graphique ne révèle que les métadonnées du certificat enregistré, jamais le certificat lui-même, quels que soient les privilèges de l'identité requérante. Cela renforce le besoin critique d'une manipulation et d'un stockage sécurisés des clés privées dans l'ensemble de l'organisation.
Pour identifier le principal de service associé, nous nous authentifions en tant qu'utilisateur à faible privilège en utilisant la fonction Connect-MgGraph
. Une fois que nous avons extrait l'AppId derrière Corporate Finance Analytics
(Figure 5), nous sommes en mesure de nous authentifier directement en tant que principal du service avec le certificat.
REMARQUE : Bien que l'ACA soit désactivée dans le locataire(figure 6), l'authentification du principal de service à l'aide du certificat réussira quand même. En effet, l'ACA basée sur l'utilisateur fait référence à une expérience de connexion interactive basée sur le navigateur, tandis que les principaux services s'appuient sur un flux d'informations d'identification client OAuth 2.0 non interactif utilisant des assertions de certificat. La désactivation de l'ACA n'affecte que l'authentification interactive des utilisateurs à l'aide de certificats et n'a aucun impact sur l'authentification programmatique des principaux services. Il s'agit de voies d'authentification distinctes dans la plateforme Entra ID.
Étape 2 : Découvrir les autorisations des applications et élaborer notre stratégie d'attaque
Après s'être authentifié en tant que principal du service à l'aide du certificat, nous vérifions son roles
dans le JWT accordé via le Get-MgContext
et découvrir qu'il contient AppRoleAssignment.ReadWrite.All
(Figure 7). Cette autorisation permet au principal de service d'attribuer TOUT rôle d'application à tous les principaux de service, y compris à lui-même.
Pour escalader, nous énumérons le principal de service Microsoft Graph (GraphAggregatorService
), qui contient tous les rôles OAuth assignables et qui est présent dans chaque locataire d'Entra en tant qu'application de première partie. Elle est instanciée à partir d'un enregistrement d'application global hébergé dans le propre locataire de Microsoft et peut être identifiée par l'AppId statique de 00000003-0000-0000-c000-000000000000
. Comme toutes les applications multi-locataires, elle apparaît dans chaque locataire client en tant que directeur du service local-l'identité réelle utilisée pour appliquer le contrôle d'accès. (Nous avons abordé le modèle d'application Entra ID dans la section Scénario 1.)
L'AppId dans Figure 8 pointe vers la définition globale de Microsoft Graph, qui contient tous les rôles OAuth attribuables qui définissent des champs d'autorisation réservés aux applications, tels que Directory.Read.All
ou RoleManagement.ReadWrite.Directory
. Chaque locataire détient une instance de principal de service local de l'application globale, et Les autorisations OAuth sont attribuées au niveau du principal de service. via AppRoleAssignment
. Cela permet aux applications de fonctionner indépendamment des utilisateurs, avec les champs d'application définis dans le manifeste global de l'application, mais appliqués par le principal de service local du locataire.
Fait amusant : il existe actuellement 576 autorisations graphiques uniques !
Maintenant que nous avons localisé le principal du service Microsoft Graph et que nous disposons de ses autorisations OAuth, l'étape suivante consiste à choisir l'autorisation à utiliser pour l'escalade. La permission qui a le plus d'impact pour l'objectif de notre attaque est RoleManagement.ReadWrite.Directory
Selon une note publiée sur le site officiel de l la documentation (Figure 9) :
Ce niveau d'accès est dangereux de par sa conception. Il donne un contrôle programmatique complet sur les API de gestion des rôles d'Entra ID et permet une escalade directe des privilèges sans interaction avec l'utilisateur.
Étape 3 : Attribution de permissions dangereuses
Nous pouvons attribuer cette permission à notre principal de service compromis à l'aide des commandes suivantes :
$roleManagementRole = $graphSP.AppRoles | Where-Object { $_.Value -eq "RoleManagement.ReadWrite.Directory" } $appRoleAssignmentParams = @{ PrincipalId = $SP.Id ResourceId = $graphSP.Id AppRoleId = $roleManagementRole.Id } New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $SP.Id -BodyParameter $appRoleAssignmentParams
Après l'exécution de cette commande, bien que nous ayons accordé au principal de service le droit d'accès à l'information, nous ne sommes pas parvenus à le faire. RoleManagement.ReadWrite.Directory
l'autorisation, telle qu'elle a été inspectée par Get-MgContext
-nous n'observons aucun changement dans le contexte de sécurité actuel (Figure 10). Il s'agit d'un comportement attendu.
Les jetons d'accès délivrés par Entra ID sont comme des instantanés statiques des droits accordés au moment où le jeton a été délivré. Lorsque Connect-MgGraph
est exécuté, il agit comme un client OAuth2.0 et initie un flux d'authentification contre le point de terminaison du jeton (https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
). Ce point d'accès valide les informations d'identification présentées et émet un jeton d'accès JWT signé avec les revendications de l'appelant (comme les rôles de l'application, les portées et le contexte du locataire) sur la base de l'état d'autorisation actuel au moment de la demande.
Comme les jetons d'accès ne sont pas rafraîchis dynamiquement lorsque les permissions changent, les nouveaux rôles d'application (comme celui que nous venons d'ajouter) n'apparaîtront pas tant qu'un nouveau jeton n'aura pas été explicitement obtenu. Pour obtenir les nouveaux privilèges, nous devons mettre fin à la session existante (Disconnect-MgGraph
) et s'authentifier à nouveau auprès d'elle (Connect-MgGraph
) pour déclencher l'émission d'un nouveau jeton d'accès qui inclut les nouvelles revendications (Figure 11).
Avec la RoleManagement.ReadWrite.Directory
accordée, le principal de service que nous avons peut maintenant modifier les attributions de rôles de répertoire pour toute identité dans le locataire - y compris en s'ajoutant lui-même au rôle d'administrateur global :
$globalAdminRoleId = "62e90394-69f5-4237-9190-012177145e10" # GA role GUID $globalAdminRole = Get-MgDirectoryRole -Filter "roleTemplateId eq '$globalAdminRoleId'" -ErrorAction SilentlyContinue $roleMemberParams = @{ "@odata.id" = "https://graph.microsoft.com/v1.0/servicePrincipals/$($SP.Id)" } New-MgDirectoryRoleMemberByRef -DirectoryRoleId $globalAdminRole.Id -BodyParameter $roleMemberParams
Maintenant, si nous vérifions les rôles attribués au principal de service compromis, nous pourrons voir le GUID de l'administrateur global(figure 12).
Si vous avez suivi de près, une différence de comportement essentielle peut être mise en évidence :
Pourquoi est-il nécessaire d'obtenir un nouveau JWT pour l'identité après avoir accordé une permission à une application, mais pas après avoir attribué un rôle à un répertoire ?
La réponse se trouve dans la façon dont Entra ID applique ces deux modèles d'autorisation :
- Autorisations de l'application (par exemple
RoleManagement.ReadWrite.Directory
) sont émises en tant que revendications statiques dans le jeton d'accès JWT au moment de l'authentification. Ces permissions sont représentées dans l'élémentroles
(ouscp
dans les flux délégués). Le jeton est en fait une assertion signée cryptographiquement qui reflète les rôles et les champs d'application de l'appelant au moment de l'émission et, en général, toute modification de ces autorisations nécessite la réémission du jeton. - Rôles dans l'annuaire (par exemple
Global Administrator
) sont également émises en tant que revendications statiques dans le jeton d'accès, mais elles suivent un modèle d'application différent. Bien que les jetons puissent inclure des attributions de rôles d'annuaire via l'optionwids
ou legroup
(dans le cas de l'appartenance à un groupe), la plupart des API de Microsoft généralement évaluer ces rôles de manière dynamique au moment de l'exécution. Lorsqu'une demande est faite, le backend interroge les attributions de rôle actuelles dans Entra ID pour connaître l'ID de l'objet de l'appelant. Cette recherche en temps réel permet aux rôles nouvellement attribués de prendre effet immédiatement sans qu'il soit nécessaire de renouveler le jeton.
Ceci étant dit, Microsoft note explicitement dans sa documentation de référence sur les revendications de jetons d'accès que :
Les
roles
,groups
,scp
etwids
ne constituent pas une liste exhaustive de la manière dont une ressource peut autoriser un utilisateur ou une application, ni une liste exhaustive des autorisations accordées à l'appelant. La ressource cible peut utiliser une autre méthode pour autoriser l'accès à ses ressources protégées.
Étape 4 : Passage à une session d'administration
Avec le rôle GA assigné à notre principal de service, nous possédons maintenant des privilèges complets au niveau de l'annuaire. Cela nous permet de réinitialiser le mot de passe de l'utilisateur administrateur cible et d'assumer son identité(Figure 13).
Pour récapituler la dernière étape, nous n'utiliserons pas le TAP ou le portail Azure comme nous l'avons fait dans le scénario 1.
Au lieu de cela, nous nous appuierons sur l'outil BARK1 Get-MSGraphTokenWithUsernamePassword
pour s'authentifier auprès de Microsoft Graph avec les nouvelles informations d'identification de l'administrateur et récupérer le drapeau dans le fichier /me
point final (Figure 14) - en restant fidèle au titre de ce scénario : Graph Me the Crown (and Roles).
Une fois le scénario terminé, nous exécutons le script de nettoyage(figure 15) pour rétablir le locataire dans son état d'origine.
Trouvez vos angles morts
Ce scénario illustre la façon dont les autorisations graphiques pour les applications uniquement, combinées à l'authentification basée sur des certificats, peuvent créer des angles morts dans la gouvernance traditionnelle des identités.
Aucun compromis avec l'utilisateur n'est nécessaire. Aucun flux interactif n'est nécessaire. En enchaînant deux autorisations graphiques -AppRoleAssignment.ReadWrite.All
et RoleManagement.ReadWrite.Directory
-l'attaquant escalade silencieusement un principal de service pour en faire un administrateur global.
Les équipes de sécurité doivent traiter les autorisations d'application et les informations d'identification des principaux services comme des actifs critiques, et non comme des identités secondaires. L'application d'une gouvernance stricte sur l'utilisation des certificats et l'attribution des autorisations graphiques est essentielle pour empêcher l'escalade des privilèges sans tête comme celle modélisée ici dans le scénario 2.
Relevez le prochain défi d'EntraGoat
- GitHub - Semperis/EntraGoat
- Qu'est-ce qu'EntraGoat ? Un environnement de simulation Entra ID délibérément vulnérable
- Démarrer avec EntraGoat : casser l'ID d'Entra de manière intelligente
- Scénario EntraGoat 1 : Abus de propriété d'un directeur de service dans Entra ID
Note de bas de page
- 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.