py iam expand

Sign up for Prowler Updates

Please enable JavaScript in your browser to complete this form.
Andoni Alonso headshot
Andoni Alonso // August 21, 2025

Unmasking Hidden Dangers: How Prowler Now Detects Obfuscated IAM Policies

It all started with a fascinating blog post from the team at Permiso introducing their “Sky Scalpel” tool. Their research highlighted a clever technique for hiding dangerous permissions within AWS IAM policies. This got me thinking about the different ways policies can be manipulated to evade detection.

A few weeks later, after I had just joined the Prowler team, I saw a Bluesky post from Victor Grenu that brought the topic back to the forefront. Was Prowler prepared for these kinds of evasions? It sounded like the perfect project to start my new journey.

Did you know you can obfuscate AWS IAM Policies to bypass CSPM security rules and detection? Evasions can take different forms, such as Unicode encoding, wildcards, random casing, insignificant whitespace, and reordering How did you prevent such techniques in your organization?

— Victor Grenu (@zoph.me) March 6, 2025 at 4:59 PM

The Deceptive Policy: A Wolf in Sheep’s Clothing

To understand the threat, let’s look at a policy meant to grant full administrative access to AWS KMS. This is a critical service and IAM policies should follow the least privilege principle when allowing access.

A standard, explicit policy for this is easy to spot:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "kmsFullAdmin",
      "Effect": "Allow",
      "Action": "kms:*",
      "Resource": "*"
    }
  ]
}

An automated scanner can easily flag kms:* as overly permissive.

But what if we write the policy like this?

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "notThePolicyYouAreLookingFor",
      "Effect": "\u0041llow",
      "Action": "kms:**",
      "Resource": "*"
    }
  ]
}

This version uses two simple tricks:

  • \u0041llow is the unicode representation of Allow
  • kms:** is functionally identical to kms:*.

For python, unicode is not a problem:

"\u0041llow" == "Allow"
True

But if you try to detect admin access by matching kms:*, this won’t be detected:

"kms:*" == "kms:**"

False

The same way it won’t match if you list every kms action possible, but then our example would be more than 60 lines long…

There are other tricks we could use: adding whitespaces, random casing, other wildcards (?) or even playing with Allow/Deny or Action/NotAction statements.

My favorite trick is how an invalid action statement can grant accidental (or not-so-accidental) administrator access:

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "invalidAction",
			"Effect": "Allow",
			"NotAction": "s3:prowlerAction",
			"Resource": "*"
		}
	]
}

Since s3:prowlerAction is not a real AWS action, AWS ignores it. The NotAction statement effectively becomes empty, meaning it denies nothing. The result is a policy that allows all actions on all resources.

And yes, you guessed right. If we’re writing this, it’s because Prowler was vulnerable to these kinds of obfuscation techniques.

The Fix: Normalizing IAM Policies with py-iam-expand

Seeing this, I knew we had to enhance Prowler’s IAM analysis capabilities. The solution wasn’t to play whack-a-mole with every new pattern, but to normalize the policy into a canonical form before evaluation.

To tackle this we developed py-iam-expand, a small Python library and CLI designed specifically to expand and normalize IAM actions. This tool resolves wildcards and other variations into a definitive list of effective permissions.

For example, py-iam-expand takes the obfuscated policy and expands the actions, leaving no room for ambiguity:

$ cat kms.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "kms:**"
        }
    ]
}

$ py-iam-expand < kms.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "kms:CancelKeyDeletion",
        "kms:ConnectCustomKeyStore",
        "kms:CreateAlias",
        "kms:CreateCustomKeyStore",
        "kms:CreateGrant",
        "kms:CreateKey",
        "kms:Decrypt",
	 [...]
        "kms:VerifyMac"
      ]
    }
  ]
}

Because both kms:* and kms:** expand to the exact same list of permissions, our check can now reliably determine that this policy grants full KMS access.

We also released it as a CLI for quick manual auditing during security assessments. No pattern can escape expansion:

$ py-iam-expand "iam:*ss?ole"
iam:PassRole

$ py-iam-expand "sts:?S????????"
sts:AssumeRole
sts:AssumeRoot

The Result: A More Resilient Prowler

With py-iam-expand integrated, Prowler’s checks are no longer fooled by these tricks. We updated our checks to use this normalization engine first.

Now, since version 5.8, when Prowler scans the deceptive policy, it correctly identifies the risk and generates a FAIL finding, showing the true administrative privilege being granted.

You can see here our previous KMS check example, iam_policy_no_full_access_to_kms, detected. This and the other 57 AWS IAM checks benefit from this improvement. You can see all of them in Prowler Hub.

Conclusion

This journey is a perfect example of the cat-and-mouse game that is cloud security. As attackers devise new evasion techniques, our tools must evolve to meet them. By normalizing IAM actions before we evaluate them, we’ve made Prowler more resilient and our users more secure.

You can check the py-iam-expand repo here, and as always, we welcome your feedback and contributions. 

Stay secure 🤘

Recent Articles

bedrock header
August 12, 2025

Bedrock’s New API Keys: Convenience at a Hidden Security Cost

Recently, the AWS team rolled out the red carpet for a slick new feature in their post, "Accelerate AI development with Amazon Bedrock API keys." The promise was a dream...

June 24, 2025

CSPM for GCP: Securing Your Google Cloud Environment with Modern Cloud Security Posture Management

Modern organizations rapidly embrace the Google Cloud Platform for its scalability, innovation capabilities, and cost-effectiveness.  However, this digital transformation comes with a critical challenge: maintaining robust security across increasingly complex...

Screenshot at
June 4, 2025

Prowler’s State of Cloud Security Report 2025

https://youtu.be/S8nLoxgrc5o Rajiv Taori and Laura Franzese walk through the findings of the 2025 State of Cloud Security Report We've been in this industry long enough to know that survey data...