AWS IAM Policy Does Not Grant Any Administrative Access


Description

This policy validates that no IAM policies grant admin access. This should be combined with suppressions on the legitimate IAM admin policies in your account so that it only fires when new and unexpected policies granting admin access are created.

Query · python

import json

from policyuniverse.expander_minimizer import expand_policy
from policyuniverse.policy import Policy

# White listed policies (e.g. the approved admin policies) can be specified here, or as an
# exception to this policy.

ADMIN_ACTIONS = {
    "Permissions",
}


def policy(resource):
    iam_policy = Policy(expand_policy(json.loads(resource["PolicyDocument"])))
    action_summary = iam_policy.action_summary()

    # Sometimes AWS Service Linked Roles+Policies violate the
    # expectation as expressed in policyuniverse.
    # service-linked roles have a path of /aws-service-role/
    #   service-linked roles and their policies are not editable
    # service-role is editable, and so not included
    if resource.get("Path", "") == "/aws-service-role/":
        return True

    # Check if the policy grants any administrative privileges
    return not any(
        ADMIN_ACTIONS.intersection(action_summary[service]) for service in action_summary
    )

Analyst notes

Delete the unapproved IAM admin policy.

Raw source AWS IAM Policy Does Not Grant Any Administrative Access · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: policy
Filename: aws_iam_policy_does_not_grant_admin_access.py
PolicyID: "AWS.IAM.Policy.DoesNotGrantAdminAccess"
DisplayName: "AWS IAM Policy Does Not Grant Any Administrative Access"
Enabled: true
ResourceTypes:
  - AWS.IAM.Policy
Tags:
  - AWS
  - PCI
  - Security, Identity & Compliance
  - Privilege Escalation:Valid Accounts
Reports:
  PCI:
    - 2.2.4
    - 7.1.2
  MITRE ATT&CK:
    - TA0004:T1078
Severity: Medium
Description: >
  This policy validates that no IAM policies grant admin access. This should be combined with suppressions on the legitimate IAM admin policies in your account so that it only fires when new and unexpected policies granting admin access are created.
