VNX-TF-004: Terraform IAM policy with wildcard Action (*)
Detect IAM policy statements in Terraform that allow every action (Action "*" in jsonencode, heredoc or one-line escaped JSON policies, actions = ["*"] in aws_iam_policy_document or an Azure custom role), skipping statements whose own Effect is Deny.
| Field | Value |
|---|---|
| Rule ID | VNX-TF-004 |
| Severity | High |
| SARIF level | error |
| Kind | iac |
| Languages | Terraform |
| CWE | CWE-269 |
| CAPEC | CAPEC-122 |
| ATT&CK | T1078 |
| CWSS vector | CWSS:1.0/TI:H/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H |
| Tags | iam least-privilege aws terraform |
| Source | internal/sast/rules/vnx-tf-004.rego |
Overview
This rule flags Terraform .tf files where a policy statement allows "*" as its action: Action = "*" or a list containing "*" in a jsonencode() policy, "Action": "*" or ["*"] (also over several lines) in a heredoc JSON policy, actions = ["*"] in a data "aws_iam_policy_document" statement, or actions = ["*"] in the permissions of an azurerm_role_definition. The effect is read from the statement itself, so a Deny statement (for example a region or MFA guard) is not reported even when an Allow statement sits beside it. Service-scoped wildcards such as "s3:*" are not reported.
Least-privilege IAM policies are the foundational access control mechanism in AWS. When a policy grants Action = "*", the associated role, user, or group can perform any operation on any resource in the account that is not blocked by a Service Control Policy (SCP), including creating new IAM users, modifying security group rules, exfiltrating data from S3, or deleting production resources.
This rule corresponds to CWE-269: Improper Privilege Management.
Why This Matters
Overly permissive IAM policies are one of the most common misconfigurations in AWS environments and a primary vector for privilege escalation. An attacker who compromises a workload running with an overly permissive role, through application-level vulnerabilities, container escape, SSRF, or credential theft, inherits all the permissions of that role.
A wildcard Action = "*" policy effectively grants administrative access. Even if the resource scope is limited (e.g., Resource = "arn:aws:s3:::my-bucket/*"), combined with a broad resource or Resource = "*", it grants full administrative control over the account. This means a compromised Lambda function, EC2 instance, or ECS task can create new IAM users with console access, modify CloudTrail settings to disable audit logging, exfiltrate secrets from Secrets Manager, and pivot to any other AWS service.
The risk extends beyond direct exploitation: over-permissive roles make the blast radius of any compromise much larger and make incident response much harder, since you cannot determine what an attacker did or did not access.
What Gets Flagged
resource "aws_iam_policy" "admin" {
policy = jsonencode({
Statement = [{
Effect = "Allow"
Action = "*" # FLAGGED
Resource = "*"
}]
})
}
data "aws_iam_policy_document" "admin" {
statement {
actions = ["*"] # FLAGGED: effect defaults to Allow
resources = ["*"]
}
}
Not flagged: a statement with effect = "Deny" and actions = ["*"], or Action = ["logs:CreateLogStream", "logs:PutLogEvents"].
Remediation
- Enumerate only the specific actions required by the workload. Review what the application actually does and grant only those actions. Use AWS IAM Access Analyzer to identify the minimum permissions from CloudTrail logs:
# SAFE: minimal permissions for a specific task
resource "aws_iam_role_policy" "app_policy" {
role = aws_iam_role.app.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["s3:GetObject", "s3:PutObject"]
Resource = "arn:aws:s3:::${var.bucket_name}/*"
},
{
Effect = "Allow"
Action = ["sqs:ReceiveMessage", "sqs:DeleteMessage"]
Resource = aws_sqs_queue.work_queue.arn
}
]
})
}
-
Use AWS managed policies with defined scopes as a starting point, then restrict further. Prefer service-specific managed policies (e.g.,
AmazonS3ReadOnlyAccess) overAdministratorAccess. -
Use IAM Access Analyzer to generate least-privilege policies from actual usage. This automatically produces a policy containing only the actions that were observed in CloudTrail logs during a review period.
References
- CWE-269: Improper Privilege Management
- AWS IAM – Grant least privilege
- AWS IAM Access Analyzer – Policy generation
- CIS AWS Foundations Benchmark – IAM controls
- OWASP Infrastructure as Code Security Cheat Sheet
- Terraform aws_iam_policy_document documentation
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-tf-004
// nosec vnx-tf-004
Or record an ignore rule: vulnetix ignore add --rule vnx-tf-004 --reason "...". See Suppressing findings.