Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-SkillsFramework mappings
MITRE ATT&CK
Overview
This skill uses boto3 and Cloudsplaining-style analysis to identify IAM privilege escalation paths in AWS accounts. It downloads the account authorization details, analyzes each policy for dangerous permission combinations (iam:PassRole + lambda:CreateFunction, iam:CreatePolicyVersion, sts:AssumeRole), and flags policies that violate least-privilege principles.
When to Use
- When investigating security incidents that require detecting aws iam privilege escalation
- When building detection rules or threat hunting queries for this domain
- When SOC analysts need structured procedures for this analysis type
- When validating security monitoring coverage for related attack techniques
Prerequisites
- Python 3.8+ with boto3 library
- AWS credentials with IAM read-only access (iam:GetAccountAuthorizationDetails)
- Optional: cloudsplaining Python package for HTML report generation
Steps
- Download IAM Authorization Details — Call iam:GetAccountAuthorizationDetails to retrieve all users, groups, roles, and policies
- Analyze Policies for Privilege Escalation — Check each policy for known escalation permission combinations
- Identify Wildcard Resource Policies — Flag policies using Resource: "*" with dangerous actions
- Map Principal-to-Policy Relationships — Build a graph of which principals can access which escalation paths
- Score and Prioritize Findings — Rank findings by severity based on escalation vector type
- Generate Report — Produce structured JSON report with remediation guidance
Expected Output
- JSON report of privilege escalation findings with severity scores
- List of dangerous permission combinations per principal
- Wildcard resource policy audit results
- Remediation recommendations for each finding
Source materials
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md2.2 KB
AWS IAM Privilege Escalation Detection API Reference
boto3 IAM API
import boto3
iam = boto3.client("iam")
# Download full account authorization details
paginator = iam.get_paginator("get_account_authorization_details")
for page in paginator.paginate():
users = page["UserDetailList"]
roles = page["RoleDetailList"]
policies = page["Policies"]
# Get specific policy version
policy = iam.get_policy_version(
PolicyArn="arn:aws:iam::123456789012:policy/MyPolicy",
VersionId="v2"
)Cloudsplaining CLI
# Install
pip install cloudsplaining
# Download account authorization details
cloudsplaining download --profile myprofile
# Scan authorization file for privilege escalation
cloudsplaining scan --input-file default.json --output results/
# Scan a single policy file
cloudsplaining scan-policy-file --input-file policy.jsonKnown Privilege Escalation Vectors
| Vector | Required Permissions | Risk |
|---|---|---|
| CreatePolicyVersion | iam:CreatePolicyVersion |
Critical |
| AttachUserPolicy | iam:AttachUserPolicy |
Critical |
| PutUserPolicy | iam:PutUserPolicy |
Critical |
| PassRole + Lambda | iam:PassRole, lambda:CreateFunction, lambda:InvokeFunction |
Critical |
| PassRole + EC2 | iam:PassRole, ec2:RunInstances |
Critical |
| UpdateAssumeRolePolicy | iam:UpdateAssumeRolePolicy |
Critical |
| PassRole + CloudFormation | iam:PassRole, cloudformation:CreateStack |
High |
| PassRole + SSM | iam:PassRole, ssm:SendCommand |
Critical |
AWS CLI IAM Audit Commands
# List all users with attached policies
aws iam list-users --output json
# Get user's inline policies
aws iam list-user-policies --user-name admin
# Get attached managed policies
aws iam list-attached-user-policies --user-name admin
# Simulate policy evaluation
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/admin \
--action-names iam:CreatePolicyVersion iam:AttachUserPolicy
# Get account authorization details (full dump)
aws iam get-account-authorization-details > auth-details.jsonParliament (Policy Linting)
pip install parliament
parliament --file policy.jsonScripts 1
agent.py9.3 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Detect AWS IAM privilege escalation paths using boto3 policy analysis."""
import json
import argparse
from datetime import datetime
from collections import defaultdict
try:
import boto3
HAS_BOTO3 = True
except ImportError:
HAS_BOTO3 = False
ESCALATION_COMBOS = [
{"name": "CreatePolicyVersion", "permissions": ["iam:CreatePolicyVersion"],
"description": "Create new policy version with elevated privileges", "severity": "critical"},
{"name": "SetDefaultPolicyVersion", "permissions": ["iam:SetDefaultPolicyVersion"],
"description": "Set a previously created permissive policy version as default", "severity": "critical"},
{"name": "PassRole+Lambda", "permissions": ["iam:PassRole", "lambda:CreateFunction", "lambda:InvokeFunction"],
"description": "Pass privileged role to Lambda and invoke it", "severity": "critical"},
{"name": "PassRole+EC2", "permissions": ["iam:PassRole", "ec2:RunInstances"],
"description": "Launch EC2 with privileged instance profile", "severity": "critical"},
{"name": "PassRole+CloudFormation", "permissions": ["iam:PassRole", "cloudformation:CreateStack"],
"description": "Deploy CloudFormation stack with privileged role", "severity": "high"},
{"name": "AttachUserPolicy", "permissions": ["iam:AttachUserPolicy"],
"description": "Attach AdministratorAccess to own user", "severity": "critical"},
{"name": "AttachGroupPolicy", "permissions": ["iam:AttachGroupPolicy"],
"description": "Attach AdministratorAccess to own group", "severity": "critical"},
{"name": "AttachRolePolicy", "permissions": ["iam:AttachRolePolicy"],
"description": "Attach elevated policy to assumable role", "severity": "high"},
{"name": "PutUserPolicy", "permissions": ["iam:PutUserPolicy"],
"description": "Add inline policy to own user", "severity": "critical"},
{"name": "PutGroupPolicy", "permissions": ["iam:PutGroupPolicy"],
"description": "Add inline policy to own group", "severity": "high"},
{"name": "AssumeRole", "permissions": ["sts:AssumeRole"],
"description": "Assume role with higher privileges", "severity": "medium"},
{"name": "PassRole+SageMaker", "permissions": ["iam:PassRole", "sagemaker:CreateNotebookInstance"],
"description": "Create SageMaker notebook with privileged role", "severity": "high"},
{"name": "UpdateAssumeRolePolicy", "permissions": ["iam:UpdateAssumeRolePolicy"],
"description": "Modify trust policy to assume privileged role", "severity": "critical"},
{"name": "PassRole+SSM", "permissions": ["iam:PassRole", "ssm:SendCommand"],
"description": "Execute commands on EC2 via SSM with privileged role", "severity": "critical"},
]
def get_account_authorization(profile=None):
"""Download full IAM authorization details via boto3."""
session = boto3.Session(profile_name=profile) if profile else boto3.Session()
iam = session.client("iam")
paginator = iam.get_paginator("get_account_authorization_details")
details = {"UserDetailList": [], "GroupDetailList": [], "RoleDetailList": [], "Policies": []}
for page in paginator.paginate():
details["UserDetailList"].extend(page.get("UserDetailList", []))
details["GroupDetailList"].extend(page.get("GroupDetailList", []))
details["RoleDetailList"].extend(page.get("RoleDetailList", []))
details["Policies"].extend(page.get("Policies", []))
return details
def extract_policy_actions(policy_document):
"""Extract all actions from a policy document."""
actions = set()
if isinstance(policy_document, str):
policy_document = json.loads(policy_document)
for statement in policy_document.get("Statement", []):
if statement.get("Effect") != "Allow":
continue
stmt_actions = statement.get("Action", [])
if isinstance(stmt_actions, str):
stmt_actions = [stmt_actions]
for action in stmt_actions:
actions.add(action.lower())
resource = statement.get("Resource", "")
if resource == "*" or (isinstance(resource, list) and "*" in resource):
actions.add("__wildcard_resource__")
return actions
def check_escalation_paths(actions):
"""Check if a set of actions contains known privilege escalation combos."""
findings = []
has_wildcard = "iam:*" in actions or "*" in actions
for combo in ESCALATION_COMBOS:
required = {p.lower() for p in combo["permissions"]}
if has_wildcard or required.issubset(actions):
findings.append({
"escalation_path": combo["name"],
"required_permissions": combo["permissions"],
"description": combo["description"],
"severity": combo["severity"],
})
return findings
def analyze_wildcard_policies(actions):
"""Flag dangerous wildcard action patterns."""
dangerous_wildcards = []
wildcard_patterns = [a for a in actions if a.endswith(":*") or a == "*"]
dangerous_services = {"iam:*", "sts:*", "lambda:*", "ec2:*", "s3:*", "cloudformation:*", "*"}
for wp in wildcard_patterns:
if wp in dangerous_services:
dangerous_wildcards.append({
"action": wp, "severity": "critical" if wp in {"iam:*", "*"} else "high",
"finding": f"Wildcard action {wp} grants broad access",
})
return dangerous_wildcards
def analyze_account(auth_details):
"""Analyze all principals for escalation paths."""
findings = []
for user in auth_details.get("UserDetailList", []):
all_actions = set()
for policy in user.get("UserPolicyList", []):
all_actions.update(extract_policy_actions(policy.get("PolicyDocument", {})))
for policy in user.get("AttachedManagedPolicies", []):
arn = policy.get("PolicyArn", "")
for p in auth_details.get("Policies", []):
if p.get("Arn") == arn:
for ver in p.get("PolicyVersionList", []):
if ver.get("IsDefaultVersion"):
all_actions.update(extract_policy_actions(ver.get("Document", {})))
escalations = check_escalation_paths(all_actions)
wildcards = analyze_wildcard_policies(all_actions)
if escalations or wildcards:
findings.append({
"principal_type": "User", "principal_name": user.get("UserName"),
"arn": user.get("Arn"), "escalation_paths": escalations,
"wildcard_findings": wildcards,
})
for role in auth_details.get("RoleDetailList", []):
all_actions = set()
for policy in role.get("RolePolicyList", []):
all_actions.update(extract_policy_actions(policy.get("PolicyDocument", {})))
for policy in role.get("AttachedManagedPolicies", []):
arn = policy.get("PolicyArn", "")
for p in auth_details.get("Policies", []):
if p.get("Arn") == arn:
for ver in p.get("PolicyVersionList", []):
if ver.get("IsDefaultVersion"):
all_actions.update(extract_policy_actions(ver.get("Document", {})))
escalations = check_escalation_paths(all_actions)
wildcards = analyze_wildcard_policies(all_actions)
if escalations or wildcards:
findings.append({
"principal_type": "Role", "principal_name": role.get("RoleName"),
"arn": role.get("Arn"), "escalation_paths": escalations,
"wildcard_findings": wildcards,
})
return findings
def generate_report(findings, source):
"""Generate privilege escalation analysis report."""
severity_counts = defaultdict(int)
for f in findings:
for esc in f.get("escalation_paths", []):
severity_counts[esc["severity"]] += 1
for wc in f.get("wildcard_findings", []):
severity_counts[wc["severity"]] += 1
return {
"report_time": datetime.utcnow().isoformat(),
"source": source,
"total_principals_with_findings": len(findings),
"severity_summary": dict(severity_counts),
"findings": findings,
}
def main():
parser = argparse.ArgumentParser(description="AWS IAM Privilege Escalation Detector")
parser.add_argument("--profile", help="AWS CLI profile name")
parser.add_argument("--input-file", help="Pre-downloaded authorization details JSON")
parser.add_argument("--output", default="iam_escalation_report.json")
args = parser.parse_args()
if args.input_file:
with open(args.input_file) as f:
auth_details = json.load(f)
source = args.input_file
elif HAS_BOTO3:
auth_details = get_account_authorization(args.profile)
source = f"live-account (profile={args.profile or 'default'})"
else:
print("[!] boto3 not installed and no --input-file provided")
return
findings = analyze_account(auth_details)
report = generate_report(findings, source)
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"[+] Analyzed {len(auth_details.get('UserDetailList', []))} users, "
f"{len(auth_details.get('RoleDetailList', []))} roles")
print(f"[+] Found {len(findings)} principals with escalation paths")
print(f"[+] Report saved to {args.output}")
if __name__ == "__main__":
main()
Keep exploring