Runbook: Delete the unapproved IAM admin policy.
Reference: https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html
Tests:
  - Name: Policy Does Not Grant Administrative Privileges
    ExpectedResult: true
    Resource:
      {
        "AccountId": "123456789012",
        "Arn": "arn:aws:iam::123456789012:policy/ExamplePolicy",
        "AttachmentCount": 1,
        "DefaultVersionId": "v1",
        "Description": null,
        "Entities":
          {
            "PolicyGroups": null,
            "PolicyRoles": [{ "RoleId": "1111", "RoleName": "ExampleRole" }],
            "PolicyUsers": null,
          },
        "Id": "AAAAA",
        "IsAttachable": true,
        "Name": "ExamplePolicy",
        "Path": "/",
        "PermissionsBoundaryUsageCount": 0,
        "PolicyDocument": '{"Version":"2012-10-17","Statement":[{"Action":["kms:DescribeKey"],"Resource":"arn:aws:kms:us-west-2:123456789012:key/1","Effect":"Allow","Sid":"EncryptMessages"}]}',
        "Region": "global",
        "ResourceId": "arn:aws:iam::123456789012:policy/ExamplePolicy",
        "ResourceType": "AWS.IAM.Policy",
        "Tags": null,
        "TimeCreated": "2019-01-01T00:00:00.000Z",
        "UpdateDate": "2019-01-01T00:00:00Z",
      }
  - Name: Policy Does Grant Administrative Privileges
    ExpectedResult: false
    Resource:
      {
        "AccountId": "123456789012",
        "Arn": "arn:aws:iam::123456789012:policy/ExamplePolicy",
        "AttachmentCount": 1,
        "DefaultVersionId": "v1",
        "Description": null,
        "Entities":
          {
            "PolicyGroups": null,
            "PolicyRoles": [{ "RoleId": "1111", "RoleName": "ExampleRole" }],
            "PolicyUsers": null,
          },
        "Id": "AAAAA",
        "IsAttachable": true,
        "Name": "ExamplePolicy",
        "Path": "/",
        "PermissionsBoundaryUsageCount": 0,
        "PolicyDocument": '{"Version":"2012-10-17","Statement":[{"Action":["kms:PutKeyPolicy","kms:GenerateDataKey"],"Resource":"arn:aws:kms:us-west-2:123456789012:key/1","Effect":"Allow","Sid":"EncryptMessages"},{"Action":["sqs:SendMessage","sqs:SendMessageBatch"],"Resource":"arn:aws:sqs:us-west-2:123456789012:example-queue","Effect":"Allow","Sid":"SendSQSMessages"}]}',
        "Region": "global",
        "ResourceId": "arn:aws:iam::123456789012:policy/ExamplePolicy",
        "ResourceType": "AWS.IAM.Policy",
        "Tags": null,
        "TimeCreated": "2019-01-01T00:00:00.000Z",
        "UpdateDate": "2019-01-01T00:00:00Z",
      }
  - Name: AWS Service-Linked Policy Network Admin
    ExpectedResult: true
    Resource:
      {
        "AccountId": "123456789012",
        "Arn": "arn:aws:iam::123456789012:policy/aws-service-role/AmazonGuardDutyServiceRolePolicy",
        "AttachmentCount": 1,
        "DefaultVersionId": "v6",
        "Entities":
          {
            "PolicyGroups": null,
            "PolicyRoles":
              [
                {
                  "RoleId": "AROOOOOOOOOOOOOOOOOOO",
                  "RoleName": "AWSServiceRoleForAmazonGuardDuty",
                },
              ],
            "PolicyUsers": null,
          },
        "Id": "ANPPPPPPPPPPPPPPPPPPP",
        "IsAttachable": true,
        "Name": "AmazonGuardDutyServiceRolePolicy",
        "Path": "/aws-service-role/",
        "PermissionsBoundaryUsageCount": 0,
        "PolicyDocument": "{\n\t\"Version\": \"2012-10-17\",\n\t\"Statement\": [\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"ec2:DescribeInstances\",\n\t\t\t\t\"ec2:DescribeImages\",\n\t\t\t\t\"ec2:DescribeVpcEndpoints\",\n\t\t\t\t\"ec2:DescribeSubnets\",\n\t\t\t\t\"ec2:DescribeVpcPeeringConnections\",\n\t\t\t\t\"ec2:DescribeTransitGatewayAttachments\",\n\t\t\t\t\"organizations:ListAccounts\",\n\t\t\t\t\"organizations:DescribeAccount\",\n\t\t\t\t\"s3:GetBucketPublicAccessBlock\",\n\t\t\t\t\"s3:GetEncryptionConfiguration\",\n\t\t\t\t\"s3:GetBucketTagging\",\n\t\t\t\t\"s3:GetAccountPublicAccessBlock\",\n\t\t\t\t\"s3:ListAllMyBuckets\",\n\t\t\t\t\"s3:GetBucketAcl\",\n\t\t\t\t\"s3:GetBucketPolicy\",\n\t\t\t\t\"s3:GetBucketPolicyStatus\",\n\t\t\t\t\"lambda:GetFunctionConfiguration\",\n\t\t\t\t\"lambda:ListTags\",\n\t\t\t\t\"eks:ListClusters\",\n\t\t\t\t\"eks:DescribeCluster\",\n\t\t\t\t\"ec2:DescribeVpcEndpointServices\",\n\t\t\t\t\"ec2:DescribeSecurityGroups\"\n\t\t\t],\n\t\t\t\"Resource\": \"*\"\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": \"iam:CreateServiceLinkedRole\",\n\t\t\t\"Resource\": \"*\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringEquals\": {\n\t\t\t\t\t\"iam:AWSServiceName\": \"malware-protection.guardduty.amazonaws.com\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": \"ec2:CreateVpcEndpoint\",\n\t\t\t\"Resource\": \"arn:aws:ec2:*:*:vpc-endpoint/*\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"ForAnyValue:StringEquals\": {\n\t\t\t\t\t\"aws:TagKeys\": \"GuardDutyManaged\"\n\t\t\t\t},\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"ec2:VpceServiceName\": [\n\t\t\t\t\t\t\"com.amazonaws.*.guardduty-data\",\n\t\t\t\t\t\t\"com.amazonaws.*.guardduty-data-fips\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"ec2:ModifyVpcEndpoint\",\n\t\t\t\t\"ec2:DeleteVpcEndpoints\"\n\t\t\t],\n\t\t\t\"Resource\": \"arn:aws:ec2:*:*:vpc-endpoint/*\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"Null\": {\n\t\t\t\t\t\"aws:ResourceTag/GuardDutyManaged\": false\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"ec2:CreateVpcEndpoint\",\n\t\t\t\t\"ec2:ModifyVpcEndpoint\"\n\t\t\t],\n\t\t\t\"Resource\": [\n\t\t\t\t\"arn:aws:ec2:*:*:vpc/*\",\n\t\t\t\t\"arn:aws:ec2:*:*:security-group/*\",\n\t\t\t\t\"arn:aws:ec2:*:*:subnet/*\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": \"ec2:CreateTags\",\n\t\t\t\"Resource\": \"arn:aws:ec2:*:*:vpc-endpoint/*\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringEquals\": {\n\t\t\t\t\t\"ec2:CreateAction\": \"CreateVpcEndpoint\"\n\t\t\t\t},\n\t\t\t\t\"ForAnyValue:StringEquals\": {\n\t\t\t\t\t\"aws:TagKeys\": \"GuardDutyManaged\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"ec2:AuthorizeSecurityGroupIngress\",\n\t\t\t\t\"ec2:AuthorizeSecurityGroupEgress\",\n\t\t\t\t\"ec2:RevokeSecurityGroupIngress\",\n\t\t\t\t\"ec2:RevokeSecurityGroupEgress\",\n\t\t\t\t\"ec2:DeleteSecurityGroup\"\n\t\t\t],\n\t\t\t\"Resource\": \"arn:aws:ec2:*:*:security-group/*\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"Null\": {\n\t\t\t\t\t\"aws:ResourceTag/GuardDutyManaged\": false\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": \"ec2:CreateSecurityGroup\",\n\t\t\t\"Resource\": \"arn:aws:ec2:*:*:security-group/*\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"aws:RequestTag/GuardDutyManaged\": \"*\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": \"ec2:CreateSecurityGroup\",\n\t\t\t\"Resource\": \"arn:aws:ec2:*:*:vpc/*\"\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": \"ec2:CreateTags\",\n\t\t\t\"Resource\": \"arn:aws:ec2:*:*:security-group/*\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringEquals\": {\n\t\t\t\t\t\"ec2:CreateAction\": \"CreateSecurityGroup\"\n\t\t\t\t},\n\t\t\t\t\"ForAnyValue:StringEquals\": {\n\t\t\t\t\t\"aws:TagKeys\": \"GuardDutyManaged\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": \"eks:CreateAddon\",\n\t\t\t\"Resource\": \"arn:aws:eks:*:*:cluster/*\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"ForAnyValue:StringEquals\": {\n\t\t\t\t\t\"aws:TagKeys\": \"GuardDutyManaged\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"eks:DeleteAddon\",\n\t\t\t\t\"eks:UpdateAddon\",\n\t\t\t\t\"eks:DescribeAddon\"\n\t\t\t],\n\t\t\t\"Resource\": \"arn:aws:eks:*:*:addon/*/aws-guardduty-agent/*\"\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": \"eks:TagResource\",\n\t\t\t\"Resource\": \"arn:aws:eks:*:*:cluster/*\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"ForAnyValue:StringEquals\": {\n\t\t\t\t\t\"aws:TagKeys\": \"GuardDutyManaged\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t]\n}",
        "Region": "global",
        "ResourceId": "arn:aws:iam::123456789012:policy/aws-service-role/AmazonGuardDutyServiceRolePolicy",
        "ResourceType": "AWS.IAM.Policy",
        "TimeCreated": "2017-11-28T20:12:59Z",
        "UpdateDate": "2023-03-05T19:19:19Z",
      }
  - Name: AWS Service Linked Role Policy Admin
    ExpectedResult: true
    Resource:
      {
        "AccountId": "123456789012",
        "Arn": "arn:aws:iam::123456789012:policy/aws-service-role/AmazonElasticFileSystemServiceRolePolicy",
        "AttachmentCount": 1,
        "DefaultVersionId": "v4",
        "Entities":
          {
            "PolicyGroups": null,
            "PolicyRoles":
              [
                {
                  "RoleId": "AROOOOOOOOOOOOOOOOOOO",
                  "RoleName": "AWSServiceRoleForAmazonElasticFileSystem",
                },
              ],
            "PolicyUsers": null,
          },
        "Id": "ANPPPPPPPPPPPPPPPPPPP",
        "IsAttachable": true,
        "Name": "AmazonElasticFileSystemServiceRolePolicy",
        "Path": "/aws-service-role/",
        "PermissionsBoundaryUsageCount": 0,
        "PolicyDocument": "{\n\t\"Version\": \"2012-10-17\",\n\t\"Statement\": [\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"backup-storage:MountCapsule\",\n\t\t\t\t\"ec2:CreateNetworkInterface\",\n\t\t\t\t\"ec2:DeleteNetworkInterface\",\n\t\t\t\t\"ec2:DescribeSecurityGroups\",\n\t\t\t\t\"ec2:DescribeSubnets\",\n\t\t\t\t\"ec2:DescribeNetworkInterfaceAttribute\",\n\t\t\t\t\"ec2:ModifyNetworkInterfaceAttribute\",\n\t\t\t\t\"tag:GetResources\"\n\t\t\t],\n\t\t\t\"Resource\": \"*\"\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"kms:DescribeKey\"\n\t\t\t],\n\t\t\t\"Resource\": \"arn:aws:kms:*:*:key/*\"\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"backup:CreateBackupVault\",\n\t\t\t\t\"backup:PutBackupVaultAccessPolicy\"\n\t\t\t],\n\t\t\t\"Resource\": [\n\t\t\t\t\"arn:aws:backup:*:*:backup-vault:aws/efs/automatic-backup-vault\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"backup:CreateBackupPlan\",\n\t\t\t\t\"backup:CreateBackupSelection\"\n\t\t\t],\n\t\t\t\"Resource\": [\n\t\t\t\t\"arn:aws:backup:*:*:backup-plan:*\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"iam:CreateServiceLinkedRole\"\n\t\t\t],\n\t\t\t\"Resource\": \"*\",\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringEquals\": {\n\t\t\t\t\t\"iam:AWSServiceName\": [\n\t\t\t\t\t\t\"backup.amazonaws.com\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"iam:PassRole\"\n\t\t\t],\n\t\t\t\"Resource\": [\n\t\t\t\t\"arn:aws:iam::*:role/aws-service-role/backup.amazonaws.com/AWSServiceRoleForBackup\"\n\t\t\t],\n\t\t\t\"Condition\": {\n\t\t\t\t\"StringLike\": {\n\t\t\t\t\t\"iam:PassedToService\": \"backup.amazonaws.com\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"Effect\": \"Allow\",\n\t\t\t\"Action\": [\n\t\t\t\t\"elasticfilesystem:DescribeFileSystems\",\n\t\t\t\t\"elasticfilesystem:CreateReplicationConfiguration\",\n\t\t\t\t\"elasticfilesystem:DescribeReplicationConfigurations\",\n\t\t\t\t\"elasticfilesystem:DeleteReplicationConfiguration\"\n\t\t\t],\n\t\t\t\"Resource\": \"*\"\n\t\t}\n\t]\n}",
        "Region": "global",
        "ResourceId": "arn:aws:iam::123456789012:policy/aws-service-role/AmazonElasticFileSystemServiceRolePolicy",
        "ResourceType": "AWS.IAM.Policy",
        "TimeCreated": "2019-11-05T16:52:41Z",
        "UpdateDate": "2023-03-10T19:19:19Z",
      }


# ------ paired body: aws_iam_policy_does_not_grant_admin_access.py ------

import json

from policyuniverse.expander_minimizer import expand_policy
from policyuniverse.policy import Policy

# White listed policies (e.g. the approved admin policies) can be specified here, or as an
# exception to this policy.

ADMIN_ACTIONS = {
    "Permissions",
}


def policy(resource):
    iam_policy = Policy(expand_policy(json.loads(resource["PolicyDocument"])))
    action_summary = iam_policy.action_summary()

    # Sometimes AWS Service Linked Roles+Policies violate the
    # expectation as expressed in policyuniverse.
    # service-linked roles have a path of /aws-service-role/
    #   service-linked roles and their policies are not editable
    # service-role is editable, and so not included
    if resource.get("Path", "") == "/aws-service-role/":
        return True

    # Check if the policy grants any administrative privileges
    return not any(
        ADMIN_ACTIONS.intersection(action_summary[service]) for service in action_summary
    )

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.