Aller au contenu principal
Retour au blog
CIEM ResearchIdentity SecurityAWS IAM

CIEM : Anatomie des Menaces Identitaires Cloud

Analyse des risques liés aux identités cloud : permission explosion, privilege escalation paths, et stratégies de least privilege.

C
Cyvex Research Lab
28 Décembre 2024
20 min
Code source

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

+12%

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 :

34%

Over-Privileged Humans

Développeurs avec AdministratorAccess en prod

67%

Over-Privileged Services

Lambda/EC2 avec permissions excessives

89%

Stale Credentials

Access keys > 90 jours sans rotation

45%

Cross-Account Risks

Trust policies trop permissives

23%

Privilege Escalation

Chemins vers AdministratorAccess

18%

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 :

Identity Attack Graph3 Blast Radius Paths
Critical
High
Medium
Developer47 permsDevOps-Role156 permsAdmin-Role312 permsLambda Func89 permsEC2 Instance203 permsS3 BucketRDS DatabaseSecrets Mgr

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
S312 actions → 847 effective (70.6x)
EC28 actions → 312 effective (39x)
IAM4 actions → 156 effective (39x)
Lambda6 actions → 89 effective (14.8x)
RDS3 actions → 67 effective (22.3x)

* 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 :

dangerous-policy.jsonjson
1{
2 "Version": "2012-10-17",
3 "Statement": [
4 {
5 "Sid": "DeveloperAccess",
6 "Effect": "Allow",
7 "Action": [
8 "s3:*", // VULN #1: Full S3 access
9 "ec2:*", // VULN #2: Full EC2 access
10 "iam:PassRole", // VULN #3: Can pass any role
11 "iam:CreateAccessKey", // VULN #4: Can create keys
12 "sts:AssumeRole" // VULN #5: Can assume roles
13 ],
14 "Resource": "*" // VULN #6: No resource constraint
15 },
16 {
17 "Sid": "SecretsAccess",
18 "Effect": "Allow",
19 "Action": [
20 "secretsmanager:GetSecretValue", // VULN #7: Can read all secrets
21 "kms:Decrypt" // VULN #8: Can decrypt anything
22 ],
23 "Resource": "*"
24 },
25 {
26 "Sid": "LambdaAdmin",
27 "Effect": "Allow",
28 "Action": [
29 "lambda:*", // VULN #9: Full Lambda access
30 "logs:*" // VULN #10: Can delete logs
31 ],
32 "Resource": "*"
33 }
34 ]
35}

3.2 Trust Policy Vulnérable

Les trust policies mal configurées permettent le cross-account takeover :

vulnerable-trust-policy.jsonjson
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 ID
13 }
14 }
15 },
16 {
17 "Effect": "Allow",
18 "Principal": {
19 "Service": "ec2.amazonaws.com"
20 },
21 "Action": "sts:AssumeRole" // Any EC2 in account can assume
22 }
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éthodePermissions RequisesImpactPrévalence
CreateAccessKeyiam:CreateAccessKeyPersistence sur user12%
PassRole + Lambdaiam:PassRole, lambda:CreateFunctionExec as role23%
PassRole + EC2iam:PassRole, ec2:RunInstancesExec as role31%
UpdateAssumeRolePolicyiam:UpdateAssumeRolePolicyAssume any role8%
AttachUserPolicyiam:AttachUserPolicySelf-promote admin15%
CreatePolicyVersioniam:CreatePolicyVersionModify policy11%

4Détection CIEM

4.1 CIEM Analyzer Python

Script complet d'analyse des risques identitaires avec calcul de blast radius :

