This document defines the YAML schema used for documenting AWS IAM privilege escalation paths in this repository.
Current version: 1.6.0
- Breaking change:
parentfield changed from string to nested object - Added
parent.id(string, required if parent present): The parent path ID - Added
parent.modification(string, required if parent present): Explanation of what permission(s) the child adds to remove a prerequisite - Clarifies parent/child relationship: child adds required permission(s) that expand attack applicability by removing parent's prerequisites
- Parent/child is specifically for variants where child works in scenarios where parent fails
- Added
parentoptional field for structured parent-child relationships between paths - Enables filtering for primary vs. variant techniques
- Supports programmatic discovery of technique families
- Works alongside
discoveryAttribution.derivativeOffor comprehensive lineage tracking
- Added
detectionToolsoptional field for documenting open source detection tool coverage - Tool metadata (names, descriptions, GitHub links) stored in
metadata.json - YAML files only need to specify the detection source code URL for each tool
- Frontend always displays "Detection Coverage" section with placeholder message if no tools listed
- Breaking change:
toolSupportfield is deprecated and replaced withlearningEnvironments - Added
learningEnvironmentsoptional field for documenting available learning labs and CTF environments - Supports both open-source (self-hosted) and closed-source (hosted service) environments
- Breaking change:
attackVisualizationchanged from string (Mermaid) to structured object - Added support for interactive visualizations with nodes and edges
- Added support for conditional branching paths (multiple outcomes)
- Added support for click-to-view descriptions on nodes and edges
- Added node types:
principal,resource,action,outcome - Added optional branch identifiers and conditions for edges
- Added
attackVisualizationoptional field for Mermaid diagrams of attack paths - Provides visual representation of privilege escalation flows
- Supports simple and complex diagrams with optional styling
- Initial schema release
- Core required and optional fields
- Support for both new and legacy formats (permissions, prerequisites, exploitationSteps)
Files should be named using the format: {service}-{number}.yaml
Examples:
iam-001.yamlec2-001.yamllambda-001.yaml
- IAM-focused paths: Use
iam-###(e.g.,iam-001,iam-002) - PassRole combinations: Use the service of the resource being created/manipulated
iam:PassRole+ec2:RunInstances→ec2-001iam:PassRole+lambda:CreateFunction→lambda-001iam:PassRole+cloudformation:CreateStack→cloudformation-001
- Other service paths: Use the primary service (e.g.,
ssm-001,ec2-002) - Sequential numbering: IDs are assigned sequentially as paths are added
Unique identifier for the privilege escalation path.
- Format:
{service}-{number}(e.g.,iam-001) - Must be unique across all paths
Human-readable name using AWS IAM permission syntax.
- Single permission:
iam:CreatePolicyVersion - Multiple permissions:
iam:PassRole+ec2:RunInstances - Use
+to separate multiple permissions - Use exact AWS permission syntax (service:Action)
The type of privilege escalation.
Allowed values:
self-escalation- Modify own permissions directlyprincipal-access- Gain access to other principalsnew-passrole- Escalate via service + PassRole combinationcredential-access- Access to hardcoded credentials, not through IAMexisting-passrole- Modify resources to gain elevated access
List of all AWS services involved in the escalation path.
Example:
services:
- iam
- ec2IAM permissions for the escalation path, organized into required and additional helpful permissions.
The permissions object contains two arrays:
required(array of objects, required): Minimum permissions needed by the principal exploiting this pathadditional(array of objects, optional): Helpful get/list permissions that aid in exploitation but could come from a separate read-only principal
Each permission object contains:
permission(string, required): The IAM permission (e.g.,iam:CreatePolicyVersion)resourceConstraints(string, optional): Any resource-level constraints or requirements
Example:
permissions:
required:
- permission: "iam:PassRole"
resourceConstraints: "Must have access to a privileged role ARN"
- permission: "ec2:RunInstances"
resourceConstraints: "Must be able to launch EC2 instances"
additional:
- permission: "iam:ListRoles"
resourceConstraints: "Helpful for discovering available roles to pass"
- permission: "iam:GetRole"
resourceConstraints: "Useful for viewing role trust policies and permissions"Clear, concise explanation of how the privilege escalation works.
Should include:
- What the attack accomplishes
- How the permissions are abused
- The end result for the attacker
Step-by-step instructions for exploiting the path using different tools. This is an object where each key is a tool name and the value is an array of step objects.
Supported tools:
awscli- AWS Command Line Interfacepacu- Pacu (AWS exploitation framework)pmapper- Principal Mapperstratus- Stratus Red Teamleonidas- Leonidas (AWS attack simulation)nebula- Nebulapathrunner- Pathrunner
Each step object contains:
step(integer, required): Step number (1, 2, 3, etc.)command(string, required): The command to execute for the tooldescription(string, required): Explanation of what this step does
Example:
exploitationSteps:
awscli:
- step: 1
command: "aws iam create-policy-version --policy-arn arn:aws:iam::123456789012:policy/MyPolicy --policy-document file://admin_policy.json --set-as-default"
description: "Create a new policy version with administrative permissions and set it as default"
- step: 2
command: "aws sts get-caller-identity"
description: "Verify that the new permissions are active"
pacu:
- step: 1
command: "run iam__create_policy_version --policy-arn arn:aws:iam::123456789012:policy/MyPolicy --policy-file admin_policy.json"
description: "Use Pacu to create a new policy version with admin permissions"Explains the limitations of this privilege escalation path and under what conditions it provides administrative access versus limited access.
This field is particularly important for PassRole-based paths, where access level depends on the permissions of available roles in the environment.
Example:
limitations: |
This path provides administrative access only if the passed role has administrative
permissions (e.g., AdministratorAccess policy). If only limited roles are available,
you gain access limited to those permissions. However, even limited access may enable
multi-hop attacks.Security recommendations for preventing and detecting this escalation path.
Format requirement: All recommendations MUST use multi-line YAML format with the | pipe operator (never use quoted strings with \n).
Should include:
- How to restrict the permissions using least privilege
- Monitoring and detection strategies
- IAM policy conditions and constraints
- Best practices
For iam:PassRole privilege escalation paths, use this standardized template adapted for the specific service:
recommendation: |
High powered service roles + overly permissive `iam:PassRole` is what makes this privilege escalation path exploitable and impactful.
- **Avoid administrative service roles** - Very rarely does a [SERVICE_RESOURCE] need administrative access. Use the principle of least privilege.
- **Avoid granting `iam:PassRole` on all resources** - Whenever possible, restrict `iam:PassRole` to specific roles or specific services.
Use IAM policy conditions to restrict which roles can be passed and to which services:
```json
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::ACCOUNT_ID:role/Specific[ServiceRole]",
"Condition": {
"StringEquals": {
"iam:PassedToService": "[service].amazonaws.com"
}
}
}- Monitor CloudTrail for unusual [SERVICE_RESOURCE] creation followed by immediate [execution/invocation]
- Monitor CloudTrail for [SERVICE_RESOURCE] creation by principals who do not usually create [resources]
- Monitor CloudTrail for roles being passed to [SERVICE] that haven't been used before
- Monitor and alert on [SERVICE_RESOURCE] creation with privileged roles
- Regularly audit [SERVICE_RESOURCES] for excessive IAM permissions
- Regularly audit all IAM roles that trust the [SERVICE] service and down-scope any roles with administrative access
**Template placeholders to adapt:**
- `[SERVICE_RESOURCE]` - The AWS resource type (e.g., "Lambda function", "EC2 instance", "SageMaker notebook")
- `[ServiceRole]` - The role name pattern (e.g., "LambdaRole", "EC2Role", "SageMakerRole")
- `[service]` - The service name (e.g., "lambda", "ec2", "sagemaker")
- `[SERVICE]` - The capitalized service name (e.g., "Lambda", "EC2", "SageMaker")
- `[execution/invocation]` - Service-specific action (e.g., "invocation", "execution", "startup")
- `[resources]` - Plural resource name (e.g., "functions", "instances", "notebooks")
- `[SERVICE_RESOURCES]` - Plural capitalized (e.g., "Lambda functions", "EC2 instances")
**Examples of adapted recommendations:**
- **Lambda**: "Lambda function", "lambda.amazonaws.com", "invocation", "functions"
- **EC2**: "EC2 instance", "ec2.amazonaws.com", "execution", "instances"
- **SageMaker**: "SageMaker notebook", "sagemaker.amazonaws.com", "startup", "notebooks"
- **CloudFormation**: "CloudFormation stack", "cloudformation.amazonaws.com", "resource creation", "stacks"
**For non-PassRole paths**, use multi-line format with service-specific prevention and monitoring guidance.
Example (non-PassRole):
```yaml
recommendation: |
Restrict access to `iam:CreatePolicyVersion` using the principle of least privilege.
Very few principals need this permission, so it should be restricted to only the
few principals that need it.
Monitor use of this sensitive permission using CloudSIEM detections, and look for usage anomalies.
Rich attribution information supporting primary discovery, derivative relationships, and original source tracking. This field allows documenting complex attribution scenarios including derivative work and multi-level attribution chains.
This is the preferred attribution format. All new paths should use discoveryAttribution.
The discoveryAttribution object contains up to three sub-objects:
Information about who first documented this specific attack path variation.
Fields:
author(string, optional): Researcher's name (use for individual contributors)source(string, optional): Source name (use for organizations/websites like "HackTricks" or "pathfinding.cloud")organization(string, optional): Organization name (if author is specified)date(string, optional): Year or date documented (YYYY format)link(string, optional): URL to the source documentation
Note: Use either author OR source, not both. Use author for individual researchers and source for organizations/websites.
Information about what this path is derived from. Only include if this path is a variation of another existing path.
Fields:
pathId(string, required): The path ID this is derived from (e.g.,ecs-004)modification(string, required): Description of what was modified or added compared to the original path
Information about the ultimate origin if there's a multi-level derivative chain. Only include if this path is a derivative of a derivative and you want to trace back to the original discovery.
Note: Skip ultimateOrigin if it would be the same as derivativeOf.pathId - only use it for multi-level chains.
Fields:
pathId(string, required): The original path ID (e.g.,ecs-004)author(string, required): Original discoverer's nameorganization(string, optional): Organization namedate(string, optional): Year discovered (YYYY format)link(string, required): URL to original research/documentation
Example 1 (Original discovery by researcher):
discoveryAttribution:
firstDocumented:
author: Spencer Gietzen
organization: Rhino Security Labs
date: 2018
link: https://rhinosecuritylabs.com/aws/weaponizing-ecs-task-definitions-steal-credentials-running-containers/Example 2 (Derivative documented by another source):
discoveryAttribution:
firstDocumented:
source: HackTricks
link: https://cloud.hacktricks.wiki/en/pentesting-cloud/aws-security/aws-privilege-escalation/aws-ecs-privesc/
derivativeOf:
pathId: ecs-004
modification: "Uses ecs:CreateService instead of ecs:RunTask to execute the malicious task definition"
ultimateOrigin:
pathId: ecs-004
author: Spencer Gietzen
organization: Rhino Security Labs
date: 2018
link: https://rhinosecuritylabs.com/aws/weaponizing-ecs-task-definitions-steal-credentials-running-containers/Example 3 (New path created on pathfinding.cloud):
discoveryAttribution:
firstDocumented:
author: Seth Art
organization: Datadog
date: 2025
derivativeOf:
pathId: ecs-003
modification: "Adds ecs:CreateCluster permission for scenarios where no ECS cluster exists"
ultimateOrigin:
pathId: ecs-004
author: Spencer Gietzen
organization: Rhino Security Labs
date: 2018
link: https://rhinosecuritylabs.com/aws/weaponizing-ecs-task-definitions-steal-credentials-running-containers/Example 4 (Direct derivative, no multi-level chain):
discoveryAttribution:
firstDocumented:
author: Seth Art
organization: Datadog
date: 2025
derivativeOf:
pathId: ecs-004
modification: "Adds ecs:CreateCluster permission for scenarios where no ECS cluster exists"
# Note: ultimateOrigin omitted because it would be the same as derivativeOf.pathIdDefines a parent-child relationship when this path is a variant that adds required permission(s) to bypass a prerequisite of the parent technique.
This field creates a structured parent-child relationship between paths, enabling:
- Easy filtering for primary vs. variant paths in the UI
- Programmatic discovery of related techniques
- Clear explanation of what makes the child variant necessary
Parent object structure:
id(string, required): The parent path IDmodification(string, required): Explanation of what required permission(s) this child adds and which prerequisite it removes/bypasses
Terminology Note: We use different terminology in different contexts for clarity:
- In YAML/code:
parentfield (concise, follows common data structure conventions) - In UI/documentation: "Primary Technique" and "Variants" (semantic, conveys meaning)
Rationale:
parentfield name: Natural for pointing upward in the hierarchy (parent.id), concise in YAML- "Primary Technique": Conveys that this is the foundational/original technique
- "Variant": Explains WHAT it is (a modification that expands applicability), not just that it's a child
This hybrid approach gives semantic clarity where users see it (UI/docs) while keeping code simple and conventional.
When to use parent/child:
- ✅ Parent path is exploitable with specific prerequisites (e.g., "user must have < 2 access keys")
- ✅ Child path adds required permission(s) that remove/bypass those prerequisites
- ✅ Child works in scenarios where parent fails
- ✅ Same core attack technique, just expanded applicability
When NOT to use parent/child (use relatedPaths instead):
- ❌ Different categories (new-passrole vs existing-passrole)
- ❌ Alternative approaches that don't remove prerequisites (siblings)
- ❌ Neither path works without additional permissions (no true "base" case)
Key principle: Child adds permission(s) to the required section that expand when the attack works by removing parent's prerequisites.
Examples:
Parent/Child (correct usage):
- IAM-002 (CreateAccessKey) → IAM-003 (CreateAccessKey + DeleteAccessKey)
- Parent requires: User has < 2 access keys
- Child removes prerequisite: Can delete existing key first
- Lambda-003 (UpdateFunctionCode) → Lambda-004 (UpdateFunctionCode + InvokeFunction)
- Parent requires: Function has existing trigger
- Child removes prerequisite: Brings own execution via InvokeFunction
Not Parent/Child (siblings - use relatedPaths):
- Glue-003 (PassRole + CreateJob + StartJobRun) and Glue-004 (PassRole + CreateJob + CreateTrigger)
- Neither works without the third permission
- Different execution methods, not removing prerequisites
- These are alternative approaches, not variants
Relationship to discoveryAttribution:
- The
parentfield is for machine-readable technical relationships - The
discoveryAttributionfield is for human-readable credit and attribution - Keep both - they serve different purposes
Example:
# IAM-003 (child/variant)
id: iam-003
name: iam:CreateAccessKey + iam:DeleteAccessKey
parent:
id: iam-002
modification: "Adds iam:DeleteAccessKey to enable exploitation even when the target user already has 2 access keys (AWS maximum). The attacker deletes one existing key before creating their own, removing the prerequisite that the user must have fewer than 2 keys."
discoveryAttribution:
firstDocumented:
author: Spencer Gietzen
organization: Rhino Security Labs
date: 2019
link: https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/# IAM-002 (primary/parent technique)
id: iam-002
name: iam:CreateAccessKey
# No parent field - this is a primary technique
discoveryAttribution:
firstDocumented:
author: Spencer Gietzen
organization: Rhino Security Labs
date: 2019
link: https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/# Lambda-004 (child/variant)
id: lambda-004
name: lambda:UpdateFunctionCode + lambda:InvokeFunction
parent:
id: lambda-003
modification: "Adds lambda:InvokeFunction to enable exploitation even when the target function has no existing trigger mechanism. Removes the prerequisite that something must already be invoking the function."Conditions that must be met for the escalation to work.
New format (recommended): Object with tab names as keys (e.g., admin, lateral), each containing an array of prerequisite strings. This is especially useful for PassRole paths where admin vs. lateral movement have different requirements.
Legacy format: Simple array of prerequisite strings (still supported for paths with uniform prerequisites).
Supported tab names:
admin- Prerequisites for administrative privilege escalationlateral- Prerequisites for lateral movement (non-admin access)
Example (new format):
prerequisites:
admin:
- "A role must exist that trusts ec2.amazonaws.com to assume it"
- "The role must have administrative permissions (e.g., AdministratorAccess)"
- "The role must have an instance profile associated with it"
lateral:
- "A role must exist that trusts ec2.amazonaws.com to assume it"
- "The role must have an instance profile associated with it"Example (legacy format):
prerequisites:
- "User must have fewer than 2 access keys"
- "Target policy must be attached to the user's role or group"Links to external resources, blog posts, and documentation.
Each reference object contains:
title(string, required): Title of the resourceurl(string, required): Full URL
Example:
references:
- title: "AWS Privilege Escalation Methods"
url: "https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/"
- title: "IAM Vulnerable Repository"
url: "https://github.com/BishopFox/iam-vulnerable"List of related privilege escalation path IDs.
Example:
relatedPaths:
- "iam-002"
- "iam-007"Links to detection rules in various security platforms.
Each detection rule object contains:
platform(string, required): Security platform name (e.g., "CloudSIEM", "AWS Config", "Splunk")ruleId(string, optional): Rule identifierurl(string, optional): Link to the rule documentation
Example:
detectionRules:
- platform: "CloudSIEM"
ruleId: "7b6-2a8-df9"
url: "https://docs.datadoghq.com/security/default_rules/7b6-2a8-df9/"Documents available learning labs, CTF environments, and cloud security training platforms where this privilege escalation path can be practiced safely.
This field is an object where each key is the environment name (e.g., iam-vulnerable, pathfinding-labs, cloudfoxable, cybr, pwndlabs) and the value is an object with details about that environment.
Common fields for all environments:
type(string, required): Environment type - eitheropen-sourceorclosed-sourcedescription(string, required): Brief explanation of what the learning environment providesscenario(string, optional): The specific scenario/lab name or URL within the environment
Additional fields for open-source environments:
githubLink(string, required for open-source): URL to the GitHub repository
Additional fields for closed-source environments:
scenarioPricingModel(string, required for closed-source): Eitherpaidorfree
Example (open-source):
learningEnvironments:
iam-vulnerable:
type: open-source
githubLink: https://github.com/BishopFox/iam-vulnerable
scenario: IAM-UpdateLoginProfile
description: "Deploy Terraform into your own AWS account and practice individual exploitation paths"
pathfinding-labs:
type: open-source
githubLink: https://github.com/DataDog/pathfinding-labs
scenario: iam-updateloginprofile
description: "Deploy Terraform scenarios individually or in groups, each with attack and cleanup scripts"Example (closed-source):
learningEnvironments:
cybr:
type: closed-source
description: "A hosted learning environment with free and paid labs accessible by subscription"
scenario: https://cybr.com/hands-on-labs/lab/iam-updateloginprofile-privilege-escalation/
scenarioPricingModel: paid
pwndlabs:
type: closed-source
description: "A cloud security training platform with hands-on AWS labs"
scenario: https://pwnedlabs.io/labs/iam-privilege-escalation
scenarioPricingModel: freeNote: This field is deprecated as of schema version 1.3.0 and replaced by learningEnvironments. It will be removed in a future version.
Indicates which security tools support detecting/testing this path.
Boolean fields:
pmapper(boolean): PMapper supportiamVulnerable(boolean): IAM Vulnerable supportpacu(boolean): Pacu supportprowler(boolean): Prowler support
Example:
toolSupport:
pmapper: true
iamVulnerable: true
pacu: false
prowler: trueDocuments which open source security assessment tools can detect this privilege escalation path and links to their detection source code.
This field is an object where each key is the tool name (e.g., pmapper, cloudsplaining, pacu, prowler, scoutsuite) and the value is a URL string pointing to the specific source code file or line where this path's detection logic is implemented.
Supported tool names:
pmapper: Principal Mapper by NCC Groupcloudsplaining: Cloudsplaining by Salesforcepacu: Pacu AWS exploitation framework by Rhino Security Labsprowler: Prowler multi-cloud security tool
Note: The frontend loads tool metadata (display names, GitHub repository links, descriptions) from metadata.json. Only the detectionSource URL needs to be specified in each path's YAML file.
Example:
detectionTools:
pmapper: https://github.com/nccgroup/PMapper/blob/master/principalmapper/graphing/iam_edges.py#L123
cloudsplaining: https://github.com/salesforce/cloudsplaining/blob/master/cloudsplaining/shared/constants.py#L45
pacu: https://github.com/RhinoSecurityLabs/pacu/blob/master/pacu/modules/iam__privesc_scan/main.py#L234Frontend Behavior:
- If this field is present with tool entries, the "Detection Coverage" section displays tabs for each tool with:
- Tool name (from metadata)
- Link to GitHub repository (from metadata)
- Link to detection source code (from YAML)
- Tool description with pros/cons (from metadata)
- If this field is absent or empty, the section displays: "This path is not currently supported by any open source detection tools."
- The section is always visible on the frontend regardless of whether tools are listed
A structured representation of the attack path that creates an interactive visualization showing the flow from starting principal to target outcomes, including conditional branches.
Structure:
nodes(array, required): List of nodes in the attack graphedges(array, required): List of edges connecting nodes
Node Fields:
id(string, required): Unique identifier for the nodelabel(string, required): Display text for the nodetype(string, required): Node type - one of:principal: IAM users, roles, or starting principal (any entity with IAM identity)resource: AWS resources involved in attack (EC2, Lambda, S3, etc.) - NOT IAM users/rolespayload: Attacker exploitation choice, malicious code/commands being executedaction: (Deprecated - usepayloadinstead) Action or intermediate stepoutcome: Final outcome/result
color(string, optional): Hex color code (e.g.,#ff9999). Defaults based on type if not specifieddescription(string, optional): Markdown description shown when node is clicked
Important Type Distinctions:
principalvsresource: ALL IAM users and roles must be typed asprincipal, never asresource. This includes starting principals, target users/roles, assumed roles, execution roles, etc.payloadvs edges: IAM actions (likeiam:CreateAccessKey) should be represented as edge labels, NOT as payload nodes. Payload nodes represent what the attacker DOES with acquired permissions (e.g., "Execute malicious script", "Exfiltrate credentials")- When to use
payload: Use for attacker exploitation choices like: User Data scripts, Lambda function code, buildspec commands, reverse shells, credential exfiltration methods
Description Formatting Guidelines:
All description fields (for both nodes and edges) should follow these formatting rules to ensure consistent display in the frontend:
-
Single-line paragraphs: Text should flow as single-line paragraphs without artificial line breaks at ~80 characters
- ✅ GOOD:
The principal with iam:PassRole and ec2:RunInstances permissions. Can be an IAM user or role. - ❌ BAD:
The principal with iam:PassRole and ec2:RunInstances permissions.\nCan be an IAM user or role.(artificial break)
- ✅ GOOD:
-
Multi-line structure: Use line breaks only for intentional separation:
- Separate paragraphs (different thoughts or topics)
- Code blocks with bash/python commands
- Bulleted or numbered lists
- Example commands followed by explanations
-
Code blocks: Always preserve multi-line structure for commands
description: | Execute the command to create a Lambda function. Command: ```bash aws lambda create-function \ --function-name privesc-function \ --runtime python3.9 \ --role "arn:aws:iam::ACCOUNT_ID:role/PRIVILEGED_ROLE"
This creates the function with the privileged role attached.
-
Lists: Keep list items on separate lines
description: | The script could perform several actions: - Attach AdministratorAccess policy to starting principal - Create new admin access keys for starting principal - Add starting principal to admin group
Edge Fields:
from(string, required): Source node idto(string, required): Target node idlabel(string, required): Edge label describing the action/transitionbranch(string, optional): Branch identifier (e.g.,A,B,C,A1,B1) for conditional pathscondition(string, optional): Condition type (e.g.,admin,no_permissions,some_permissions,iam_write_permissions)description(string, optional): Markdown description shown when edge is clicked
Critical Edge Labeling Rules:
- Edge labels should ONLY include permissions from
permissions.required - Reconnaissance permissions (describe/list/get) from
permissions.additionalshould NOT be edge labels - Optional reconnaissance permissions can be mentioned in node descriptions instead
- Edges represent the required attack path flow, not helpful optional reconnaissance steps
- Example: ❌ WRONG - Edge labeled "ec2:DescribeLaunchTemplates" when this is in additional permissions
- Example: ✅ CORRECT - Edge labeled "ec2:CreateLaunchTemplateVersion + ec2:ModifyLaunchTemplate" (only required permissions)
Important Edge Rendering Rules:
- Conditional edges (those with
branchorconditionfields) are rendered as dashed lines with NO visible label on the graph - Transitive edges (those without
branchorcondition) are rendered as solid lines WITH visible labels on the graph - All edges (both conditional and transitive) show their label in the tooltip when clicked
- Edge labels should describe the action or condition clearly, as they will be shown in tooltips
Node Label Conventions:
- Use
starting-principalwhen the attack works from either a user or role - Use
Starting UserorStarting Roleonly when the attack is specific to one type - Use descriptive, specific labels for resources (e.g.,
target-role,EC2 Instance) - Use clear outcome labels (e.g.,
Effective Administrator,No additional access)
Color Conventions (defaults):
principal(users/roles):#ff9999(red)resource(AWS resources):#ffcc99(orange)payload(attacker actions):#99ccff(blue)action(deprecated):#99ccff(blue) - usepayloadinsteadoutcome(success):#99ff99(green)outcome(partial):#ffeb99(yellow)outcome(dead end):#cccccc(gray)
When to Use Conditional Branching:
Use conditional branching when the outcome of an attack path depends on environmental factors that vary:
-
Permission-dependent outcomes: The privileges gained depend on the permissions of a resource (e.g., assumed role, passed role)
- Example: sts:AssumeRole gains admin only if the target role has admin permissions
-
Multiple attack approaches: Different techniques to exploit the same vulnerability
- Example: EC2 PassRole can use User Data script OR reverse shell
-
Resource-dependent outcomes: Success depends on what resources exist in the environment
- Example: AttachUserPolicy gains admin only if an admin policy exists to attach
Branch Naming Convention:
- Use single letters (
A,B,C) for major branches (different attack approaches) - Use numbered variants (
A1,A2,A3) for conditional outcomes within a branch - Example: Branch
A= User Data approach,A1= admin outcome,A2= partial outcome,A3= minimal outcome
Common Conditional Patterns:
For PassRole-based attacks, use this three-outcome pattern:
- admin outcome (green): Target resource has administrative permissions
- some_permissions outcome (yellow): Target resource has elevated but non-admin permissions
- no_permissions outcome (gray): Target resource has minimal permissions
Example (simple with branching):
attackVisualization:
nodes:
- id: start
label: Starting Principal
type: principal
description: |
The principal initiating the attack. Can be an IAM user or role
with sts:AssumeRole permission on the target role.
- id: target_role
label: Target Role
type: resource
description: |
The privileged role being assumed. Must have a trust policy that
allows the starting principal to assume it.
- id: admin
label: Effective Administrator
type: outcome
description: Full administrative access to the AWS account.
- id: no_access
label: No additional access
type: outcome
color: '#cccccc'
description: |
The assumed role has no interesting permissions beyond what
the starting principal already had.
- id: some_perms
label: Check for Additional Access
type: outcome
color: '#ffeb99'
description: |
The assumed role has some permissions. Check for data access
or additional privilege escalation paths.
edges:
- from: start
to: target_role
label: sts:AssumeRole
description: |
Use sts:AssumeRole to assume the target role. Requires both
the permission and a matching trust policy.
- from: target_role
to: admin
label: If role has admin permissions
branch: A
condition: admin
description: |
If the target role has AdministratorAccess or equivalent,
attacker gains full administrative access.
- from: target_role
to: no_access
label: If role has no interesting permissions
branch: B
condition: no_permissions
description: |
If the role only has minimal permissions, there may be
no additional access gained.
- from: target_role
to: some_perms
label: If role has some permissions
branch: C
condition: some_permissions
description: |
If the role has permissions to access data or other escalation
paths, further exploration is needed.Example (complex multi-step attack):
attackVisualization:
nodes:
- id: start
label: Starting Principal
type: principal
- id: ec2_instance
label: EC2 Instance
type: resource
description: New EC2 instance created with privileged instance profile
- id: priv_role
label: privileged-role
type: resource
description: IAM role assumed by the EC2 instance
- id: exfil
label: Exfiltrate credentials
type: action
color: '#99ccff'
description: Access instance via User Data or SSM to steal credentials
- id: admin
label: Effective Administrator
type: outcome
edges:
- from: start
to: ec2_instance
label: iam:PassRole + ec2:RunInstances
description: Launch EC2 instance and pass privileged role to it
- from: ec2_instance
to: priv_role
label: Instance assumes role
description: EC2 instance automatically assumes the passed role
- from: priv_role
to: exfil
label: User Data / SSM access
description: Access the instance to retrieve temporary credentials
- from: exfil
to: admin
label: Use stolen credentials
description: Use the exfiltrated credentials to gain admin accessid: "iam-001"
name: "iam:CreatePolicyVersion"
category: "self-escalation"
services:
- iam
permissions:
required:
- permission: "iam:CreatePolicyVersion"
resourceConstraints: "Policy ARN must be in the Resource section and policy must be attached to the actor"
description: |
Anyone with access to iam:CreatePolicyVersion can create a new version of an IAM policy.
If a user can create a new version of a policy that is already attached to them, they can
grant themselves administrative privileges by creating a new policy version with elevated
permissions and setting it as the default version.
prerequisites:
- condition: "Policy must already be attached to the actor's user, role, or group"
type: "resource-state"
exploitationSteps:
- step: 1
command: "aws iam create-policy-version --policy-arn arn:aws:iam::123456789012:policy/MyPolicy --policy-document file://admin_policy.json --set-as-default"
description: "Create a new policy version with administrative permissions and set it as default"
- step: 2
command: "aws sts get-caller-identity"
description: "Verify that the new permissions are now active"
recommendation: |
Restrict access to iam:CreatePolicyVersion using the principle of least privilege.
Very few principals need this permission, so it should be restricted to only the
few principals that need it. Monitor use of this sensitive permission using
CloudSIEM detections, and look for usage anomalies.
discoveryAttribution:
firstDocumented:
author: "Spencer Gietzen"
organization: "Rhino Security Labs"
date: "2019"
link: "https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/"
references:
- title: "AWS Privilege Escalation Methods and Mitigation"
url: "https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/"
relatedPaths:
- "iam-002"
- "iam-007"
- "iam-008"
detectionRules:
- platform: "CloudSIEM"
ruleId: "7b6-2a8-df9"
url: "https://docs.datadoghq.com/security/default_rules/7b6-2a8-df9/"
detectionTools:
pmapper: https://github.com/nccgroup/PMapper/blob/master/principalmapper/graphing/iam_edges.py#L123
cloudsplaining: https://github.com/salesforce/cloudsplaining/blob/master/cloudsplaining/shared/constants.py#L45
learningEnvironments:
iam-vulnerable:
type: open-source
githubLink: https://github.com/BishopFox/iam-vulnerable
scenario: IAM-CreatePolicyVersion
description: "Deploy Terraform into your own AWS account and practice individual exploitation paths"
attackVisualization: |
graph LR
A[user-with-policy] -->|iam:CreatePolicyVersion| B[Modify attached policy]
B -->|Set as default| C[Policy with admin permissions]
C -->|Automatic effect| D[Effective Administrator]
style A fill:#ff9999,stroke:#333,stroke-width:2px
style B fill:#ffcc99,stroke:#333,stroke-width:2px
style C fill:#ffcc99,stroke:#333,stroke-width:2px
style D fill:#99ff99,stroke:#333,stroke-width:2pxAll YAML files must validate against this schema. A validation script is provided in scripts/validate-schema.py to check new contributions.
To validate a file:
python scripts/validate-schema.py data/paths/iam/iam-001.yamlTo validate all files:
python scripts/validate-schema.py data/paths/