Detection rules › Panther
AWS S3 Large Download
Returns S3 GetObject events where a user has downloaded more than the configured threshold of data within the specified time window. Supports filtering by bucket patterns and user types.
Rules detecting the same action
Other rules on this platform that filter on the same API call or operation.
Rule body yaml
AnalysisType: scheduled_query
QueryName: "AWS S3 Large Download"
Enabled: false
Description: >
Returns S3 GetObject events where a user has downloaded more than the configured threshold
of data within the specified time window. Supports filtering by bucket patterns and user types.
Tags:
- Beta
- Data Exfiltration
Query: |-
SELECT
userIdentity:arn as user_arn,
COALESCE(userIdentity:userName, userIdentity:sessionContext:sessionIssuer:userName, 'unknown') as user_name,
userIdentity:type as user_type,
requestParameters:bucketName as bucket_name,
sourceIPAddress as source_ip,
userAgent as user_agent,
SUM(COALESCE(additionalEventData:bytesTransferredOut::int, 0)) as total_bytes_downloaded,
COUNT(*) as object_count,
MIN(p_event_time) as first_download_time,
MAX(p_event_time) as last_download_time,
ARRAY_AGG(DISTINCT requestParameters:key) as sample_objects
FROM panther_logs.public.aws_cloudtrail
WHERE eventName = 'GetObject'
AND eventSource = 's3.amazonaws.com'
AND userIdentity:type IN ('IAMUser', 'AssumedRole', 'FederatedUser')
AND errorCode IS NULL
AND p_event_time >= DATEADD(minute, -10, CURRENT_TIMESTAMP())
AND userIdentity:arn NOT LIKE '%panther-snowflake-api%'
-- AND (requestParameters:bucketName LIKE '%sensitive%' OR requestParameters:bucketName LIKE '%prod%')
GROUP BY
userIdentity:arn,
COALESCE(userIdentity:userName, userIdentity:sessionContext:sessionIssuer:userName, 'unknown'),
userIdentity:type,
requestParameters:bucketName,
sourceIPAddress,
userAgent
HAVING SUM(COALESCE(additionalEventData:bytesTransferredOut::int, 0)) >= 52428800 -- 50MB default
ORDER BY total_bytes_downloaded DESC
Schedule:
RateMinutes: 10
TimeoutMinutes: 3
Detection logic
Stage 1: source
panther_logs.public.aws_cloudtrail
Stage 2: filter
eventName eq "GetObject"
eventSource eq "s3.amazonaws.com"
userIdentity:type in ["IAMUser", "AssumedRole", "FederatedUser"]
errorCode is_null
userIdentity:arn not wildcard "*panther-snowflake-api*"
This rule also runs imperative logic the parser cannot express as a filter; the conditions above are the structured part it could extract.
Stage 3: having
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
userIdentity:arn | match | panther-snowflake-api |
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
errorCode | is_null | |
eventName | eq |
|
eventSource | eq |
|
userIdentity:type | in |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
user_arn | userIdentity:arn |
user_name | COALESCE ( userIdentity:userName , userIdentity:sessionContext:sessionIssuer:userName , 'unknown' ) |
user_type | userIdentity:type |
bucket_name | requestParameters:bucketName |
source_ip | sourceIPAddress |
user_agent | userAgent |
total_bytes_downloaded | SUM ( COALESCE ( additionalEventData:bytesTransferredOut : : int , 0 ) ) |
object_count | COUNT ( * ) |
first_download_time | MIN ( p_event_time ) |
last_download_time | MAX ( p_event_time ) |
sample_objects | ARRAY_AGG ( DISTINCT requestParameters:key ) |