|
| 1 | +--- |
| 2 | +author_name: Raajhesh Kannaa Chidambaram |
| 3 | +title: Obfuscated Admin IAM Policy |
| 4 | +description: Using IAM action wildcards to create policies that grant admin-equivalent access while evading name-based detections. |
| 5 | +--- |
| 6 | + |
| 7 | +# Obfuscated Admin IAM Policy |
| 8 | + |
| 9 | +<div class="grid cards" markdown> |
| 10 | +- :material-book:{ .lg .middle } __Additional Resources__ |
| 11 | + |
| 12 | + --- |
| 13 | + |
| 14 | + - [IAM JSON Policy Elements: Action](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_action.html) |
| 15 | + - [IAM Policy Simulator](https://policysim.aws.amazon.com/) |
| 16 | + - [Access Analyzer Policy Validation](https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-policy-validation.html) |
| 17 | +</div> |
| 18 | + |
| 19 | +When an attacker gains the ability to create or modify IAM policies (e.g., via `iam:CreatePolicy`, `iam:CreatePolicyVersion`, or `iam:PutUserPolicy`), the obvious move is to attach `AdministratorAccess` or create a policy with `"Action": "*"`. Both are trivially detected by security tooling that matches on known policy ARNs or exact string comparisons. |
| 20 | + |
| 21 | +A subtler approach is to use IAM's built-in wildcard matching to construct policies that are functionally equivalent to admin access but don't match simple detection patterns. |
| 22 | + |
| 23 | +## How IAM Wildcard Matching Works |
| 24 | + |
| 25 | +IAM supports two wildcard characters in the `Action` element of policy statements: |
| 26 | + |
| 27 | +- `*` matches any combination of characters (including none) |
| 28 | +- `?` matches any single character |
| 29 | + |
| 30 | +These are evaluated at request time by the IAM policy engine. This means `s3:Get*` matches `s3:GetObject`, `s3:GetBucketPolicy`, and every other `s3:Get` action. The `?` wildcard matches exactly one character, so `?am:*` matches both `iam:*` and `ram:*`. |
| 31 | + |
| 32 | +## Obfuscation Techniques |
| 33 | + |
| 34 | +### Technique 1: Service-Action Wildcard Split |
| 35 | + |
| 36 | +Instead of `"Action": "*"`, use `"Action": "*:*"`. This is functionally identical. The `*` before the colon matches any service prefix, and the `*` after the colon matches any action. Most pattern-matching detections look for the literal string `"*"` as a standalone action, not `"*:*"`. |
| 37 | + |
| 38 | +```json |
| 39 | +{ |
| 40 | + "Version": "2012-10-17", |
| 41 | + "Statement": [ |
| 42 | + { |
| 43 | + "Effect": "Allow", |
| 44 | + "Action": "*:*", |
| 45 | + "Resource": "*" |
| 46 | + } |
| 47 | + ] |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +### Technique 2: Single-Character Wildcards on Service Names |
| 52 | + |
| 53 | +The `?` wildcard lets you target specific services without spelling them out. This bypasses detections that look for exact service prefixes. |
| 54 | + |
| 55 | +```json |
| 56 | +{ |
| 57 | + "Version": "2012-10-17", |
| 58 | + "Statement": [ |
| 59 | + { |
| 60 | + "Effect": "Allow", |
| 61 | + "Action": [ |
| 62 | + "?am:*", |
| 63 | + "s?s:*", |
| 64 | + "?t?:*", |
| 65 | + "??2:*", |
| 66 | + "?3:*", |
| 67 | + "???bda:*", |
| 68 | + "???s:*" |
| 69 | + ], |
| 70 | + "Resource": "*" |
| 71 | + } |
| 72 | + ] |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +Breakdown of what these match: |
| 77 | + |
| 78 | +| Pattern | Matches | |
| 79 | +|---------|---------| |
| 80 | +| `?am:*` | `iam:*`, `ram:*` | |
| 81 | +| `s?s:*` | `sqs:*`, `sns:*`, `sms:*` | |
| 82 | +| `?t?:*` | `sts:*`, `sts:*` | |
| 83 | +| `??2:*` | `ec2:*`, `ss2:*` | |
| 84 | +| `?3:*` | `s3:*` | |
| 85 | +| `???bda:*` | `lambda:*` | |
| 86 | +| `???s:*` | `logs:*`, `ecs:*`, `eks:*`, `sqs:*`, `sns:*`, `kms:*` | |
| 87 | + |
| 88 | +This approach is noisier (it may match unintended services) but that is a feature, not a bug, from an attacker's perspective. More permissions make it harder to pinpoint the intent. |
| 89 | + |
| 90 | +### Technique 3: Partial Action Wildcards |
| 91 | + |
| 92 | +Rather than granting `*` on entire services, you can use wildcards within action names to cover critical operations while looking innocuous. |
| 93 | + |
| 94 | +```json |
| 95 | +{ |
| 96 | + "Version": "2012-10-17", |
| 97 | + "Statement": [ |
| 98 | + { |
| 99 | + "Effect": "Allow", |
| 100 | + "Action": [ |
| 101 | + "iam:Creat*", |
| 102 | + "iam:Attac*", |
| 103 | + "iam:Put*", |
| 104 | + "iam:Pass*", |
| 105 | + "iam:Delet*", |
| 106 | + "iam:Updat*", |
| 107 | + "iam:List*", |
| 108 | + "iam:Get*", |
| 109 | + "sts:As*", |
| 110 | + "s3:*bject*", |
| 111 | + "ec2:Run*", |
| 112 | + "ec2:Describe*", |
| 113 | + "lambda:Creat*", |
| 114 | + "lambda:Invok*", |
| 115 | + "lambda:Updat*" |
| 116 | + ], |
| 117 | + "Resource": "*" |
| 118 | + } |
| 119 | + ] |
| 120 | +} |
| 121 | +``` |
| 122 | + |
| 123 | +This covers the most impactful IAM, STS, S3, EC2, and Lambda operations without using a single bare `*` action. Detections that only flag `"Action": "*"` or `AdministratorAccess` miss this entirely. |
| 124 | + |
| 125 | +### Technique 4: Multiple Statements with Broad Wildcards |
| 126 | + |
| 127 | +Split the policy across multiple statements. Some tools only evaluate individual statements rather than the aggregate effect of the full policy. |
| 128 | + |
| 129 | +```json |
| 130 | +{ |
| 131 | + "Version": "2012-10-17", |
| 132 | + "Statement": [ |
| 133 | + { |
| 134 | + "Sid": "ReadOnly", |
| 135 | + "Effect": "Allow", |
| 136 | + "Action": [ |
| 137 | + "*:Get*", |
| 138 | + "*:List*", |
| 139 | + "*:Describe*" |
| 140 | + ], |
| 141 | + "Resource": "*" |
| 142 | + }, |
| 143 | + { |
| 144 | + "Sid": "Operations", |
| 145 | + "Effect": "Allow", |
| 146 | + "Action": [ |
| 147 | + "*:Create*", |
| 148 | + "*:Delete*", |
| 149 | + "*:Update*", |
| 150 | + "*:Put*", |
| 151 | + "*:Attach*", |
| 152 | + "*:Detach*" |
| 153 | + ], |
| 154 | + "Resource": "*" |
| 155 | + }, |
| 156 | + { |
| 157 | + "Sid": "Invoke", |
| 158 | + "Effect": "Allow", |
| 159 | + "Action": [ |
| 160 | + "*:Run*", |
| 161 | + "*:Start*", |
| 162 | + "*:Stop*", |
| 163 | + "*:Invoke*", |
| 164 | + "*:Execute*" |
| 165 | + ], |
| 166 | + "Resource": "*" |
| 167 | + } |
| 168 | + ] |
| 169 | +} |
| 170 | +``` |
| 171 | + |
| 172 | +Each statement looks like it grants a specific category of operations. Together, they cover nearly all AWS actions across all services. |
| 173 | + |
| 174 | +## Combining with Inline Policies |
| 175 | + |
| 176 | +Managed policies (including `AdministratorAccess`) show up in `iam:ListAttachedUserPolicies` and `iam:ListAttachedRolePolicies`. Inline policies are stored differently and require `iam:GetUserPolicy` or `iam:GetRolePolicy` to retrieve. Placing the obfuscated policy as an inline policy on a user or role adds another layer of evasion. |
| 177 | + |
| 178 | +```bash |
| 179 | +aws iam put-user-policy \ |
| 180 | + --user-name target-user \ |
| 181 | + --policy-name AmazonPersonalizeReadOnly \ |
| 182 | + --policy-document file://obfuscated-admin.json |
| 183 | +``` |
| 184 | + |
| 185 | +Note the deliberately misleading policy name. Inline policy names are freeform strings with no validation against their actual contents. |
| 186 | + |
| 187 | +## Detection Guidance |
| 188 | + |
| 189 | +Detecting these patterns requires going beyond simple string matching: |
| 190 | + |
| 191 | +1. **Use IAM Access Analyzer Policy Validation.** The [`ValidatePolicy`](https://docs.aws.amazon.com/access-analyzer/latest/APIReference/API_ValidatePolicy.html) API flags overly permissive policies, including those using wildcards. Run this against all policies periodically. |
| 192 | + |
| 193 | +2. **Expand wildcards before evaluation.** Tools like [Parliament](https://github.com/duo-labs/parliament) and [AWS IAM Access Analyzer](https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html) can resolve wildcard patterns against the full list of known AWS actions and determine the effective permission set. |
| 194 | + |
| 195 | +3. **Monitor policy creation and modification events in CloudTrail.** Key events to watch: |
| 196 | + - `CreatePolicy` / `CreatePolicyVersion` |
| 197 | + - `PutUserPolicy` / `PutRolePolicy` / `PutGroupPolicy` |
| 198 | + - Look for `?` or `*:*` patterns in the policy document within the CloudTrail event. |
| 199 | + |
| 200 | +4. **Flag any policy granting `Resource: "*"` with broad action patterns.** A policy that uses wildcards in the service prefix portion of the action (e.g., `*:*`, `?am:*`) should be treated as high severity regardless of the action specifics. |
| 201 | + |
| 202 | +5. **Compare effective permissions.** Use `iam:SimulatePrincipalPolicy` to test what a principal can actually do, rather than relying on policy document parsing alone. |
0 commit comments