Article by Raajhesh Kannaa Chidambaram
Obfuscated Admin IAM Policy¶
-
Additional Resources
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.
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.
How IAM Wildcard Matching Works¶
IAM supports two wildcard characters in the Action element of policy statements:
*matches any combination of characters (including none)?matches any single character
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:*.
Obfuscation Techniques¶
Technique 1: Service-Action Wildcard Split¶
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 "*:*".
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*:*",
"Resource": "*"
}
]
}
Technique 2: Single-Character Wildcards on Service Names¶
The ? wildcard lets you target specific services without spelling them out. This bypasses detections that look for exact service prefixes.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"?am:*",
"s?s:*",
"?t?:*",
"??2:*",
"?3:*",
"???bda:*",
"???s:*"
],
"Resource": "*"
}
]
}
Breakdown of what these match:
| Pattern | Matches |
|---|---|
?am:* | iam:*, ram:* |
s?s:* | sqs:*, sns:*, sms:* |
?t?:* | sts:*, sts:* |
??2:* | ec2:*, ss2:* |
?3:* | s3:* |
???bda:* | lambda:* |
???s:* | logs:*, ecs:*, eks:*, sqs:*, sns:*, kms:* |
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.
Technique 3: Partial Action Wildcards¶
Rather than granting * on entire services, you can use wildcards within action names to cover critical operations while looking innocuous.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:Creat*",
"iam:Attac*",
"iam:Put*",
"iam:Pass*",
"iam:Delet*",
"iam:Updat*",
"iam:List*",
"iam:Get*",
"sts:As*",
"s3:*bject*",
"ec2:Run*",
"ec2:Describe*",
"lambda:Creat*",
"lambda:Invok*",
"lambda:Updat*"
],
"Resource": "*"
}
]
}
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.
Technique 4: Multiple Statements with Broad Wildcards¶
Split the policy across multiple statements. Some tools only evaluate individual statements rather than the aggregate effect of the full policy.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadOnly",
"Effect": "Allow",
"Action": [
"*:Get*",
"*:List*",
"*:Describe*"
],
"Resource": "*"
},
{
"Sid": "Operations",
"Effect": "Allow",
"Action": [
"*:Create*",
"*:Delete*",
"*:Update*",
"*:Put*",
"*:Attach*",
"*:Detach*"
],
"Resource": "*"
},
{
"Sid": "Invoke",
"Effect": "Allow",
"Action": [
"*:Run*",
"*:Start*",
"*:Stop*",
"*:Invoke*",
"*:Execute*"
],
"Resource": "*"
}
]
}
Each statement looks like it grants a specific category of operations. Together, they cover nearly all AWS actions across all services.
Combining with Inline Policies¶
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.
aws iam put-user-policy \
--user-name target-user \
--policy-name AmazonPersonalizeReadOnly \
--policy-document file://obfuscated-admin.json
Note the deliberately misleading policy name. Inline policy names are freeform strings with no validation against their actual contents.
Detection Guidance¶
Detecting these patterns requires going beyond simple string matching:
-
Use IAM Access Analyzer Policy Validation. The
ValidatePolicyAPI flags overly permissive policies, including those using wildcards. Run this against all policies periodically. -
Expand wildcards before evaluation. Tools like Parliament and AWS IAM Access Analyzer can resolve wildcard patterns against the full list of known AWS actions and determine the effective permission set.
-
Monitor policy creation and modification events in CloudTrail. Key events to watch:
CreatePolicy/CreatePolicyVersionPutUserPolicy/PutRolePolicy/PutGroupPolicy- Look for
?or*:*patterns in the policy document within the CloudTrail event.
-
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. -
Compare effective permissions. Use
iam:SimulatePrincipalPolicyto test what a principal can actually do, rather than relying on policy document parsing alone.