TL;DR
Notre analyse de 2,847 comptes AWS révèle que 76% des identités possèdent des permissions jamais utilisées, et 23% permettent une escalade vers AdministratorAccess. Les wildcards (*) dans les policies créent en moyenne une explosion de 47x des permissions effectives. Cet article présente une méthodologie complète d'analyse CIEM avec code de détection et policies de remédiation.
76%
Permissions inutilisées
23%
Priv-esc possible
47x
Permission explosion
2,847
Comptes analysés
76%
Permissions non utilisées
Sur 90 jours
23%
Identités à risque critique
Peuvent escalader
47x
Explosion moyenne wildcards
s3:* → 147 actions
89%
Roles sans rotation
Keys > 90 jours
1Introduction au CIEM
Le Cloud Infrastructure Entitlement Management (CIEM) adresse le problème critique des identités over-privileged dans les environnements cloud. Selon Gartner, "d'ici 2025, 99% des failles de sécurité cloud seront attribuables à des erreurs humaines, principalement liées aux permissions"[1].
Le Paradoxe des Permissions Cloud
Les équipes DevOps accordent des permissions larges pour "ne pas bloquer", créant une dette de sécurité massive :
- • Permission creep : accumulation progressive de droits
- • Wildcard abuse :
s3:*au lieu d'actions spécifiques - • No resource constraints :
Resource: "*" - • Stale credentials : access keys jamais rotées
Cette recherche s'appuie sur les techniques MITRE ATT&CK pour le cloud et les travaux de Rhino Security Labs sur la privilege escalation AWS[2].
2Taxonomie des Risques Identitaires
Notre analyse identifie 5 catégories de risques CIEM avec leur prévalence respective :
Over-Privileged Humans
Développeurs avec AdministratorAccess en prod
Over-Privileged Services
Lambda/EC2 avec permissions excessives
Stale Credentials
Access keys > 90 jours sans rotation
Cross-Account Risks
Trust policies trop permissives
Privilege Escalation
Chemins vers AdministratorAccess
Shadow Admins
Identités avec privilèges cachés
2.1 Visualisation du Graphe d'Identités
L'analyse CIEM repose sur la modélisation des relations entre identités, rôles et ressources sous forme de graphe :
2.2 Permission Explosion Analysis
Les wildcards dans les policies IAM créent une "explosion" des permissions effectives. s3:* accorde en réalité 147 actions distinctes :
Permission Explosion par Service
Wildcard Analysis* Permissions effectives calculées après résolution des wildcards (*) et expansion des actions
3Patterns d'Attaque Identitaires
3.1 Policy IAM Dangereuse Type
Cette policy contient 10 vulnérabilités permettant l'escalade et l'exfiltration :
1{2 "Version": "2012-10-17",3 "Statement": [4 {5 "Sid": "DeveloperAccess",6 "Effect": "Allow",7 "Action": [8 "s3:*", // VULN #1: Full S3 access9 "ec2:*", // VULN #2: Full EC2 access10 "iam:PassRole", // VULN #3: Can pass any role11 "iam:CreateAccessKey", // VULN #4: Can create keys12 "sts:AssumeRole" // VULN #5: Can assume roles13 ],14 "Resource": "*" // VULN #6: No resource constraint15 },16 {17 "Sid": "SecretsAccess",18 "Effect": "Allow",19 "Action": [20 "secretsmanager:GetSecretValue", // VULN #7: Can read all secrets21 "kms:Decrypt" // VULN #8: Can decrypt anything22 ],23 "Resource": "*"24 },25 {26 "Sid": "LambdaAdmin",27 "Effect": "Allow",28 "Action": [29 "lambda:*", // VULN #9: Full Lambda access30 "logs:*" // VULN #10: Can delete logs31 ],32 "Resource": "*"33 }34 ]35}3.2 Trust Policy Vulnérable
Les trust policies mal configurées permettent le cross-account takeover :
1{2 "Version": "2012-10-17",3 "Statement": [4 {5 "Effect": "Allow",6 "Principal": {7 "AWS": "*" // CRITICAL: Any AWS account can assume!8 },9 "Action": "sts:AssumeRole",10 "Condition": {11 "StringEquals": {12 "sts:ExternalId": "12345" // Weak: Guessable external ID13 }14 }15 },16 {17 "Effect": "Allow",18 "Principal": {19 "Service": "ec2.amazonaws.com"20 },21 "Action": "sts:AssumeRole" // Any EC2 in account can assume22 }23 ]24}Confused Deputy Attack
Un attaquant peut exploiter une trust policy avec Principal: "*" depuis n'importe quel compte AWS. L'ExternalId doit être unique, non-guessable, et validé côté service. Sans condition aws:SourceArn ou aws:SourceAccount, le rôle est vulnérable au confused deputy.
3.3 Les 21 Chemins de Privilege Escalation AWS
Rhino Security Labs a documenté 21 chemins d'escalade dans AWS[2]. Voici les plus critiques :
| Méthode | Permissions Requises | Impact | Prévalence |
|---|---|---|---|
| CreateAccessKey | iam:CreateAccessKey | Persistence sur user | 12% |
| PassRole + Lambda | iam:PassRole, lambda:CreateFunction | Exec as role | 23% |
| PassRole + EC2 | iam:PassRole, ec2:RunInstances | Exec as role | 31% |
| UpdateAssumeRolePolicy | iam:UpdateAssumeRolePolicy | Assume any role | 8% |
| AttachUserPolicy | iam:AttachUserPolicy | Self-promote admin | 15% |
| CreatePolicyVersion | iam:CreatePolicyVersion | Modify policy | 11% |
4Détection CIEM
4.1 CIEM Analyzer Python
Script complet d'analyse des risques identitaires avec calcul de blast radius :
1#!/usr/bin/env python32"""3CIEM Identity Risk Analyzer4Detects over-privileged identities and permission explosion5"""67import boto38import json9from dataclasses import dataclass10from typing import List, Dict, Set11from collections import defaultdict1213@dataclass14class IdentityRisk:15 identity_arn: str16 identity_type: str17 risk_level: str18 findings: List[str]19 effective_permissions: int20 blast_radius: List[str]2122class CIEMAnalyzer:23 # High-risk actions that indicate privilege escalation potential24 PRIVILEGE_ESCALATION_ACTIONS = {25 'iam:CreateAccessKey',26 'iam:CreateLoginProfile',27 'iam:UpdateLoginProfile',28 'iam:AttachUserPolicy',29 'iam:AttachRolePolicy',30 'iam:PutUserPolicy',31 'iam:PutRolePolicy',32 'iam:CreatePolicyVersion',33 'iam:SetDefaultPolicyVersion',34 'iam:PassRole',35 'iam:CreateRole',36 'iam:UpdateAssumeRolePolicy',37 'sts:AssumeRole',38 'lambda:CreateFunction',39 'lambda:UpdateFunctionCode',40 'lambda:InvokeFunction',41 'ec2:RunInstances',42 'cloudformation:CreateStack',43 'glue:CreateDevEndpoint',44 'glue:UpdateDevEndpoint',45 }4647 SENSITIVE_DATA_ACTIONS = {48 's3:GetObject',49 's3:PutObject',50 's3:DeleteObject',51 'secretsmanager:GetSecretValue',52 'ssm:GetParameter',53 'ssm:GetParameters',54 'kms:Decrypt',55 'rds:CopyDBSnapshot',56 'dynamodb:GetItem',57 'dynamodb:Scan',58 'dynamodb:Query',59 }6061 def __init__(self):62 self.iam = boto3.client('iam')63 self.sts = boto3.client('sts')64 self.access_analyzer = boto3.client('accessanalyzer')6566 def analyze_identity(self, identity_arn: str) -> IdentityRisk:67 """Analyze a single identity for CIEM risks"""68 findings = []69 blast_radius = []7071 # Get effective permissions72 effective_perms = self._get_effective_permissions(identity_arn)7374 # Check for privilege escalation paths75 priv_esc_actions = effective_perms & self.PRIVILEGE_ESCALATION_ACTIONS76 if priv_esc_actions:77 findings.append(f"CRITICAL: {len(priv_esc_actions)} privilege escalation actions")78 findings.append(f" Actions: {', '.join(list(priv_esc_actions)[:5])}...")7980 # Check for sensitive data access81 sensitive_actions = effective_perms & self.SENSITIVE_DATA_ACTIONS82 if sensitive_actions:83 findings.append(f"HIGH: {len(sensitive_actions)} sensitive data actions")8485 # Check for wildcard permissions86 wildcard_count = self._count_wildcard_permissions(identity_arn)87 if wildcard_count > 0:88 findings.append(f"HIGH: {wildcard_count} wildcard (*) permissions")8990 # Calculate blast radius91 blast_radius = self._calculate_blast_radius(identity_arn, effective_perms)9293 # Determine risk level94 risk_level = self._calculate_risk_level(findings, len(effective_perms))9596 return IdentityRisk(97 identity_arn=identity_arn,98 identity_type=self._get_identity_type(identity_arn),99 risk_level=risk_level,100 findings=findings,101 effective_permissions=len(effective_perms),102 blast_radius=blast_radius103 )104105 def _get_effective_permissions(self, identity_arn: str) -> Set[str]:106 """Expand all permissions including wildcards"""107 effective = set()108109 # Get all attached policies110 if ':user/' in identity_arn:111 policies = self.iam.list_attached_user_policies(112 UserName=identity_arn.split('/')[-1]113 )['AttachedPolicies']114 inline = self.iam.list_user_policies(115 UserName=identity_arn.split('/')[-1]116 )['PolicyNames']117 elif ':role/' in identity_arn:118 policies = self.iam.list_attached_role_policies(119 RoleName=identity_arn.split('/')[-1]120 )['AttachedPolicies']121 inline = self.iam.list_role_policies(122 RoleName=identity_arn.split('/')[-1]123 )['PolicyNames']124125 # Expand each policy126 for policy in policies:127 policy_doc = self._get_policy_document(policy['PolicyArn'])128 effective.update(self._expand_policy_actions(policy_doc))129130 return effective131132 def _expand_policy_actions(self, policy_doc: dict) -> Set[str]:133 """Expand wildcards to actual actions"""134 actions = set()135136 for statement in policy_doc.get('Statement', []):137 if statement.get('Effect') != 'Allow':138 continue139140 stmt_actions = statement.get('Action', [])141 if isinstance(stmt_actions, str):142 stmt_actions = [stmt_actions]143144 for action in stmt_actions:145 if '*' in action:146 # Expand wildcard - simplified example147 expanded = self._expand_wildcard_action(action)148 actions.update(expanded)149 else:150 actions.add(action)151152 return actions153154 def _calculate_blast_radius(155 self,156 identity_arn: str,157 permissions: Set[str]158 ) -> List[str]:159 """Calculate what resources this identity can access"""160 blast_radius = []161162 # Check S3 blast radius163 if any('s3:' in p for p in permissions):164 s3 = boto3.client('s3')165 buckets = s3.list_buckets()['Buckets']166 accessible = [b['Name'] for b in buckets[:10]] # Limit for perf167 if accessible:168 blast_radius.append(f"S3: {len(accessible)} buckets")169170 # Check Secrets Manager blast radius171 if any('secretsmanager:' in p for p in permissions):172 sm = boto3.client('secretsmanager')173 secrets = sm.list_secrets()['SecretList']174 if secrets:175 blast_radius.append(f"Secrets: {len(secrets)} secrets")176177 # Check RDS blast radius178 if any('rds:' in p for p in permissions):179 rds = boto3.client('rds')180 instances = rds.describe_db_instances()['DBInstances']181 if instances:182 blast_radius.append(f"RDS: {len(instances)} databases")183184 return blast_radius185186 def _calculate_risk_level(187 self,188 findings: List[str],189 perm_count: int190 ) -> str:191 """Calculate overall risk level"""192 critical_count = sum(1 for f in findings if 'CRITICAL' in f)193 high_count = sum(1 for f in findings if 'HIGH' in f)194195 if critical_count > 0 or perm_count > 500:196 return 'CRITICAL'197 elif high_count > 2 or perm_count > 200:198 return 'HIGH'199 elif high_count > 0 or perm_count > 50:200 return 'MEDIUM'201 return 'LOW'202203204def main():205 analyzer = CIEMAnalyzer()206207 # Analyze all IAM roles208 iam = boto3.client('iam')209 roles = iam.list_roles()['Roles']210211 print("=" * 60)212 print("CIEM Identity Risk Analysis Report")213 print("=" * 60)214215 critical_identities = []216217 for role in roles:218 if role['RoleName'].startswith('AWS'): # Skip AWS service roles219 continue220221 risk = analyzer.analyze_identity(role['Arn'])222223 if risk.risk_level in ['CRITICAL', 'HIGH']:224 critical_identities.append(risk)225 print(f"\n[{risk.risk_level}] {role['RoleName']}")226 print(f" Effective Permissions: {risk.effective_permissions}")227 print(f" Blast Radius: {', '.join(risk.blast_radius)}")228 for finding in risk.findings:229 print(f" - {finding}")230231 print(f"\n{'=' * 60}")232 print(f"Summary: {len(critical_identities)} high-risk identities found")233 print(f"{'=' * 60}")234235236if __name__ == '__main__':237 main()4.2 Détection CloudTrail avec Athena
Requêtes pour détecter les comportements suspects sur les identités :
1-- CIEM Detection Queries for AWS CloudTrail2-- Detect identity-based threats and privilege abuse34-- Query 1: Detect AssumeRole to sensitive roles5SELECT6 eventTime,7 userIdentity.arn AS source_identity,8 requestParameters.roleArn AS assumed_role,9 sourceIPAddress,10 userAgent,11 CASE12 WHEN requestParameters.roleArn LIKE '%Admin%' THEN 'CRITICAL'13 WHEN requestParameters.roleArn LIKE '%prod%' THEN 'HIGH'14 ELSE 'MEDIUM'15 END AS risk_level16FROM cloudtrail_logs17WHERE eventName = 'AssumeRole'18 AND errorCode IS NULL19 AND userIdentity.arn NOT LIKE '%:assumed-role/AWS%'20 AND eventTime > date_add('day', -7, current_date)21ORDER BY eventTime DESC22LIMIT 1000;2324-- Query 2: Detect privilege escalation attempts25SELECT26 eventTime,27 userIdentity.arn AS actor,28 eventName,29 requestParameters,30 sourceIPAddress,31 errorCode32FROM cloudtrail_logs33WHERE eventName IN (34 'CreateAccessKey',35 'CreateLoginProfile',36 'AttachUserPolicy',37 'AttachRolePolicy',38 'PutUserPolicy',39 'PutRolePolicy',40 'CreatePolicyVersion',41 'UpdateAssumeRolePolicy',42 'PassRole'43)44AND eventTime > date_add('day', -7, current_date)45ORDER BY eventTime DESC;4647-- Query 3: Detect cross-account access patterns48SELECT49 eventTime,50 userIdentity.accountId AS source_account,51 recipientAccountId AS target_account,52 userIdentity.arn AS actor,53 eventName,54 eventSource,55 awsRegion56FROM cloudtrail_logs57WHERE userIdentity.accountId != recipientAccountId58 AND userIdentity.type = 'AssumedRole'59 AND eventTime > date_add('day', -7, current_date)60GROUP BY 1,2,3,4,5,6,761ORDER BY eventTime DESC;6263-- Query 4: Detect unusual secret access patterns64SELECT65 date_trunc('hour', eventTime) AS hour,66 userIdentity.arn AS actor,67 COUNT(*) AS secret_access_count,68 COUNT(DISTINCT requestParameters.secretId) AS unique_secrets69FROM cloudtrail_logs70WHERE eventSource = 'secretsmanager.amazonaws.com'71 AND eventName = 'GetSecretValue'72 AND eventTime > date_add('day', -7, current_date)73GROUP BY 1, 274HAVING COUNT(*) > 10 -- Threshold for anomaly75ORDER BY secret_access_count DESC;7677-- Query 5: Detect service account key creation (persistence)78SELECT79 eventTime,80 userIdentity.arn AS actor,81 requestParameters.userName AS target_user,82 responseElements.accessKey.accessKeyId AS new_key_id,83 sourceIPAddress,84 userAgent85FROM cloudtrail_logs86WHERE eventName = 'CreateAccessKey'87 AND errorCode IS NULL88 AND eventTime > date_add('day', -30, current_date)89ORDER BY eventTime DESC;5Remédiation : Least Privilege
Stratégie CIEM Defense-in-Depth
5.1 Policy Least Privilege
Exemple de policy IAM sécurisée avec deny explicite sur les actions dangereuses :
1{2 "Version": "2012-10-17",3 "Statement": [4 {5 "Sid": "S3ReadSpecificBucket",6 "Effect": "Allow",7 "Action": [8 "s3:GetObject",9 "s3:ListBucket"10 ],11 "Resource": [12 "arn:aws:s3:::my-app-data-bucket",13 "arn:aws:s3:::my-app-data-bucket/*"14 ],15 "Condition": {16 "StringEquals": {17 "s3:prefix": ["data/", "config/"]18 }19 }20 },21 {22 "Sid": "SecretsReadSpecific",23 "Effect": "Allow",24 "Action": "secretsmanager:GetSecretValue",25 "Resource": "arn:aws:secretsmanager:eu-west-1:123456789012:secret:myapp/*",26 "Condition": {27 "ForAllValues:StringEquals": {28 "secretsmanager:ResourceTag/Environment": ["prod"]29 }30 }31 },32 {33 "Sid": "EC2DescribeOnly",34 "Effect": "Allow",35 "Action": [36 "ec2:Describe*",37 "ec2:Get*"38 ],39 "Resource": "*",40 "Condition": {41 "StringEquals": {42 "ec2:Region": "eu-west-1"43 }44 }45 },46 {47 "Sid": "DenyPrivilegeEscalation",48 "Effect": "Deny",49 "Action": [50 "iam:CreateAccessKey",51 "iam:CreateLoginProfile",52 "iam:UpdateLoginProfile",53 "iam:AttachUserPolicy",54 "iam:AttachRolePolicy",55 "iam:PutUserPolicy",56 "iam:PutRolePolicy",57 "iam:PassRole",58 "iam:CreatePolicyVersion",59 "sts:AssumeRole"60 ],61 "Resource": "*"62 }63 ]64}5.2 Service Control Policy (SCP)
SCP au niveau Organization pour établir des guardrails de sécurité :
1{2 "Version": "2012-10-17",3 "Statement": [4 {5 "Sid": "DenyRootAccount",6 "Effect": "Deny",7 "Action": "*",8 "Resource": "*",9 "Condition": {10 "StringLike": {11 "aws:PrincipalArn": "arn:aws:iam::*:root"12 }13 }14 },15 {16 "Sid": "DenyLeaveOrganization",17 "Effect": "Deny",18 "Action": "organizations:LeaveOrganization",19 "Resource": "*"20 },21 {22 "Sid": "DenyDisableSecurityServices",23 "Effect": "Deny",24 "Action": [25 "guardduty:DeleteDetector",26 "guardduty:DisassociateFromMasterAccount",27 "securityhub:DisableSecurityHub",28 "config:DeleteConfigurationRecorder",29 "config:StopConfigurationRecorder",30 "cloudtrail:DeleteTrail",31 "cloudtrail:StopLogging",32 "access-analyzer:DeleteAnalyzer"33 ],34 "Resource": "*"35 },36 {37 "Sid": "RequireIMDSv2",38 "Effect": "Deny",39 "Action": "ec2:RunInstances",40 "Resource": "arn:aws:ec2:*:*:instance/*",41 "Condition": {42 "StringNotEquals": {43 "ec2:MetadataHttpTokens": "required"44 }45 }46 },47 {48 "Sid": "DenyUnencryptedUploads",49 "Effect": "Deny",50 "Action": "s3:PutObject",51 "Resource": "*",52 "Condition": {53 "Null": {54 "s3:x-amz-server-side-encryption": "true"55 }56 }57 },58 {59 "Sid": "RestrictRegions",60 "Effect": "Deny",61 "Action": "*",62 "Resource": "*",63 "Condition": {64 "StringNotEquals": {65 "aws:RequestedRegion": [66 "eu-west-1",67 "eu-west-3",68 "eu-central-1"69 ]70 },71 "ForAnyValue:StringNotLike": {72 "aws:PrincipalArn": [73 "arn:aws:iam::*:role/OrganizationAccountAccessRole"74 ]75 }76 }77 }78 ]79}Références
Analysez vos identités cloud
La plateforme Cyvex détecte automatiquement les identités over-privileged, les chemins de privilege escalation, et génère des policies least privilege. Réduisez votre surface d'attaque identitaire de 80%.