Techniques
Sample rules
AWS Discovery API Calls via CLI from a Single Resource
- source: elastic
- technicques:
- T1580
Description
Detects when a single AWS resource is running multiple read-only, discovery API calls in a 10-second window. This behavior could indicate an actor attempting to discover the AWS infrastructure using compromised credentials or a compromised instance. Adversaries may use this information to identify potential targets for further exploitation or to gain a better understanding of the target’s infrastructure.
Detection logic
from logs-aws.cloudtrail-* metadata _id, _version, _index
// create time window buckets of 10 seconds
| eval Esql.time_window_date_trunc = date_trunc(10 seconds, @timestamp)
| where
event.dataset == "aws.cloudtrail"
// filter on CloudTrail audit logs for IAM, EC2, S3, etc.
and event.provider in (
"iam.amazonaws.com",
"ec2.amazonaws.com",
"s3.amazonaws.com",
"rds.amazonaws.com",
"lambda.amazonaws.com",
"dynamodb.amazonaws.com",
"kms.amazonaws.com",
"cloudfront.amazonaws.com",
"elasticloadbalancing.amazonaws.com",
"cloudtrail.amazonaws.com",
"sts.amazonaws.com",
"ses.amazonaws.com"
)
// ignore AWS service actions
and aws.cloudtrail.user_identity.type != "AWSService"
// filter for aws-cli specifically
and user_agent.name == "aws-cli"
// exclude DescribeCapacityReservations events related to AWS Config
and event.action != "DescribeCapacityReservations"
// filter for Describe, Get, List, and Generate API calls
| where true in (
starts_with(event.action, "Describe"),
starts_with(event.action, "Get"),
starts_with(event.action, "List"),
starts_with(event.action, "Generate")
)
// extract owner, identity type, and actor from the ARN
| dissect aws.cloudtrail.user_identity.arn "%{}::%{Esql_priv.aws_cloudtrail_user_identity_arn_owner}:%{Esql.aws_cloudtrail_user_identity_arn_type}/%{Esql.aws_cloudtrail_user_identity_arn_roles}"
| where starts_with(Esql.aws_cloudtrail_user_identity_arn_roles, "AWSServiceRoleForConfig") != true
// keep relevant fields (preserving ECS fields and computed time window)
| keep
@timestamp,
Esql.time_window_date_trunc,
event.action,
aws.cloudtrail.user_identity.arn,
aws.cloudtrail.user_identity.type,
aws.cloudtrail.user_identity.access_key_id,
source.ip,
cloud.account.id,
event.provider,
user_agent.name,
source.as.organization.name,
cloud.region,
data_stream.namespace
// count the number of unique API calls per time window and actor
| stats
Esql.event_action_count_distinct = count_distinct(event.action),
Esql.event_action_values = VALUES(event.action),
Esql.event_timestamp_values = VALUES(@timestamp),
Esql.aws_cloudtrail_user_identity_type_values = VALUES(aws.cloudtrail.user_identity.type),
Esql.aws_cloudtrail_user_identity_access_key_id_values = VALUES(aws.cloudtrail.user_identity.access_key_id),
Esql.source_ip_values = VALUES(source.ip),
Esql.cloud_account_id_values = VALUES(cloud.account.id),
Esql.event_provider_values = VALUES(event.provider),
Esql.user_agent_name_values = VALUES(user_agent.name),
Esql.source_as_organization_name_values = VALUES(source.as.organization.name),
Esql.cloud_region_values = VALUES(cloud.region),
Esql.data_stream_namespace_values = VALUES(data_stream.namespace)
by Esql.time_window_date_trunc, aws.cloudtrail.user_identity.arn
// filter for more than 5 unique API calls per 10s window
| where Esql.event_action_count_distinct > 5