Skip to content

Commit bc51321

Browse files
Add article: obfuscated admin IAM policies using action wildcards
Covers wildcard-based IAM policy obfuscation techniques that grant admin-equivalent access while evading name-based policy detections. Includes detection guidance and example policies. Fixes #419
1 parent c02f803 commit bc51321

1 file changed

Lines changed: 202 additions & 0 deletions

File tree

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
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

Comments
 (0)