CloudTrail Logs Show No Potential Enumeration Activity
Overview
This check analyzes your CloudTrail logs to detect AWS identities that are making an unusually high number of discovery API calls (such as List*, Describe*, and Get*) within a short time window. When someone or something in your account is calling these APIs excessively, it may indicate that an attacker is trying to map out your AWS environment.
Severity: Critical
Risk
When this check fails, it means an identity in your account is exhibiting reconnaissance behavior. This is a serious concern because:
- Attackers map your environment first: Before stealing data or causing damage, attackers typically explore what resources exist and what permissions they have
- Valid credentials may be compromised: This activity uses real credentials, meaning someone has unauthorized access to your account
- It enables further attacks: Once attackers understand your environment, they can escalate privileges, access sensitive data, or disrupt services
- It can be hard to detect: These are legitimate-looking API calls, just in suspicious volumes
Remediation Steps
Prerequisites
You need access to:
- AWS Console with IAM permissions (to review and modify user/role permissions)
- The identity ARN from the Prowler finding (shows which user or role triggered the alert)
Setting up AWS CLI access
If you prefer command-line remediation, ensure you have:
- AWS CLI installed: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
- Credentials configured with appropriate permissions:
aws configure - Verify access:
aws sts get-caller-identity
Step 1: Identify the Suspicious Identity
First, find out which user or role triggered the alert. The Prowler finding includes an ARN that looks like one of these:
- IAM User:
arn:aws:iam::123456789012:user/username - IAM Role:
arn:aws:iam::123456789012:role/rolename - Assumed Role Session:
arn:aws:sts::123456789012:assumed-role/rolename/session-name
Step 2: Investigate the Activity (AWS Console)
- Go to CloudTrail in the AWS Console
- Click Event history in the left sidebar
- In the Lookup attributes dropdown, select User name
- Enter the username or role name from the finding
- Review the recent API calls to understand what was being accessed
Look for patterns like:
- Calls to many different services
- Rapid succession of
List*andDescribe*calls - Activity during unusual hours
- API calls from unexpected IP addresses or regions
Step 3: Take Immediate Action
If the activity appears malicious, take immediate containment steps:
For an IAM User:
- Go to IAM > Users in the AWS Console
- Select the user from the finding
- Click the Security credentials tab
- Under Access keys, click Make inactive for any active keys
- Under Console password, click Manage and select Disable
For an IAM Role (EC2 Instance):
- Go to EC2 > Instances
- Select the instance using the suspicious role
- Click Actions > Security > Modify IAM role
- Either remove the role or replace it with a more restrictive one
For an IAM Role (Lambda/Other Service):
- Identify the service using the role
- Either stop the service temporarily or update the role's trust policy to prevent further use
AWS CLI commands for containment
Deactivate an IAM user's access key:
aws iam update-access-key \
--user-name <USER_NAME> \
--access-key-id <ACCESS_KEY_ID> \
--status Inactive \
--region us-east-1
List all access keys for a user:
aws iam list-access-keys \
--user-name <USER_NAME> \
--region us-east-1
Delete an access key (if confirmed compromised):
aws iam delete-access-key \
--user-name <USER_NAME> \
--access-key-id <ACCESS_KEY_ID> \
--region us-east-1
Remove an IAM role from an EC2 instance:
aws ec2 disassociate-iam-instance-profile \
--association-id <ASSOCIATION_ID> \
--region us-east-1
Step 4: Apply Least Privilege (Long-Term Fix)
After containing the immediate threat, implement policies to prevent future enumeration:
- Review current permissions: Check what
List*,Describe*, andGet*permissions the identity has - Restrict to necessary resources: Instead of
"Resource": "*", specify only the resources needed - Use conditions: Add conditions like source IP, MFA, or time-based restrictions
Example: Restrict EC2 describe permissions to specific VPC:
In the IAM Console:
- Go to IAM > Policies
- Find the policy attached to the user/role
- Click Edit policy
- Modify the resource from
"*"to specific ARNs
AWS CLI: Apply a deny policy for broad enumeration
Create a policy that denies broad enumeration unless specifically allowed:
aws iam put-user-policy \
--user-name <USER_NAME> \
--policy-name DenyBroadEnumeration \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyBroadEnumeration",
"Effect": "Deny",
"Action": [
"ec2:Describe*",
"iam:List*",
"iam:Get*",
"s3:List*",
"rds:Describe*",
"lambda:List*"
],
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}' \
--region us-east-1
This policy denies broad enumeration unless the user has authenticated with MFA.
CloudFormation template
AWSTemplateFormatVersion: '2010-09-09'
Description: IAM policy to restrict enumeration APIs and enforce least privilege
Parameters:
UserName:
Type: String
Description: The IAM user to attach the restrictive policy to
Resources:
DenyEnumerationPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: DenyBroadEnumerationAPIs
Description: Denies broad enumeration API access to enforce least privilege
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: DenyBroadEnumeration
Effect: Deny
Action:
- 'ec2:Describe*'
- 'iam:List*'
- 'iam:Get*'
- 's3:List*'
- 'rds:Describe*'
- 'lambda:List*'
- 'dynamodb:List*'
- 'dynamodb:Describe*'
Resource: '*'
Condition:
StringNotEquals:
aws:PrincipalTag/AllowedEnumeration: 'true'
Users:
- !Ref UserName
Outputs:
PolicyArn:
Description: ARN of the created managed policy
Value: !Ref DenyEnumerationPolicy
Deploy:
aws cloudformation deploy \
--template-file template.yaml \
--stack-name deny-enumeration-policy \
--parameter-overrides UserName=<USER_NAME> \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1
Terraform configuration
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
variable "user_name" {
description = "The IAM user to attach the restrictive policy to"
type = string
}
resource "aws_iam_policy" "deny_enumeration" {
name = "DenyBroadEnumerationAPIs"
description = "Denies broad enumeration API access to enforce least privilege"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "DenyBroadEnumeration"
Effect = "Deny"
Action = [
"ec2:Describe*",
"iam:List*",
"iam:Get*",
"s3:List*",
"rds:Describe*",
"lambda:List*",
"dynamodb:List*",
"dynamodb:Describe*"
]
Resource = "*"
Condition = {
StringNotEquals = {
"aws:PrincipalTag/AllowedEnumeration" = "true"
}
}
}
]
})
}
resource "aws_iam_user_policy_attachment" "attach_deny_enumeration" {
user = var.user_name
policy_arn = aws_iam_policy.deny_enumeration.arn
}
output "policy_arn" {
description = "ARN of the created managed policy"
value = aws_iam_policy.deny_enumeration.arn
}
Deploy:
terraform init
terraform apply -var="user_name=<USER_NAME>"
Step 5: Implement Organization-Wide Controls
For broader protection, consider implementing Service Control Policies (SCPs):
- Go to AWS Organizations in the Console
- Navigate to Policies > Service control policies
- Create a new SCP that limits discovery APIs across accounts
This prevents any identity in your organization from performing excessive enumeration, regardless of their individual IAM permissions.
Verification
After remediation, verify the issue is resolved:
-
Re-run the Prowler check:
prowler aws --checks cloudtrail_threat_detection_enumeration -
Monitor CloudTrail for the next 24-48 hours to ensure no new enumeration activity from the remediated identity
-
Check the IAM Console to confirm:
- Access keys are deactivated/deleted for compromised users
- Restrictive policies are in place
- MFA is enabled for all users
Advanced verification with AWS CLI
Check recent API activity for a user:
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=Username,AttributeValue=<USER_NAME> \
--start-time $(date -u -v-24H +%Y-%m-%dT%H:%M:%SZ) \
--region us-east-1 \
--query 'Events[*].{Time:EventTime,Event:EventName,Source:EventSource}' \
--output table
Verify access key status:
aws iam list-access-keys \
--user-name <USER_NAME> \
--query 'AccessKeyMetadata[*].{KeyId:AccessKeyId,Status:Status,Created:CreateDate}' \
--output table \
--region us-east-1
List policies attached to user:
aws iam list-attached-user-policies \
--user-name <USER_NAME> \
--region us-east-1
Additional Resources
- AWS CloudTrail User Guide
- IAM Best Practices
- AWS Security Incident Response Guide
- Service Control Policies
- Prowler Documentation
Notes
- False positives can occur: Legitimate automation, CI/CD pipelines, or admin activities may trigger this check. Review the activity context before taking action.
- Baseline your environment: Know what normal discovery activity looks like for your accounts so you can better identify anomalies.
- Rotate credentials after incidents: If you suspect compromise, rotate all credentials for the affected identity and review what resources they accessed.
- Enable GuardDuty: AWS GuardDuty provides additional threat detection capabilities that complement CloudTrail analysis.
- Consider AWS Detective: For complex investigations, AWS Detective can help visualize and analyze security findings.
Compliance Mapping
This check maps to the following compliance frameworks:
| Framework | Control |
|---|---|
| C5 | Reconnaissance Detection |
| CCC | Cloud Security Monitoring |
| ISO27001 | A.12.4 Logging and Monitoring |
| KISA-ISMS-P | Threat Detection |
| NIS2 | Security Monitoring |
| SOC2 | CC6.1 Logical Access Controls |