System Design: Cloud IAM Privilege Escalation Audit System
1. Problem Statement
Design a system that audits cloud IAM configurations across AWS, Azure, and GCP, identifies privilege escalation paths, and generates a prioritized remediation report.
Functional requirements:
- Ingest IAM configuration exports from AWS (IAM Policy documents + role assignments), Azure (RBAC assignments + management group hierarchy), GCP (IAM bindings + conditions)
- Model principals, permissions, and resources as a directed graph
- Find paths from a low-privileged principal to a sensitive action or resource
(e.g., from
arn:aws:iam::123456789:user/appusertoiam:CreatePolicyVersionors3:GetObjecton a protected bucket) - Flag known privilege-escalation permission combinations:
- AWS:
iam:PassRole+lambda:CreateFunction+lambda:InvokeFunction - AWS:
iam:CreatePolicyVersionalone - Azure: Owner assignment on a subscription
- GCP:
resourcemanager.projects.setIamPolicy
- AWS:
- Show detection: for each escalation path, the CloudTrail/Activity Log event that would fire
- Output: a prioritized finding list (highest-privilege gain first) with remediation
Non-functional requirements:
- Offline: operates on policy export files; no live cloud connectivity required
- Multi-cloud: a single model supports AWS, Azure, and GCP simultaneously
- Scale: up to 10,000 principals and 100,000 permission assignments per cloud account
2. Constraints
In scope:
- IAM/RBAC policy evaluation model for each cloud
- Privilege escalation edge taxonomy
- Graph-based path finding
- Detection mapping per edge
Out of scope:
- Live API calls to cloud provider control planes
- Automated remediation (applying changes)
- Data plane access (actual S3 object reads, etc.)
3. High-Level Architecture
┌──────────────────────────────────────────────────────────────────────────┐
│ IAM Export Files │
│ AWS: iam-policies.json + role-assignments.json │
│ Azure: rbac-assignments.json + management-group-hierarchy.json │
│ GCP: iam-bindings.json + org-policy.json │
└───────────────────────────────┬──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Cloud-Specific Parsers (per-cloud policy evaluation) │
│ AWS Parser: deny > SCP > resource policy > identity policy │
│ Azure Parser: RBAC assignment scope + inherited permissions │
│ GCP Parser: binding + condition evaluation │
└───────────────────────────────┬──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Unified Graph Model │
│ Nodes: principals (users, roles, service accounts, groups) │
│ Edges: effective permissions (action → resource) │
│ Edge metadata: cloud provider, escalation risk, detection event │
└───────────────────────────────┬──────────────────────────────────────────┘
│
┌──────────┴───────────┐
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────────┐
│ Escalation Path │ │ Known-Pattern Matcher │
│ Finder │ │ (flag known combos │
│ (graph walk) │ │ without graph search) │
└────────┬────────┘ └───────────┬──────────┘
│ │
└───────────┬────────────┘
▼
┌────────────────────────────┐
│ Finding Prioritizer │
│ + Detection Mapper │
│ + Remediation Generator │
└────────────────────────────┘
4. Component Deep-Dives
4.1 Policy Evaluation Models
AWS (most complex): the evaluation algorithm in order:
- Explicit
Denyin any policy → denied - If inside an AWS Organization: SCP must allow the action → if SCP does not explicitly allow, denied
- Resource-based policy: if it has an explicit
Allowfor the principal → allowed (even without identity policy, for same-account; for cross-account, needs both) - Identity-based policy (attached to the principal): if
Allow→ allowed - Default: implicit deny
The parser must implement this evaluation order to determine effective permissions.
Azure (simpler): role assignments are additive; most permissive role wins. Deny assignments (available but rarely used) override allows. The inheritance chain: management group → subscription → resource group → resource. The parser walks up the scope chain to collect all inherited assignments.
GCP (condition-aware): IAM bindings grant a role to a member at a resource scope. Conditions (e.g., "only during business hours") restrict when the binding applies. The parser applies binding + condition evaluation.
4.2 Escalation Edge Taxonomy
The most critical AWS privilege escalation paths (each is an edge in the graph):
| Edge (permission set) | Escalation target | CloudTrail event | Risk |
|---|---|---|---|
iam:CreatePolicyVersion | Any policy → Admin | CreatePolicyVersion | CRITICAL |
iam:SetDefaultPolicyVersion | Rotate to admin version | SetDefaultPolicyVersion | CRITICAL |
iam:CreateAccessKey on any user | Access as that user | CreateAccessKey | HIGH |
iam:PassRole + lambda:CreateFunction + lambda:InvokeFunction | Admin via Lambda execution role | CreateFunction, InvokeFunction | CRITICAL |
iam:PassRole + ec2:RunInstances | Admin via EC2 instance profile | RunInstances | HIGH |
iam:AttachUserPolicy | Attach admin policy to self | AttachUserPolicy | CRITICAL |
iam:AddUserToGroup | Add self to admin group | AddUserToGroup | HIGH |
sts:AssumeRole on an admin role | Direct role assumption | AssumeRole | CRITICAL |
For each, the Known-Pattern Matcher flags any principal that holds the permission set without
requiring a full graph traversal.
4.3 Detection Mapping
Every escalation edge maps to a CloudTrail or platform audit event:
EDGE_DETECTION = {
"aws:iam:CreatePolicyVersion": {
"event_source": "iam.amazonaws.com",
"event_name": "CreatePolicyVersion",
"cloudtrail_key": "requestParameters.policyArn",
"anomaly_signal": "new policy version within 1 hour of principal creation",
},
"aws:iam:PassRole:lambda": {
"event_source": "lambda.amazonaws.com",
"event_name": "CreateFunction20150331",
"cloudtrail_key": "requestParameters.role",
"anomaly_signal": "PassRole with admin ARN from non-admin principal",
},
# ... etc
}
The report includes, for each finding: "This escalation path would generate CloudTrail event X. To detect it, create a CloudWatch Metric Filter on X with condition Y."
4.4 Finding Prioritizer
Priority scoring per finding:
score = (privilege_gain_weight × 3) + (path_length_inverse × 2) + (detection_cost_inverse × 1)
privilege_gain_weight: CRITICAL (admin access) = 10, HIGH (elevated access) = 7, MEDIUM = 4path_length_inverse: single-step escalation scores higher (1 / path_length)detection_cost_inverse: low-noise escalation scores higher (1 / total_detection_cost)
Output: findings sorted by score descending. Top finding = easiest-to-exploit, highest-privilege, hardest-to-detect escalation path.
5. Tradeoffs
Unified graph vs. per-cloud models:
- Unified: cross-cloud lateral movement paths (e.g., AWS role assumed from a GCP service account) can be modeled as edges across cloud boundaries.
- Unified: higher implementation complexity; each cloud's policy semantics are different.
- Decision: unified graph with cloud-provider labels on edges; cross-cloud edges added when the export includes trust relationships between providers.
Known-pattern matcher vs. graph search:
- Known-pattern matcher is O(1) per principal against a fixed list — very fast, catches the "hot" escalation paths documented by Rhino Security Labs.
- Graph search finds novel paths the known-pattern list misses.
- Decision: both; the known-pattern matcher runs first as a fast pass, graph search runs after for comprehensive coverage.
Full policy simulation vs. approximation:
- Full AWS IAM policy simulation requires the full policy document, conditions, resource tags,
and session context. AWS provides the
SimulatePrincipalPolicyAPI for this, but that requires live connectivity. - For offline mode, we approximate by evaluating explicit deny → SCP allow → identity allow. This may produce false positives (overcounting permissions) but never false negatives (never misses a real escalation path).
- Decision: conservative approximation (overcount) for offline mode; document the limitation.
6. Detection-Forward Perspective
Every escalation path has a CloudTrail event. The report structure is:
- Finding: "Principal X can reach admin via
iam:CreatePolicyVersion" - CloudTrail signal: "Event
iam.amazonaws.com/CreatePolicyVersionfrom non-admin principal" - Remediation: "Remove
iam:CreatePolicyVersionfrom principal X's attached policies; add a deny SCP foriam:CreatePolicyVersionexcept for named IAM admin roles" - Detection rule: "CloudWatch metric filter:
{ $.eventName = "CreatePolicyVersion" && $.userIdentity.type != "AssumedRole:IAMAdmin" }"
This is the output that makes the audit actionable. A finding without the detection rule tells the client what is broken but not how to detect its exploitation. A finding with the detection rule gives the client both the remediation and the monitoring it needs while remediation is in progress.