ciem_analyzer.pypython
1#!/usr/bin/env python3
2"""
3CIEM Identity Risk Analyzer
4Detects over-privileged identities and permission explosion
5"""
6
7import boto3
8import json
9from dataclasses import dataclass
10from typing import List, Dict, Set
11from collections import defaultdict
12
13@dataclass
14class IdentityRisk:
15 identity_arn: str
16 identity_type: str
17 risk_level: str
18 findings: List[str]
19 effective_permissions: int
20 blast_radius: List[str]
21
22class CIEMAnalyzer:
23 # High-risk actions that indicate privilege escalation potential
24 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 }
46
47 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 }
60
61 def __init__(self):
62 self.iam = boto3.client('iam')
63 self.sts = boto3.client('sts')
64 self.access_analyzer = boto3.client('accessanalyzer')
65
66 def analyze_identity(self, identity_arn: str) -> IdentityRisk:
67 """Analyze a single identity for CIEM risks"""
68 findings = []
69 blast_radius = []
70
71 # Get effective permissions
72 effective_perms = self._get_effective_permissions(identity_arn)
73
74 # Check for privilege escalation paths
75 priv_esc_actions = effective_perms & self.PRIVILEGE_ESCALATION_ACTIONS
76 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])}...")
79
80 # Check for sensitive data access
81 sensitive_actions = effective_perms & self.SENSITIVE_DATA_ACTIONS
82 if sensitive_actions:
83 findings.append(f"HIGH: {len(sensitive_actions)} sensitive data actions")
84
85 # Check for wildcard permissions
86 wildcard_count = self._count_wildcard_permissions(identity_arn)
87 if wildcard_count > 0:
88 findings.append(f"HIGH: {wildcard_count} wildcard (*) permissions")
89
90 # Calculate blast radius
91 blast_radius = self._calculate_blast_radius(identity_arn, effective_perms)
92
93 # Determine risk level
94 risk_level = self._calculate_risk_level(findings, len(effective_perms))
95
96 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_radius
103 )
104
105 def _get_effective_permissions(self, identity_arn: str) -> Set[str]:
106 """Expand all permissions including wildcards"""
107 effective = set()
108
109 # Get all attached policies
110 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']
124
125 # Expand each policy
126 for policy in policies:
127 policy_doc = self._get_policy_document(policy['PolicyArn'])
128 effective.update(self._expand_policy_actions(policy_doc))
129
130 return effective
131
132 def _expand_policy_actions(self, policy_doc: dict) -> Set[str]:
133 """Expand wildcards to actual actions"""
134 actions = set()
135
136 for statement in policy_doc.get('Statement', []):
137 if statement.get('Effect') != 'Allow':
138 continue
139
140 stmt_actions = statement.get('Action', [])
141 if isinstance(stmt_actions, str):
142 stmt_actions = [stmt_actions]
143
144 for action in stmt_actions:
145 if '*' in action:
146 # Expand wildcard - simplified example
147 expanded = self._expand_wildcard_action(action)
148 actions.update(expanded)
149 else:
150 actions.add(action)
151
152 return actions
153
154 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 = []
161
162 # Check S3 blast radius
163 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 perf
167 if accessible:
168 blast_radius.append(f"S3: {len(accessible)} buckets")
169
170 # Check Secrets Manager blast radius
171 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")
176
177 # Check RDS blast radius
178 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")
183
184 return blast_radius
185
186 def _calculate_risk_level(
187 self,
188 findings: List[str],
189 perm_count: int
190 ) -> 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)
194
195 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'
202
203
204def main():
205 analyzer = CIEMAnalyzer()
206
207 # Analyze all IAM roles
208 iam = boto3.client('iam')
209 roles = iam.list_roles()['Roles']
210
211 print("=" * 60)
212 print("CIEM Identity Risk Analysis Report")
213 print("=" * 60)
214
215 critical_identities = []
216
217 for role in roles:
218 if role['RoleName'].startswith('AWS'): # Skip AWS service roles
219 continue
220
221 risk = analyzer.analyze_identity(role['Arn'])
222
223 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}")
230
231 print(f"\n{'=' * 60}")
232 print(f"Summary: {len(critical_identities)} high-risk identities found")
233 print(f"{'=' * 60}")
234
235
236if __name__ == '__main__':
237 main()

4.2 Détection CloudTrail avec Athena

Requêtes pour détecter les comportements suspects sur les identités :

