Repository navigation
Add detection for repeated CloudWatch Logs read operations #4288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| name: AWS Repeated CloudWatch Logs Read Operations | ||
| id: 3f0ba9e1-c6cf-4317-a6bc-c4bcab2b5a06 | ||
| version: 1 | ||
| creation_date: '2026-09-21' | ||
| modification_date: '2026-09-21' | ||
| author: Maria Jose Erquiaga, Splunk | ||
| status: production | ||
| type: TTP | ||
| description: >- | ||
| The following analytic detects a high volume of Amazon CloudWatch Logs read | ||
| operations performed by the same AWS principal and account within a | ||
| five-minute period. It identifies more than 500 DescribeLogGroups, | ||
| DescribeLogStreams, DownloadLogEvents, FilterLogEvents, GetLogEvents, or | ||
| GetQueryResults API calls. This activity may indicate automated discovery or | ||
| collection of operational, application, or security telemetry from | ||
| CloudWatch Logs. | ||
| data_source: | ||
| - AWS CloudTrail | ||
| search: |- | ||
| `cloudtrail` eventSource="logs.amazonaws.com" | ||
| eventName IN ( | ||
| "DescribeLogGroups", | ||
| "DescribeLogStreams", | ||
| "DownloadLogEvents", | ||
| "FilterLogEvents", | ||
| "GetLogEvents", | ||
| "GetQueryResults" | ||
| ) | ||
| | eval user='userIdentity.principalId' | ||
| | eval vendor_account=coalesce( | ||
| vendor_account, | ||
| recipientAccountId, | ||
| 'userIdentity.accountId' | ||
| ) | ||
| | eval role_name='userIdentity.sessionContext.sessionIssuer.userName' | ||
| | eval signature=coalesce(signature, eventName) | ||
| | eval src=coalesce(src, sourceIPAddress) | ||
| | eval user_agent=coalesce(user_agent, userAgent) | ||
| | eval vendor_region=coalesce(vendor_region, awsRegion) | ||
| | where isnotnull(user) AND len(trim(user)) > 0 | ||
| | sort 0 _time | ||
| | streamstats time_window=5m count AS event_count | ||
| by user vendor_account | ||
| | where event_count > 500 | ||
| | stats | ||
| max(event_count) AS event_count | ||
| min(_time) AS firstTime | ||
| max(_time) AS lastTime | ||
| values(signature) AS api_operations | ||
| values(role_name) AS role_name | ||
| values(src) AS src | ||
| values(user_agent) AS user_agent | ||
| values(vendor_region) AS vendor_region | ||
| by user vendor_account | ||
| | `security_content_ctime(firstTime)` | ||
| | `security_content_ctime(lastTime)` | ||
| | `aws_repeated_cloudwatch_logs_read_operations_filter` | ||
| how_to_implement: >- | ||
| The Splunk Add-on for AWS is required to collect AWS CloudTrail events. | ||
| Configure CloudTrail to capture management Read events and ingest them with | ||
| the aws:cloudtrail sourcetype. The tested CloudWatch Logs operations are | ||
| recorded as management events, so a CloudWatch Logs data-event selector is | ||
| not required. The analytic uses a rolling five-minute window and counts | ||
|
Comment on lines
+60
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a deployment follows this guidance and collects only management Read events, CloudTrail will not ingest Useful? React with 👍 / 👎. |
||
| unique CloudTrail event identifiers to reduce duplicate-ingestion effects. | ||
| known_false_positives: >- | ||
| Automated log analytics, SIEM pipelines, incident-response tooling, backup | ||
| processes, and administrative troubleshooting may continuously read | ||
| CloudWatch Logs and generate a high volume of matching operations. The | ||
| prevalence of this activity has not yet been evaluated against broader | ||
| customer telemetry. Review the principal, account, source addresses, user | ||
| agents, Regions, and accessed CloudWatch Logs resources before escalating. | ||
| references: | ||
| - https://attack.mitre.org/techniques/T1530/ | ||
| - https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_DescribeLogGroups.html | ||
| - https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_DescribeLogStreams.html | ||
| - https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_FilterLogEvents.html | ||
| - https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_GetLogEvents.html | ||
| - https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_GetQueryResults.html | ||
| drilldown_searches: | ||
| - name: View CloudWatch Logs read operations for - "$user$" | ||
| search: '%original_detection_search% | search user="$user$" vendor_account="$vendor_account$"' | ||
| earliest_offset: $info_min_time$ | ||
| latest_offset: $info_max_time$ | ||
| - name: View risk events for the last 7 days for - "$user$" | ||
| search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$user$") | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' | ||
| earliest_offset: 7d | ||
| latest_offset: "0" | ||
| finding: | ||
| title: AWS principal $user$ performed $event_count$ CloudWatch Logs read operations | ||
| entity: | ||
| field: user | ||
| type: user | ||
| score: 50 | ||
| threat_objects: | ||
| - field: src | ||
| type: ip_address | ||
| analytic_story: | ||
| - Data Exfiltration | ||
| asset_type: AWS Account | ||
| mitre_attack_id: | ||
| - T1530 | ||
| product: | ||
| - Splunk Enterprise | ||
| - Splunk Enterprise Security | ||
| - Splunk Cloud | ||
| category: cloud | ||
| security_domain: threat | ||
| tests: | ||
| - name: True Positive Test | ||
| attack_data: | ||
| - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1530/aws_cloudwatch_logs_high_volume_retrieval/aws_cloudwatch_logs_high_volume_retrieval.json | ||
| sourcetype: aws:cloudtrail | ||
| source: aws_cloudtrail | ||
| test_type: unit | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In environments where CloudTrail events are delivered or ingested more than once, this counts every copy as a separate API operation because no
eventIDdeduplication occurs. A principal with at most 500 actual calls can therefore cross the threshold and generate a false alert, contradicting the implementation guidance that promises unique identifiers are counted to reduce duplicate-ingestion effects. Deduplicate byeventIDbeforestreamstats(while retaining events without an ID), or remove that guarantee and explicitly accept duplicate-sensitive counts.Useful? React with 👍 / 👎.