Emprunt d'identité de compte de service GCP : la chaîne
Retracer l'emprunt d'identité de comptes de service dans l'audit Google Cloud : GenerateAccessToken, SignBlob, délégation, actAs et Token Creator.
En bref. L'emprunt d'identité d'un compte de service laisse deux traces. L'appel d'émission (GenerateAccessToken, GenerateIdToken, SignBlob, SignJwt sur iamcredentials.googleapis.com) est un journal Data Access : il n'existe que si la journalisation Data Access d'IAM était active. L'utilisation du jeton apparaît dans les journaux du service ciblé, avec principalEmail égal au compte de service et l'appelant réel dans authenticationInfo.serviceAccountDelegationInfo. Lisez les deux, et cherchez qui a reçu Token Creator ou Service Account User au départ.
L'emprunt d'identité est légitime et même recommandé : il remplace les clés téléchargeables par des jetons de courte durée. C'est aussi l'un des chemins d'élévation de privilèges les plus utilisés dans Google Cloud, car la permission qui le permet, iam.serviceAccounts.getAccessToken, est souvent accordée largement. Les travaux de Rhino Security Labs sur l'élévation de privilèges GCP listent getAccessToken, signBlob, signJwt et implicitDelegation comme autant de méthodes d'élévation distinctes.
Emprunt d'identité ou utilisation d'une clé
Google trace clairement la frontière dans sa documentation sur l'emprunt d'identité : l'emprunt implique deux identités (le principal authentifié et le compte de service), alors que l'authentification par clé n'en implique qu'une. Pour l'enquêteur :
| Mécanisme | Ce qui l'identifie dans les journaux |
|---|---|
| Clé gérée par l'utilisateur | authenticationInfo.serviceAccountKeyName (voir clé divulguée) |
| Emprunt d'identité (jeton émis via l'API IAM Credentials) | serviceAccountDelegationInfo sur les appels, GenerateAccessToken dans les journaux Data Access |
| Compte de service associé (VM, Cloud Run, Cloud Functions) | Appels en tant que compte de service, en général depuis l'IP de la charge de travail, sans nom de clé ni délégation |
| Fédération d'identité de charge de travail | principalSubject du type principal://… ou informations de délégation avec un principal tiers |
La troisième ligne compte : un jeton volé sur le serveur de métadonnées d'une VM (T1552.005) n'apparaît pas comme un emprunt d'identité ; il ressemble au compte de service de la VM, mais appelé depuis ailleurs.
Trace 1 : l'appel d'émission
D'après la page journaux d'audit de Service Account Credentials de Google, ces méthodes sont enregistrées sous cloudaudit.googleapis.com/data_access avec le service iamcredentials.googleapis.com. Activer les journaux d'audit Data Access pour l'API IAM les active aussi pour cette API.
protoPayload.serviceName="iamcredentials.googleapis.com"
protoPayload.methodName=("GenerateAccessToken" OR "GenerateIdToken" OR "SignBlob" OR "SignJwt")
Dans ces entrées, principalEmail est l'appelant (l'acteur réel, ou le maillon précédent de la chaîne) et le compte de service ciblé apparaît dans resource.labels.email_id et resourceName, comme le montrent les exemples de journaux de Google. SignBlob et SignJwt méritent une attention particulière : un JWT signé au nom d'un compte de service peut être échangé contre un jeton, et la signature de blobs permet de forger des URL signées.
Trace 2 : les appels faits avec le jeton
Tout appel d'API effectué avec un jeton obtenu par emprunt d'identité porte la chaîne :
"authenticationInfo": {
"principalEmail": "deployer@PROJECT.iam.gserviceaccount.com",
"serviceAccountDelegationInfo": [
{ "firstPartyPrincipal": { "principalEmail": "alice@example.com" } }
]
}
Cette information figure dans les journaux Admin Activity, toujours actifs. Même quand l'appel d'émission est invisible, les écritures faites avec le jeton nomment donc l'appelant réel. C'est souvent la seule trace dont on dispose.
Les chaînes
L'emprunt d'identité peut être chaîné : A emprunte l'identité de B, qui emprunte celle de C (le paramètre delegates de GenerateAccessToken ; chaque maillon a besoin d'une permission sur le compte de service suivant, que fournit le rôle Token Creator). La chaîne est enregistrée dans serviceAccountDelegationInfo sous forme de tableau. Pour la reconstituer :
- Partez de l'appel final et lisez la liste de délégation.
- Pour chaque maillon, cherchez la liaison IAM qui l'a permis : qui détient
roles/iam.serviceAccountTokenCreatorsur quel compte de service, et depuis quand. - Vérifiez si l'une de ces liaisons a été ajoutée pendant la fenêtre d'incident (
bindingDeltasADD, voir abus de SetIamPolicy).
C'est à la troisième étape que les attaques se révèlent : une identité compromise peu privilégiée s'accorde Token Creator sur un compte de service puissant, puis émet des jetons pour lui (T1098.003, puis T1550.001).
actAs : l'emprunt d'identité par une ressource
Le rôle Service Account User (roles/iam.serviceAccountUser) accorde iam.serviceAccounts.actAs : le droit d'associer un compte de service à une ressource. Quelqu'un qui dispose d'actAs sur un compte de service puissant et de compute.instances.create peut démarrer une VM qui s'exécute sous ce compte et lire son jeton sur le serveur de métadonnées. Les exemples de journaux de Google montrent le contrôle sous la forme d'une entrée authorizationInfo avec "permission": "iam.serviceAccounts.actAs" et "granted": true dans l'appel de création de la ressource.
Quand vous passez en revue des créations de VM, de services Cloud Run ou de Cloud Functions pendant un incident, lisez donc authorizationInfo et le compte de service indiqué dans la requête, pas seulement l'auteur de la création.
Signaux d'alerte
- Un utilisateur humain ou une IP externe qui émet des jetons pour un compte de service normalement réservé à une charge de travail.
- Des appels
GenerateAccessTokenqui commencent juste après un octroi de Token Creator. - De longues chaînes avec, au milieu, un compte de service dont personne n'est responsable.
- Des
SignJwtouSignBlobvenant de principaux qui n'ont aucune raison de signer quoi que ce soit. - Des appels avec informations de délégation provenant d'un principal extérieur à vos domaines.
Ce que montre l'analyseur
Dans l'analyseur :
sa_impersonation(moyenne, élévation de privilèges, T1550.001) se déclenche sur les appels réussisGenerateAccessToken,GenerateIdToken,SignBlobetSignJwt, hors agents de service gérés par Google.token_creator_granted(haute) se déclenche sur unADDde Token Creator, Service Account User, Service Account Key Admin ou Workload Identity User.- L'onglet Entités indique, pour chaque principal, les identités qui ont emprunté la sienne (d'après
serviceAccountDelegationInfo), et le détail d'une entrée de journal affiche le champ correspondant.
La limite est celle décrite plus haut : sans journaux Data Access d'IAM, seule la seconde trace existe, et l'outil avertit quand un export ne contient aucun journal Data Access. Voir les limites des journaux Data Access.
Remédiation
Retirez les liaisons Token Creator / Service Account User dont personne n'a besoin, préférez les accorder sur des comptes de service précis plutôt que sur le projet, et passez en revue les principaux capables d'emprunter l'identité de vos comptes de service les plus privilégiés. La page permissions des comptes de service de Google détaille ce que chaque rôle permet.
Questions fréquentes
Comment voir qui a emprunté l'identité d'un compte de service dans Google Cloud ?
À deux endroits. L'appel d'émission du jeton (GenerateAccessToken, GenerateIdToken, SignBlob, SignJwt sur iamcredentials.googleapis.com) est un journal Data Access dont le principalEmail est l'appelant réel. Les appels effectués avec le jeton obtenu indiquent l'appelant réel dans authenticationInfo.serviceAccountDelegationInfo.
Pourquoi GenerateAccessToken n'apparaît-il pas dans mes journaux ?
Parce qu'il est enregistré comme journal d'audit Data Access (ADMIN_READ), désactivé par défaut. Activer les journaux Data Access pour l'API IAM les active aussi pour l'API Service Account Credentials.
Pour aller plus loin
- Emprunt d'identité de compte de service — documentation Google Cloud.
- Exemples de journaux pour les comptes de service — documentation Google Cloud.
- MITRE ATT&CK T1550.001, Application Access Token.