ciem-detection.sqlsql
1-- CIEM Detection Queries for AWS CloudTrail
2-- Detect identity-based threats and privilege abuse
3
4-- Query 1: Detect AssumeRole to sensitive roles
5SELECT
6 eventTime,
7 userIdentity.arn AS source_identity,
8 requestParameters.roleArn AS assumed_role,
9 sourceIPAddress,
10 userAgent,
11 CASE
12 WHEN requestParameters.roleArn LIKE '%Admin%' THEN 'CRITICAL'
13 WHEN requestParameters.roleArn LIKE '%prod%' THEN 'HIGH'
14 ELSE 'MEDIUM'
15 END AS risk_level
16FROM cloudtrail_logs
17WHERE eventName = 'AssumeRole'
18 AND errorCode IS NULL
19 AND userIdentity.arn NOT LIKE '%:assumed-role/AWS%'
20 AND eventTime > date_add('day', -7, current_date)
21ORDER BY eventTime DESC
22LIMIT 1000;
23
24-- Query 2: Detect privilege escalation attempts
25SELECT
26 eventTime,
27 userIdentity.arn AS actor,
28 eventName,
29 requestParameters,
30 sourceIPAddress,
31 errorCode
32FROM cloudtrail_logs
33WHERE 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;
46
47-- Query 3: Detect cross-account access patterns
48SELECT
49 eventTime,
50 userIdentity.accountId AS source_account,
51 recipientAccountId AS target_account,
52 userIdentity.arn AS actor,
53 eventName,
54 eventSource,
55 awsRegion
56FROM cloudtrail_logs
57WHERE userIdentity.accountId != recipientAccountId
58 AND userIdentity.type = 'AssumedRole'
59 AND eventTime > date_add('day', -7, current_date)
60GROUP BY 1,2,3,4,5,6,7
61ORDER BY eventTime DESC;
62
63-- Query 4: Detect unusual secret access patterns
64SELECT
65 date_trunc('hour', eventTime) AS hour,
66 userIdentity.arn AS actor,
67 COUNT(*) AS secret_access_count,
68 COUNT(DISTINCT requestParameters.secretId) AS unique_secrets
69FROM cloudtrail_logs
70WHERE eventSource = 'secretsmanager.amazonaws.com'
71 AND eventName = 'GetSecretValue'
72 AND eventTime > date_add('day', -7, current_date)
73GROUP BY 1, 2
74HAVING COUNT(*) > 10 -- Threshold for anomaly
75ORDER BY secret_access_count DESC;
76
77-- Query 5: Detect service account key creation (persistence)
78SELECT
79 eventTime,
80 userIdentity.arn AS actor,
81 requestParameters.userName AS target_user,
82 responseElements.accessKey.accessKeyId AS new_key_id,
83 sourceIPAddress,
84 userAgent
85FROM cloudtrail_logs
86WHERE eventName = 'CreateAccessKey'
87 AND errorCode IS NULL
88 AND eventTime > date_add('day', -30, current_date)
89ORDER BY eventTime DESC;

5Remédiation : Least Privilege

Stratégie CIEM Defense-in-Depth

Implémenter IAM Access Analyzer pour identifier les permissions inutilisées
Utiliser des Permission Boundaries pour limiter les escalades
Déployer des SCPs au niveau Organization
Activer la rotation automatique des credentials
Implémenter le JIT (Just-In-Time) access
Auditer les trust policies cross-account
Monitorer les AssumeRole via CloudTrail
Utiliser des session policies pour les accès temporaires

5.1 Policy Least Privilege

Exemple de policy IAM sécurisée avec deny explicite sur les actions dangereuses :

least-privilege-policy.jsonjson
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é :

security-scp.jsonjson
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

[1]
Gartner. "Is the Cloud Secure?". Gartner Research, 2023. Link ↗
[2]
Rhino Security Labs. "AWS IAM Privilege Escalation Methods". Rhino Security Research, 2024. Link ↗
[3]
MITRE Corporation. "ATT&CK for Cloud". MITRE ATT&CK, 2024. Link ↗
[4]
AWS. "IAM Access Analyzer". AWS Documentation, 2024. Link ↗
[5]
Wiz Research. "The Cloud Permissions Gap". Wiz Blog, 2023. Link ↗
[6]
Ermetic (Tenable). "State of Cloud Identity Security". Ermetic Research, 2023. Link ↗

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%.

CRL

Cyvex Research Lab

Security Research Team @ Cyvex

Équipe dédiée à la recherche en sécurité cloud et identités. Contributeurs aux frameworks CIEM, découverte de vulnérabilités IAM, et développement d'outils open source pour la sécurité des environnements cloud-native.

Partager cet article