> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Role Permissions That Allow Privilege Escalation

### More Info:

Identifies permissions held by roles that can be chained to gain more access than the role was granted, for example attaching a policy, publishing a new policy version, or passing a more privileged role to a service the role can invoke. Any workload or user that can assume the role inherits the escalation path, so the roles real reach is wider than its attached policies suggest. Remove the escalating action, or scope it to the specific roles and policies the workload is meant to manage.

### Risk Level

High

### Address

Secuirty

### Compliance Standards

* AWS Startup Security Baseline
* AWS Well Architected Framework
* CIS AWS
* HIPAA
* HITRUST CSF
* ISO 27001
* NIST CSF
* Reserve Bank of India (RBI) Cyber Security Framework
* Reserve Bank of India (RBI) Master Direction – Information Technology Framework
* SOC2

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        Below is a practical, console-focused process to find and fix IAM role permissions that allow privilege escalation in AWS.

        ***

        ## 1. Understand What You’re Looking For

        Privilege escalation usually happens when a principal (user/role) can:

        * Attach/Update IAM policies to themselves or to roles they can assume:
          * `iam:AttachUserPolicy`, `iam:AttachRolePolicy`, `iam:PutUserPolicy`, `iam:PutRolePolicy`, `iam:CreatePolicyVersion`, `iam:SetDefaultPolicyVersion`, etc.
        * Assume or pass roles with more privileges:
          * `sts:AssumeRole`
          * `iam:PassRole` (combined with other actions)
        * Create or modify access keys / login profiles:
          * `iam:CreateAccessKey`, `iam:UpdateAccessKey`, `iam:CreateLoginProfile`, `iam:UpdateLoginProfile`
        * Create/Update Lambda, EC2, or other services with privileged roles:
          * `lambda:UpdateFunctionCode`, `lambda:CreateFunction`, `ec2:RunInstances` with a powerful instance profile, etc.

        Your goal: Remove or tightly restrict these permissions to least privilege.

        ***

        ## 2. Inventory IAM Roles and Attached Policies

        1. Sign in to the AWS Management Console with an admin account.
        2. Go to **IAM**:
           * Top right search bar → type “IAM” → **IAM**.
        3. In the left navigation, check:
           * **Roles**
           * **Users**
           * **Policies**

        You’ll review permissions on:

        * Managed policies (AWS-managed & Customer-managed).
        * Inline policies on users/roles/groups.

        ***

        ## 3. Identify High-Risk Policies Using IAM Access Analyzer

        1. In IAM console, left menu → **Access Analyzer** → **Policy analyzer**.
        2. Use **Policy analyzer** to search for policies containing sensitive actions:
           * In **Actions** filter, enter:
             * `iam:*`, `sts:*`, `lambda:*`, `ec2:RunInstances`, etc.
        3. For each policy result, open it in a new tab and inspect:
           * Look for statements with `Effect: Allow` and:
             * `Action: "iam:*"` or broad wildcards like `"*"` on **Action**.
             * `Resource: "*"`.
        4. Prioritize:
           * Policies with `Action` = `"*"` and `Resource` = `"*"`.
           * Policies that allow specific escalation actions (list in section 1).

        ***

        ## 4. Analyze a Specific Role for Escalation Path

        To deep dive into one role:

        1. IAM console → **Roles**.
        2. Click a **role name** to inspect it.
        3. Tabs to check:
           * **Permissions**:
             * Look at each **Attached policy** and any **Inline policies**.
             * Click policy name → **JSON** tab.
           * **Trust relationships**:
             * Check which principals can assume this role (`sts:AssumeRole`).

        For each attached/inline policy, look for:

        * Problematic *Actions*:

          Common privilege-escalation enablers:

          * `iam:AttachUserPolicy`
          * `iam:AttachGroupPolicy`
          * `iam:AttachRolePolicy`
          * `iam:PutUserPolicy`
          * `iam:PutGroupPolicy`
          * `iam:PutRolePolicy`
          * `iam:CreatePolicyVersion`
          * `iam:SetDefaultPolicyVersion`
          * `iam:PassRole`
          * `sts:AssumeRole`
          * `iam:CreateAccessKey`
          * `iam:UpdateAccessKey`
          * `iam:CreateLoginProfile`
          * `iam:UpdateLoginProfile`

        * Broad *Resources*:
          * `Resource: "*"` on the above actions.
          * Or resources that cover admin roles/policies.

        If a role a user has can assume another highly privileged role (via `sts:AssumeRole`), that is a privilege escalation path.

        ***

        ## 5. Remediate Policies (Least-Privilege Hardening)

        ### 5.1 Remove or Restrict Dangerous IAM Actions

        For each risky policy:

        1. IAM → **Policies** → search for the policy name.

        2. Open policy → **Permissions** tab → **Edit**.

        3. In **JSON** or **Visual editor**, do the following:

           a) Remove unneeded IAM-write actions completely\
           For example, remove:

           * `iam:Attach*`
           * `iam:Put*`
           * `iam:CreatePolicyVersion`
           * `iam:SetDefaultPolicyVersion`
           * `iam:CreateAccessKey`
           * `iam:UpdateAccessKey`
           * `iam:CreateLoginProfile`
           * `iam:UpdateLoginProfile`

           b) Where `iam:PassRole` is required:

           * Restrict **Resource** to specific safe roles, not `"*"`.
           * Add conditions like:
             ```json theme={null}
             "Condition": {
               "StringEquals": {
                 "iam:PassedToService": [
                   "ecs.amazonaws.com",
                   "lambda.amazonaws.com"
                 ]
               }
             }
             ```

           c) Where `sts:AssumeRole` is required:

           * Restrict to only the specific role ARNs needed.
           * Do not allow `"*"` for resources.

        4. Click **Next**, review, then **Save changes**.

        If an AWS-managed policy is risky (you can’t edit it):

        * Detach it from users/roles and replace with a custom, scoped-down customer-managed policy:
          1. **Policies** → **Create policy**.
          2. Define least-privilege permissions.
          3. Create policy, then **Attach** it to the target role/user.
          4. Detach the AWS-managed policy from the entity.

        ### 5.2 Tighten Trust Policies (Who Can Assume the Role)

        1. IAM → **Roles** → choose role → **Trust relationships** tab.

        2. Click **Edit trust policy**.

        3. Check for:
           * `"Principal": "*"` or overly broad accounts.
           * Conditions missing on `sts:AssumeRole`.

        4. Adjust to only allow required principals and, ideally, require conditions. Example (only one account and specific role):

        ```json theme={null}
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": { "AWS": "arn:aws:iam::123456789012:role/AppRole" },
              "Action": "sts:AssumeRole"
            }
          ]
        }
        ```

        Avoid letting low-privilege roles, wildcards, or external accounts assume highly privileged roles.

        ***

        ## 6. Use the IAM Policy Simulator to Validate

        1. Go to **IAM → Policy Simulator**.
        2. Choose **User** or **Role** you want to test.
        3. Select services like **IAM**, **STS**, **Lambda**, **EC2**.
        4. Test specific actions:
           * `iam:PassRole`, `iam:AttachRolePolicy`, `sts:AssumeRole`, `iam:CreateAccessKey`, etc.
        5. Confirm:
           * Only the minimal, intended actions are allowed.
           * Escalation-enabling combinations are not possible.

        ***

        ## 7. Add Guardrails: SCPs and Permissions Boundaries (If Using Organizations)

        If you use AWS Organizations:

        1. **Service Control Policies (SCPs)**:

           * AWS Organizations console → **Policies → Service control policies**.
           * Create SCPs that *deny* dangerous actions for all non-admin accounts, e.g.:

           ```json theme={null}
           {
             "Version": "2012-10-17",
             "Statement": [
               {
                 "Effect": "Deny",
                 "Action": [
                   "iam:CreatePolicyVersion",
                   "iam:SetDefaultPolicyVersion",
                   "iam:AttachRolePolicy",
                   "iam:PassRole"
                 ],
                 "Resource": "*"
               }
             ]
           }
           ```

           * Attach SCP to appropriate OUs/accounts.
           * Exclude a tightly-controlled admin account if necessary.

        2. **Permissions boundaries** (optional, more advanced):
           * Create a boundary policy that *excludes* privilege-escalation actions.
           * Attach as permission boundary to roles developers can create.

        ***

        ## 8. Continuous Monitoring

        1. IAM console → **Access Analyzer → Analyzer**:
           * Enable analyzers to detect broad or external access.
        2. **IAM → Credential report**:
           * Periodically review user access keys, console access.
        3. Enable **CloudTrail** and (optionally) **CloudTrail Lake / GuardDuty**:
           * Monitor for suspicious IAM activity (e.g., new `CreatePolicyVersion`, `SetDefaultPolicyVersion`, `AttachRolePolicy`).

        ***

        If you share a specific risky role/policy JSON, I can give exact edits line-by-line for remediation.
      </Accordion>

      <Accordion title="Using CLI">
        Below is a practical, CLI‑focused process to identify and fix IAM role permissions that allow privilege escalation in AWS.

        ***

        ## 1. Know the risky IAM permissions

        These actions are commonly used for privilege escalation when overly broad:

        * `iam:PassRole`
        * `iam:PutRolePolicy`
        * `iam:AttachRolePolicy`
        * `iam:PutUserPolicy`
        * `iam:CreatePolicyVersion`
        * `iam:SetDefaultPolicyVersion`
        * `iam:UpdateAssumeRolePolicy`
        * `iam:AddUserToGroup`, `iam:AttachGroupPolicy`
        * `sts:AssumeRole`
        * `sts:AssumeRoleWithWebIdentity`

        Your goal: **find any role that can call these actions in a dangerous way** (e.g., unscoped to specific resources, or to Admin policies/roles).

        ***

        ## 2. Enumerate IAM roles and their policies

        ```bash theme={null}
        # List all roles
        aws iam list-roles --query 'Roles[*].RoleName' --output text
        ```

        For each role (or for ones you suspect):

        ```bash theme={null}
        ROLE_NAME=YourRoleName

        # Inline policies on the role
        aws iam list-role-policies --role-name "$ROLE_NAME"

        # Get contents of each inline policy
        aws iam get-role-policy \
          --role-name "$ROLE_NAME" \
          --policy-name YourInlinePolicyName

        # Attached managed policies
        aws iam list-attached-role-policies --role-name "$ROLE_NAME"

        # For each attached policy, get its default policy document
        POLICY_ARN=arn:aws:iam::aws:policy/YourPolicy
        DEFAULT_VERSION=$(aws iam get-policy \
          --policy-arn "$POLICY_ARN" \
          --query 'Policy.DefaultVersionId' \
          --output text)

        aws iam get-policy-version \
          --policy-arn "$POLICY_ARN" \
          --version-id "$DEFAULT_VERSION"
        ```

        Look in the policy JSON for `Action` containing the risky IAM actions above.

        ***

        ## 3. Identify privilege‑escalation patterns

        Flags to look for in the policy JSON:

        1. **Wildcard actions**:
           * `"Action": "iam:*"` or `"*"` on IAM / STS
        2. **Unrestricted `PassRole`**:
           * `"Action": "iam:PassRole"` with `"Resource": "*"`
        3. **Policy modification on high‑privilege policies**:
           * `iam:AttachRolePolicy`, `iam:PutRolePolicy`, `iam:CreatePolicyVersion`, `iam:SetDefaultPolicyVersion` with `"Resource": "*"`
        4. **Trust policies that are too broad**:
           * `sts:AssumeRole` allowed from `Principal: "*"`, or from untrusted accounts/services.

        To view a role’s trust policy:

        ```bash theme={null}
        aws iam get-role --role-name "$ROLE_NAME" \
          --query 'Role.AssumeRolePolicyDocument' \
          --output json
        ```

        ***

        ## 4. Remove or scope dangerous permissions (inline policies)

        ### 4.1. Download and edit the inline policy locally

        ```bash theme={null}
        ROLE_NAME=YourRoleName
        POLICY_NAME=YourInlinePolicyName

        aws iam get-role-policy \
          --role-name "$ROLE_NAME" \
          --policy-name "$POLICY_NAME" \
          --query 'PolicyDocument' \
          --output json > policy.json
        ```

        Edit `policy.json`:

        * Remove unneeded risky actions (e.g., `iam:*`, `iam:PassRole`, etc.).
        * Or **narrow** them:
          * Restrict `Resource` to only specific ARNs (e.g., just one role that truly needs to be passed).
          * Add `Condition` (e.g., limit `iam:PassRole` to certain services).

        Example: scope `iam:PassRole` to a single role:

        ```json theme={null}
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Sid": "AllowPassSpecificRoleToEC2",
              "Effect": "Allow",
              "Action": "iam:PassRole",
              "Resource": "arn:aws:iam::123456789012:role/EC2LimitedRole",
              "Condition": {
                "StringEquals": {
                  "iam:PassedToService": "ec2.amazonaws.com"
                }
              }
            }
          ]
        }
        ```

        Re‑apply the edited inline policy:

        ```bash theme={null}
        aws iam put-role-policy \
          --role-name "$ROLE_NAME" \
          --policy-name "$POLICY_NAME" \
          --policy-document file://policy.json
        ```

        ***

        ## 5. Replace or detach risky managed policies

        If a role has broad AWS‑managed or customer‑managed policies (e.g., `AdministratorAccess`, custom `*:*` policies):

        ```bash theme={null}
        ROLE_NAME=YourRoleName
        POLICY_ARN=arn:aws:iam::aws:policy/AdministratorAccess

        # Detach the overly permissive policy
        aws iam detach-role-policy \
          --role-name "$ROLE_NAME" \
          --policy-arn "$POLICY_ARN"
        ```

        Create a **least‑privilege** customer‑managed policy:

        ```bash theme={null}
        cat > least-priv-role-policy.json << 'EOF'
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Sid": "AppNeedsOnlyTheseActions",
              "Effect": "Allow",
              "Action": [
                "s3:GetObject",
                "s3:PutObject"
              ],
              "Resource": "arn:aws:s3:::my-bucket/*"
            }
          ]
        }
        EOF

        aws iam create-policy \
          --policy-name MyLeastPrivPolicy \
          --policy-document file://least-priv-role-policy.json

        NEW_POLICY_ARN=$(aws iam list-policies \
          --scope Local \
          --query "Policies[?PolicyName=='MyLeastPrivPolicy'].Arn" \
          --output text)

        aws iam attach-role-policy \
          --role-name "$ROLE_NAME" \
          --policy-arn "$NEW_POLICY_ARN"
        ```

        ***

        ## 6. Lock down trust policies (AssumeRole)

        For any role that can lead to higher privilege (e.g., admin roles):

        1. Restrict who can assume it.
        2. Avoid `Principal: "*"`.
        3. Limit to specific AWS accounts, roles, or services.

        Get and edit trust policy:

        ```bash theme={null}
        ROLE_NAME=PrivilegedRole

        aws iam get-role --role-name "$ROLE_NAME" \
          --query 'Role.AssumeRolePolicyDocument' \
          --output json > trust.json
        ```

        Example safer trust policy (from a specific account/role only):

        ```json theme={null}
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "AWS": "arn:aws:iam::111122223333:role/ApprovedCallerRole"
              },
              "Action": "sts:AssumeRole",
              "Condition": {
                "Bool": {
                  "aws:MultiFactorAuthPresent": "true"
                }
              }
            }
          ]
        }
        ```

        Apply:

        ```bash theme={null}
        aws iam update-assume-role-policy \
          --role-name "$ROLE_NAME" \
          --policy-document file://trust.json
        ```

        ***

        ## 7. Fix dangerous policy versioning rights

        If a role can call `iam:CreatePolicyVersion` or `iam:SetDefaultPolicyVersion` on sensitive policies, an attacker can swap in an admin version.

        For customer‑managed policies:

        1. List who can modify them.
        2. Remove those actions, or scope them with conditions (e.g., only to non‑privileged policies, or only a CI/CD role).

        To reduce rights, edit the responsible role’s policy as in step 4 and remove:

        * `iam:CreatePolicyVersion`
        * `iam:SetDefaultPolicyVersion`
        * `iam:DeletePolicyVersion`

        Then re‑apply with `put-role-policy`.

        ***

        ## 8. Validate with the Policy Simulator

        Before and after changes, you can check what a role can do:

        ```bash theme={null}
        ROLE_ARN=arn:aws:iam::123456789012:role/YourRole

        aws iam simulate-principal-policy \
          --policy-source-arn "$ROLE_ARN" \
          --action-names iam:PassRole iam:CreatePolicyVersion iam:AttachRolePolicy sts:AssumeRole \
          --output json
        ```

        Verify that high‑risk actions are `explicitDeny` or not allowed.

        ***

        ## 9. Optional: enforce org‑wide via SCPs (if using AWS Organizations)

        At the org/root or OU level, create SCPs that deny dangerous privilege‑escalation patterns except for tightly controlled admin roles:

        Example SCP snippet:

        ```json theme={null}
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Sid": "DenyPassRoleToAnyRoleExceptAdminControllers",
              "Effect": "Deny",
              "Action": "iam:PassRole",
              "Resource": "*",
              "Condition": {
                "StringNotLike": {
                  "aws:PrincipalArn": [
                    "arn:aws:iam::*:role/OrgAdminRole"
                  ]
                }
              }
            }
          ]
        }
        ```

        Apply with:

        ```bash theme={null}
        aws organizations create-policy \
          --content file://scp.json \
          --name RestrictPassRole \
          --type SERVICE_CONTROL_POLICY
        # then attach to OU or root
        ```

        ***

        If you want, you can paste one specific role’s current policy JSON, and I can show you exactly how to rewrite it to remove the privilege‑escalation paths and give you the exact `aws cli` commands to apply the fix.
      </Accordion>

      <Accordion title="Using Python">
        Below is a practical, step‑by‑step remediation approach for AWS IAM privilege‑escalation risks, with Python (boto3) examples.

        ***

        ## 1. Understand the Common IAM Privilege Escalation Paths

        Typical dangerous permissions (especially if a user/principal can call them on themselves or roles they assume):

        * `iam:CreatePolicyVersion` (with `SetAsDefault=true`)
        * `iam:PutRolePolicy`, `iam:PutUserPolicy`, `iam:AttachRolePolicy`, `iam:AttachUserPolicy`, `iam:AttachGroupPolicy`
        * `iam:PassRole` (especially combined with EC2, Lambda, Step Functions, etc.)
        * `sts:AssumeRole`
        * Permissions on Lambda, EC2, Glue, Step Functions, etc., that allow attaching or modifying roles/policies:
          * `lambda:UpdateFunctionConfiguration` (to change execution role)
          * `ec2:RunInstances` with instance profile you can control
          * `glue:CreateDevEndpoint` with role you can pass
        * Wildcards like `"Action": "*"`, `"Resource": "*"` (especially in combination).

        Remediation = remove or restrict these where they are not explicitly needed, and add guardrails (permission boundaries, SCPs, condition keys).

        ***

        ## 2. Enumerate IAM Roles and Policies (Python/boto3)

        ```python theme={null}
        import boto3
        import json

        iam = boto3.client('iam')

        def list_all_roles():
            roles = []
            paginator = iam.get_paginator('list_roles')
            for page in paginator.paginate():
                roles.extend(page['Roles'])
            return roles

        def list_attached_policies_for_role(role_name):
            attached = []
            paginator = iam.get_paginator('list_attached_role_policies')
            for page in paginator.paginate(RoleName=role_name):
                attached.extend(page['AttachedPolicies'])
            return attached

        def list_inline_policies_for_role(role_name):
            policies = []
            paginator = iam.get_paginator('list_role_policies')
            for page in paginator.paginate(RoleName=role_name):
                policies.extend(page['PolicyNames'])
            return policies
        ```

        ***

        ## 3. Identify High‑Risk Permissions in Policies

        Define a set of “risky” actions and scan all attached + inline policies.

        ```python theme={null}
        RISKY_ACTIONS = {
            "iam:CreatePolicyVersion",
            "iam:PutRolePolicy",
            "iam:PutUserPolicy",
            "iam:AttachRolePolicy",
            "iam:AttachUserPolicy",
            "iam:AttachGroupPolicy",
            "iam:PassRole",
            "iam:PutRolePermissionsBoundary",
            "iam:PutUserPermissionsBoundary",
            "sts:AssumeRole",
            "*",  # catch-all, will treat carefully
        }

        def get_policy_document_arn(policy_arn, version_id=None):
            if not version_id:
                policy = iam.get_policy(PolicyArn=policy_arn)
                version_id = policy['Policy']['DefaultVersionId']
            version = iam.get_policy_version(PolicyArn=policy_arn, VersionId=version_id)
            return version['PolicyVersion']['Document']

        def get_inline_policy_document(role_name, policy_name):
            resp = iam.get_role_policy(RoleName=role_name, PolicyName=policy_name)
            return resp['PolicyDocument']

        def statement_contains_risky_actions(statement):
            actions = statement.get("Action", [])
            if isinstance(actions, str):
                actions = [actions]
            actions = [a.lower() for a in actions]
            risky = []
            for a in actions:
                if a == "*" or a in {x.lower() for x in RISKY_ACTIONS}:
                    risky.append(a)
            return risky

        def find_risky_perms_in_policy(document):
            risky_statements = []
            statements = document.get("Statement", [])
            if isinstance(statements, dict):
                statements = [statements]
            for stmt in statements:
                risky = statement_contains_risky_actions(stmt)
                if risky:
                    risky_statements.append((stmt, risky))
            return risky_statements
        ```

        Usage: loop through roles and attached policies:

        ```python theme={null}
        def audit_roles_for_priv_esc():
            roles = list_all_roles()
            findings = []

            for role in roles:
                role_name = role['RoleName']

                # Managed policies
                attached = list_attached_policies_for_role(role_name)
                for ap in attached:
                    doc = get_policy_document_arn(ap['PolicyArn'])
                    risky_stmts = find_risky_perms_in_policy(doc)
                    if risky_stmts:
                        findings.append({
                            "PrincipalType": "Role",
                            "PrincipalName": role_name,
                            "PolicyType": "Managed",
                            "PolicyArn": ap['PolicyArn'],
                            "RiskyStatements": risky_stmts,
                        })

                # Inline policies
                inline_names = list_inline_policies_for_role(role_name)
                for pname in inline_names:
                    doc = get_inline_policy_document(role_name, pname)
                    risky_stmts = find_risky_perms_in_policy(doc)
                    if risky_stmts:
                        findings.append({
                            "PrincipalType": "Role",
                            "PrincipalName": role_name,
                            "PolicyType": "Inline",
                            "PolicyName": pname,
                            "RiskyStatements": risky_stmts,
                        })

            return findings

        if __name__ == "__main__":
            findings = audit_roles_for_priv_esc()
            print(json.dumps(findings, default=str, indent=2))
        ```

        This identifies where privilege‑escalation‑prone actions exist.

        ***

        ## 4. Decide on Remediation Strategy (Per Finding)

        Per role/policy combination, you should:

        1. **Confirm business need** for each risky action.
        2. If **not needed**:
           * Remove or narrow the statement.
        3. If **needed**:
           * Restrict resources.
           * Add IAM conditions.
           * Use permission boundaries and/or SCPs.
           * Separate “break‑glass” roles from normal operators.

        Below are concrete policy changes and how to apply them via Python.

        ***

        ## 5. Remediate Excessive `iam:PassRole`

        ### 5.1 Policy Before (too broad)

        ```json theme={null}
        {
          "Effect": "Allow",
          "Action": "iam:PassRole",
          "Resource": "*"
        }
        ```

        ### 5.2 Policy After (restricted roles + conditions)

        ```json theme={null}
        {
          "Effect": "Allow",
          "Action": "iam:PassRole",
          "Resource": [
            "arn:aws:iam::123456789012:role/SpecificServiceRole"
          ],
          "Condition": {
            "StringEquals": {
              "iam:PassedToService": "lambda.amazonaws.com"
            }
          }
        }
        ```

        ### 5.3 Update Policy Version via Python

        For **managed policies**:

        ```python theme={null}
        def create_restricted_policy_version(policy_arn, new_document):
            resp = iam.create_policy_version(
                PolicyArn=policy_arn,
                PolicyDocument=json.dumps(new_document),
                SetAsDefault=True
            )
            return resp['PolicyVersion']['VersionId']
        ```

        Typical flow:

        1. Get existing document.
        2. Manually or programmatically modify statements (e.g., change `Resource: "*"` to specific ARNs; add `Condition`).
        3. Call `create_policy_version` with `SetAsDefault=True`.
        4. Optionally delete old versions later (max 5).

        ***

        ## 6. Remove or Restrict Delegated Policy Management (`iam:*Policy*`)

        If a role has permissions like `iam:CreatePolicyVersion`, `iam:PutRolePolicy`, etc., and it’s not a dedicated IAM admin role:

        1. **Create a new, least‑privilege IAM admin role** for identity team only.
        2. Remove those actions from operational roles.

        Sample adjustment:

        **Before:**

        ```json theme={null}
        {
          "Effect": "Allow",
          "Action": [
            "iam:CreatePolicyVersion",
            "iam:PutRolePolicy",
            "iam:AttachRolePolicy"
          ],
          "Resource": "*"
        }
        ```

        **After (if minimal is needed):**

        If the role only needs to attach a **pre‑approved** policy to a **specific role**:

        ```json theme={null}
        {
          "Effect": "Allow",
          "Action": [
            "iam:AttachRolePolicy"
          ],
          "Resource": "arn:aws:iam::123456789012:role/SpecificRole",
          "Condition": {
            "StringEquals": {
              "iam:PolicyARN": "arn:aws:iam::123456789012:policy/PreApprovedPolicy"
            }
          }
        }
        ```

        Apply via same `create_policy_version` mechanism.

        For **inline policies**, update with `put_role_policy`:

        ```python theme={null}
        def update_inline_policy(role_name, policy_name, new_document):
            iam.put_role_policy(
                RoleName=role_name,
                PolicyName=policy_name,
                PolicyDocument=json.dumps(new_document)
            )
        ```

        ***

        ## 7. Constrain `sts:AssumeRole` and Trust Policies

        Even if role A doesn’t have broad `iam:*`, it can escalate by assuming a more‑privileged role B.

        ### 7.1 Inspect trust policies

        ```python theme={null}
        def get_role_trust_policy(role_name):
            role = iam.get_role(RoleName=role_name)
            return role['Role']['AssumeRolePolicyDocument']

        def update_role_trust_policy(role_name, new_trust_doc):
            iam.update_assume_role_policy(
                RoleName=role_name,
                PolicyDocument=json.dumps(new_trust_doc)
            )
        ```

        ### 7.2 Restrict trust policies

        * Use specific principals, not `*`.
        * Add conditions like `aws:PrincipalTag`, `aws:SourceAccount`, `aws:SourceArn`.

        **Before (too open):**

        ```json theme={null}
        {
          "Effect": "Allow",
          "Principal": { "AWS": "*" },
          "Action": "sts:AssumeRole"
        }
        ```

        **After (restricted):**

        ```json theme={null}
        {
          "Effect": "Allow",
          "Principal": { "AWS": "arn:aws:iam::123456789012:role/SpecificCallerRole" },
          "Action": "sts:AssumeRole"
        }
        ```

        Or with conditions:

        ```json theme={null}
        {
          "Effect": "Allow",
          "Principal": { "AWS": "*" },
          "Action": "sts:AssumeRole",
          "Condition": {
            "StringEquals": { "aws:PrincipalTag/RoleType": "Automation" }
          }
        }
        ```

        ***

        ## 8. Guardrails: Permission Boundaries and SCPs (Org‑Level)

        ### 8.1 Permission Boundaries (per principal)

        Create a boundary policy that **forbids** escalation actions (or limits them tightly), then attach it as a permission boundary to roles/users that you do not want to be able to escalate.

        Example permission boundary:

        ```json theme={null}
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Deny",
              "Action": [
                "iam:CreatePolicyVersion",
                "iam:PutUserPolicy",
                "iam:PutRolePolicy",
                "iam:AttachUserPolicy",
                "iam:AttachRolePolicy",
                "iam:PassRole"
              ],
              "Resource": "*"
            }
          ]
        }
        ```

        Attach via Python:

        ```python theme={null}
        def attach_permission_boundary_to_role(role_name, boundary_arn):
            iam.update_role(
                RoleName=role_name,
                PermissionsBoundary=boundary_arn
            )
        ```

        ### 8.2 Service Control Policies (SCPs) (Org‑wide)

        In AWS Organizations, SCPs can deny dangerous actions account‑wide except for a specific IAM admin role.

        Example (pseudo; must be applied via Organizations API/console):

        ```json theme={null}
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Deny",
              "Action": [
                "iam:CreatePolicyVersion",
                "iam:PutRolePolicy",
                "iam:AttachRolePolicy",
                "iam:PassRole"
              ],
              "Resource": "*",
              "Condition": {
                "StringNotEquals": {
                  "aws:PrincipalArn": "arn:aws:iam::123456789012:role/CentralIAMAdmin"
                }
              }
            }
          ]
        }
        ```

        ***

        ## 9. Special Case: Service‑Based Escalation (Lambda, EC2, etc.)

        Even if `iam:PassRole` is restricted, some combos can still escalate; for each:

        * Lambda: `lambda:UpdateFunctionConfiguration` to change `Role`.
        * EC2: `ec2:RunInstances` with a modifiable instance profile.
        * Step Functions: `states:CreateStateMachine` with high‑priv role.

        **Remediation pattern:**

        * Constrain `Resource` to specific functions, instances, state machines.
        * Combine with **tight** `iam:PassRole` (only low‑priv roles).
        * Use conditions like `iam:PassedToService`, `ec2:InstanceProfile`, or tags.

        Example for Lambda:

        ```json theme={null}
        {
          "Effect": "Allow",
          "Action": [
            "lambda:UpdateFunctionConfiguration"
          ],
          "Resource": "arn:aws:lambda:us-east-1:123456789012:function:MyApp-*",
          "Condition": {
            "StringEquals": {
              "lambda:Role": "arn:aws:iam::123456789012:role/MyAppExecutionRole"
            }
          }
        }
        ```

        ***

        ## 10. Verification / Regression Checks

        After changes:

        1. **Use IAM Access Analyzer** (console or API) to detect potential privilege escalation.
        2. Re‑run your Python audit script to ensure:
           * No unexpected `iam:*Policy*` / `iam:PassRole` / `sts:AssumeRole` wildcards.
           * No `Action: "*"` + `Resource: "*"` for non‑admin roles.
        3. Test critical workflows to confirm business operations continue to function.

        ***

        If you want, I can:

        * Help you adapt the audit script to automatically suggest a “patched” policy document, or
        * Focus on a specific privileged role’s policies and walk through concrete JSON diffs.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        # IAM policy with privilege‑escalation actions removed or tightly scoped
        resource "aws_iam_policy" "restricted_role_management" {
          name        = "restricted-role-management"
          description = "Allows only tightly scoped role management actions without privilege escalation"

          # Replace with the ARN of the specific role this workload is allowed to pass
          # e.g. arn:aws:iam::123456789012:role/my-allowed-target-role
          # Substitute YOUR_ACCOUNT_ID and YOUR_ROLE_NAME as needed.
          policy = jsonencode({
            Version = "2012-10-17"
            Statement = [
              # Example: allow passing only a specific role, and only to a specific service
              {
                Sid    = "AllowPassSpecificRoleToSpecificService"
                Effect = "Allow"
                Action = [
                  "iam:PassRole"
                ]
                Resource = "arn:aws:iam::YOUR_ACCOUNT_ID:role/YOUR_ALLOWED_TARGET_ROLE"
                Condition = {
                  StringEquals = {
                    "iam:PassedToService" = "ecs-tasks.amazonaws.com" # SUBSTITUTE TARGET SERVICE
                  }
                }
              },

              # Example: allow listing/reading IAM metadata, which is not escalating
              {
                Sid    = "AllowIamReadOnly"
                Effect = "Allow"
                Action = [
                  "iam:GetRole",
                  "iam:ListRolePolicies",
                  "iam:ListAttachedRolePolicies",
                  "iam:ListRoles"
                ]
                Resource = "*"
              }

              # NOTE:
              #  - DO NOT include broad write actions like:
              #    iam:AttachRolePolicy
              #    iam:PutRolePolicy
              #    iam:CreatePolicy
              #    iam:CreatePolicyVersion
              #    iam:SetDefaultPolicyVersion
              #    iam:UpdateAssumeRolePolicy
              #    iam:AddUserToGroup
              #    iam:UpdateLoginProfile
              #    sts:AssumeRole on higher-privilege roles
              #  - If such actions are currently in the JSON, remove them or scope them
              #    to the exact roles/policies this workload must manage, with explicit ARNs.
            ]
          })
        }

        # Attach the remediated policy to the role that previously had an escalating policy
        resource "aws_iam_role_policy_attachment" "role_restricted_attachment" {
          role       = aws_iam_role.TARGET_ROLE.name             # SUBSTITUTE EXISTING ROLE
          policy_arn = aws_iam_policy.restricted_role_management.arn
        }

        # Example role showing how it would be referenced (if not already defined)
        resource "aws_iam_role" "TARGET_ROLE" {
          name = "YOUR_EXISTING_ROLE_NAME" # SUBSTITUTE; align with actual role name

          assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
        }

        data "aws_iam_policy_document" "assume_role_policy" {
          statement {
            effect  = "Allow"
            actions = ["sts:AssumeRole"]

            principals {
              type        = "Service"
              identifiers = ["ecs-tasks.amazonaws.com"] # SUBSTITUTE CALLER SERVICE
            }
          }
        }
        ```

        Changing an existing `aws_iam_policy` in Terraform (rather than editing inline JSON only) forces replacement of the policy resource; any existing attachments will be recreated by Terraform, which may briefly remove and reattach the policy during `apply`.

        For verification, `terraform plan` should show: removal of the old IAM policy resource (with privilege‑escalation actions), creation of the new restricted `aws_iam_policy`, and updates to `aws_iam_role_policy_attachment` resources so that the role is attached only to the remediated policy.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
