#!/usr/bin/env python3
"""
AWS Privilege Escalation Assessment Agent — AUTHORIZED TESTING ONLY
Assesses AWS IAM configurations for privilege escalation paths using boto3
and enumerates dangerous policy combinations.

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

import json
from datetime import datetime, timezone

import boto3
from botocore.exceptions import ClientError


def get_caller_identity() -> dict:
    """Get current AWS identity information."""
    sts = boto3.client("sts")
    identity = sts.get_caller_identity()
    return {
        "account": identity["Account"],
        "arn": identity["Arn"],
        "user_id": identity["UserId"],
    }


def enumerate_iam_users() -> list[dict]:
    """Enumerate all IAM users and their attached policies."""
    iam = boto3.client("iam")
    users = []
    paginator = iam.get_paginator("list_users")

    for page in paginator.paginate():
        for user in page["Users"]:
            username = user["UserName"]
            attached = iam.list_attached_user_policies(UserName=username)
            inline = iam.list_user_policies(UserName=username)
            groups = iam.list_groups_for_user(UserName=username)

            users.append({
                "username": username,
                "arn": user["Arn"],
                "attached_policies": [p["PolicyArn"] for p in attached["AttachedPolicies"]],
                "inline_policies": inline["PolicyNames"],
                "groups": [g["GroupName"] for g in groups["Groups"]],
                "has_console": user.get("PasswordLastUsed") is not None,
            })

    return users


def enumerate_iam_roles() -> list[dict]:
    """Enumerate IAM roles and their trust policies."""
    iam = boto3.client("iam")
    roles = []
    paginator = iam.get_paginator("list_roles")

    for page in paginator.paginate():
        for role in page["Roles"]:
            trust = role.get("AssumeRolePolicyDocument", {})
            attached = iam.list_attached_role_policies(RoleName=role["RoleName"])

            roles.append({
                "role_name": role["RoleName"],
                "arn": role["Arn"],
                "trust_policy": trust,
                "attached_policies": [p["PolicyArn"] for p in attached["AttachedPolicies"]],
            })

    return roles


def check_dangerous_permissions(users: list[dict]) -> list[dict]:
    """Identify users with dangerous permission combinations for privilege escalation."""
    iam = boto3.client("iam")
    escalation_paths = []

    dangerous_actions = [
        "iam:CreatePolicyVersion", "iam:SetDefaultPolicyVersion",
        "iam:PassRole", "iam:CreateRole", "iam:AttachUserPolicy",
        "iam:AttachRolePolicy", "iam:PutUserPolicy", "iam:PutRolePolicy",
        "iam:AddUserToGroup", "iam:UpdateAssumeRolePolicy",
        "sts:AssumeRole", "lambda:CreateFunction", "lambda:InvokeFunction",
        "lambda:UpdateFunctionCode", "ec2:RunInstances",
        "cloudformation:CreateStack", "glue:CreateDevEndpoint",
        "datapipeline:CreatePipeline", "ssm:SendCommand",
    ]

    for user in users:
        user_dangerous = []
        for policy_arn in user["attached_policies"]:
            try:
                policy = iam.get_policy(PolicyArn=policy_arn)
                version_id = policy["Policy"]["DefaultVersionId"]
                version = iam.get_policy_version(PolicyArn=policy_arn, VersionId=version_id)
                statements = version["PolicyVersion"]["Document"].get("Statement", [])

                for stmt in statements:
                    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({
                                "policy": policy_arn,
                                "action": action,
                                "resource": stmt.get("Resource", "*"),
                            })
            except ClientError:
                continue

        if user_dangerous:
            escalation_paths.append({
                "username": user["username"],
                "dangerous_permissions": user_dangerous,
                "escalation_risk": "HIGH" if any(
                    d["action"] == "*" for d in user_dangerous
                ) else "MEDIUM",
            })

    return escalation_paths


def check_role_chaining(roles: list[dict], account_id: str) -> list[dict]:
    """Identify role chaining opportunities for privilege escalation."""
    chains = []

    for role in roles:
        trust = role.get("trust_policy", {})
        for statement in trust.get("Statement", []):
            if statement.get("Effect") != "Allow":
                continue
            principal = statement.get("Principal", {})
            aws_principal = principal.get("AWS", [])
            if isinstance(aws_principal, str):
                aws_principal = [aws_principal]

            for p in aws_principal:
                if p == f"arn:aws:iam::{account_id}:root" or p == "*":
                    chains.append({
                        "role": role["role_name"],
                        "trust_principal": p,
                        "risk": "HIGH" if p == "*" else "MEDIUM",
                        "policies": role["attached_policies"],
                    })

    return chains


def generate_report(identity: dict, users: list, escalation: list, chains: list) -> str:
    """Generate privilege escalation assessment report."""
    lines = [
        "AWS PRIVILEGE ESCALATION ASSESSMENT — AUTHORIZED TESTING ONLY",
        "=" * 65,
        f"Account: {identity['account']}",
        f"Assessed As: {identity['arn']}",
        f"Date: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}",
        "",
        f"IAM Users Enumerated: {len(users)}",
        f"Escalation Paths Found: {len(escalation)}",
        f"Role Chaining Risks: {len(chains)}",
        "",
        "PRIVILEGE ESCALATION PATHS:",
        "-" * 40,
    ]
    for path in escalation:
        lines.append(f"  [{path['escalation_risk']}] {path['username']}")
        for perm in path["dangerous_permissions"][:5]:
            lines.append(f"    - {perm['action']} on {perm['resource']}")

    if chains:
        lines.extend(["", "ROLE CHAINING RISKS:", "-" * 40])
        for chain in chains:
            lines.append(f"  [{chain['risk']}] {chain['role']} trusts {chain['trust_principal']}")

    return "\n".join(lines)


if __name__ == "__main__":
    print("[!] AWS PRIVILEGE ESCALATION ASSESSMENT — AUTHORIZED TESTING ONLY\n")

    identity = get_caller_identity()
    print(f"[*] Account: {identity['account']}, Identity: {identity['arn']}")

    users = enumerate_iam_users()
    print(f"[*] Enumerated {len(users)} IAM users")

    roles = enumerate_iam_roles()
    print(f"[*] Enumerated {len(roles)} IAM roles")

    escalation = check_dangerous_permissions(users)
    chains = check_role_chaining(roles, identity["account"])

    report = generate_report(identity, users, escalation, chains)
    print(report)

    output = f"aws_privesc_{identity['account']}_{datetime.now(timezone.utc).strftime('%Y%m%d')}.json"
    with open(output, "w") as f:
        json.dump({"identity": identity, "users": users, "escalation_paths": escalation,
                    "role_chains": chains}, f, indent=2, default=str)
    print(f"\n[*] Results saved to {output}")
