Back to Checks
CloudTrail logs show no potential enumeration activity
cloudtrail_threat_detection_enumeration
Service: cloudtrailSeverity: critical
OfficialBuilt-inVerified
Categories:threat-detection
Resource Type:AwsCloudTrailTrail
CloudTrail activity is analyzed for AWS identities executing a broad mix of discovery APIs like List*, Describe*, and Get* within a recent time window.
An identity exceeding a configurable ratio of these actions indicates potential enumeration behavior by that principal.
Risk
Concentrated discovery activity signals reconnaissance with valid credentials. Adversaries can map assets and policies to enable privilege escalation, target data stores for exfiltration (confidentiality), and identify services to disrupt (availability), supporting stealthy lateral movement.
Run this check with Prowler
Run in Prowler CloudRun
prowler aws --checks cloudtrail_threat_detection_enumerationRecommendation
Apply least privilege to limit List*/Describe*/Get* to necessary resources and roles; use separation of duties.
- Enforce MFA and short-lived sessions
- Use SCPs to curb unnecessary discovery
- Baseline expected reads and alert on spikes as defense in depth
Remediation
CLI
aws iam update-access-key --user-name <USER_NAME> --access-key-id <ACCESS_KEY_ID> --status InactiveTerraform
# Deny common enumeration APIs for a specific IAM user
resource "aws_iam_user_policy" "<example_resource_name>" {
name = "deny-enumeration"
user = "<example_user_name>"
policy = jsonencode({
Version = "2012-10-17",
Statement = [{
Effect = "Deny", # CRITICAL: blocks typical enumeration calls
Action = [
"ec2:Describe*", # CRITICAL
"iam:List*", # CRITICAL
"s3:List*", # CRITICAL
"s3:Get*" # CRITICAL
],
Resource = "*"
}]
})
}NativeIaC
# CloudFormation: deny common enumeration APIs for a specific IAM user
Resources:
DenyEnumerationPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: deny-enumeration
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Deny # CRITICAL: blocks typical enumeration calls
Action:
- ec2:Describe* # CRITICAL: deny EC2 describe APIs
- iam:List* # CRITICAL: deny IAM list APIs
- s3:List* # CRITICAL: deny S3 list APIs
- s3:Get* # CRITICAL: deny S3 get APIs (e.g., GetBucketAcl)
Resource: "*"
Users:
- "<example_resource_name>" # CRITICAL: target the enumerating userOther
1In AWS Console, go to IAM > Users and open the user shown in the alert (ARN in the finding)
2Select the Security credentials tab
3For each active Access key, click Deactivate to set status to Inactive
4If the activity came from an EC2 instance role: go to EC2 > Instances > select the instance > Security > IAM role > Detach IAM role
5Re-run the check to confirm no new enumeration events occur
Check Code
import json
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
cloudtrail_client,
)
default_threat_detection_enumeration_actions = [
"CreateIndex",
"DescribeAccessEntry",
"DescribeAccountAttributes",
"DescribeAvailabilityZones",
"DescribeBundleTasks",
"DescribeCarrierGateways",
"DescribeClientVpnRoutes",
"DescribeCluster",
"DescribeDhcpOptions",
"DescribeFlowLogs",
"DescribeImages",
"DescribeInstanceAttribute",
"DescribeInstanceInformation",
"DescribeInstanceTypes",
"DescribeInstances",
"DescribeInstances",
"DescribeKeyPairs",
"DescribeLogGroups",
"DescribeLogStreams",
"DescribeOrganization",
"DescribeRegions",
"DescribeSecurityGroups",
"DescribeSnapshotAttribute",
"DescribeSnapshotTierStatus",
"DescribeSubscriptionFilters",
"DescribeTransitGatewayMulticastDomains",
"DescribeVolumes",
"DescribeVolumesModifications",
"DescribeVpcEndpointConnectionNotifications",
"DescribeVpcs",
"GetAccount",
"GetAccountAuthorizationDetails",
"GetAccountSendingEnabled",
"GetBucketAcl",
"GetBucketLogging",
"GetBucketPolicy",
"GetBucketReplication",
"GetBucketVersioning",
"GetCallerIdentity",
"GetCertificate",
"GetConsoleScreenshot",
"GetCostAndUsage",
"GetDetector",
"GetEbsDefaultKmsKeyId",
"GetEbsEncryptionByDefault",
"GetFindings",
"GetFlowLogsIntegrationTemplate",
"GetIdentityVerificationAttributes",
"GetInstances",
"GetIntrospectionSchema",
"GetLaunchTemplateData",
"GetLaunchTemplateData",
"GetLogRecord",
"GetParameters",
"GetPolicyVersion",
"GetPublicAccessBlock",
"GetQueryResults",
"GetRegions",
"GetSMSAttributes",
"GetSMSSandboxAccountStatus",
"GetSendQuota",
"GetTransitGatewayRouteTableAssociations",
"GetUserPolicy",
"HeadObject",
"ListAccessKeys",
"ListAccounts",
"ListAllMyBuckets",
"ListAssociatedAccessPolicies",
"ListAttachedUserPolicies",
"ListClusters",
"ListDetectors",
"ListDomains",
"ListFindings",
"ListHostedZones",
"ListIPSets",
"ListIdentities",
"ListInstanceProfiles",
"ListObjects",
"ListOrganizationalUnitsForParent",
"ListOriginationNumbers",
"ListPolicyVersions",
"ListResources",
"ListRoles",
"ListRoles",
"ListRules",
"ListServiceQuotas",
"ListSubscriptions",
"ListTargetsByRule",
"ListTopics",
"ListUsers",
"LookupEvents",
"Search",
]
class cloudtrail_threat_detection_enumeration(Check):
def execute(self):
findings = []
threshold = cloudtrail_client.audit_config.get(
"threat_detection_enumeration_threshold", 0.3
)
threat_detection_minutes = cloudtrail_client.audit_config.get(
"threat_detection_enumeration_minutes", 1440
)
enumeration_actions = cloudtrail_client.audit_config.get(
"threat_detection_enumeration_actions",
default_threat_detection_enumeration_actions,
)
potential_enumeration = {}
found_potential_enumeration = False
multiregion_trail = None
# Check if any trail is multi-region so we only need to check once
for trail in cloudtrail_client.trails.values():
if trail.is_multiregion:
multiregion_trail = trail
break
trails_to_scan = (
cloudtrail_client.trails.values()
if not multiregion_trail
else [multiregion_trail]
)
for trail in trails_to_scan:
for event_name in enumeration_actions:
for event_log in cloudtrail_client._lookup_events(
trail=trail,
event_name=event_name,
minutes=threat_detection_minutes,
):
event_log = json.loads(event_log["CloudTrailEvent"])
if (
"arn" in event_log["userIdentity"]
): # Ignore event logs without ARN since they are AWS services
if (
event_log["userIdentity"]["arn"],
event_log["userIdentity"]["type"],
) not in potential_enumeration:
potential_enumeration[
(
event_log["userIdentity"]["arn"],
event_log["userIdentity"]["type"],
)
] = set()
potential_enumeration[
(
event_log["userIdentity"]["arn"],
event_log["userIdentity"]["type"],
)
].add(event_name)
for aws_identity, actions in potential_enumeration.items():
identity_threshold = round(len(actions) / len(enumeration_actions), 2)
aws_identity_type = aws_identity[1]
aws_identity_arn = aws_identity[0]
if len(actions) / len(enumeration_actions) > threshold:
found_potential_enumeration = True
report = Check_Report_AWS(
metadata=self.metadata(), resource=cloudtrail_client.trails
)
report.region = cloudtrail_client.region
report.resource_id = aws_identity_arn.split("/")[-1]
report.resource_arn = aws_identity_arn
report.status = "FAIL"
report.status_extended = f"Potential enumeration attack detected from AWS {aws_identity_type} {aws_identity_arn.split('/')[-1]} with a threshold of {identity_threshold}."
findings.append(report)
if not found_potential_enumeration:
report = Check_Report_AWS(
metadata=self.metadata(), resource=cloudtrail_client.trails
)
report.region = cloudtrail_client.region
report.resource_id = cloudtrail_client.audited_account
report.resource_arn = cloudtrail_client._get_trail_arn_template(
cloudtrail_client.region
)
report.status = "PASS"
report.status_extended = "No potential enumeration attack detected."
findings.append(report)
return findingsReferences
- https://medium.com/falconforce/falconfriday-detecting-enumeration-in-aws-0xff25-orangecon-25-edition-4aee83651088
- https://www.elastic.co/guide/en/security/8.19/aws-discovery-api-calls-via-cli-from-a-single-resource.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html#cloudtrail-concepts-logging-data-events
- https://aws.plainenglish.io/aws-cloudtrail-event-cheatsheet-a-detection-engineers-guide-to-critical-api-calls-part-1-04fb1588556f
- https://support.icompaas.com/support/solutions/articles/62000233455-ensure-there-are-no-potential-enumeration-threats-in-cloudtrail-