Detection rules › Panther
S3 Access Via VPC Endpoint From External IP
Detects S3 data access through VPC endpoints from external/public IP addresses, which could indicate data exfiltration attempts. This rule can be customized with the following overrides: - S3_DATA_ACCESS_OPERATIONS: List of S3 operations to monitor
Rules detecting the same action
Other rules on this platform that filter on the same API call or operation.
- AWS Detect Users with KMS keys performing encryption S3 (Splunk)
- AWS Exfiltration via Anomalous GetObject API Activity (Splunk)
- AWSCloudTrail - S3 bucket suspicious ransomware activity (Kusto)
- AWSCloudTrail - S3 Object Exfiltration from Anonymous User (Kusto)
- AWSCloudTrail - S3 object publicly exposed (Kusto)
- AWSCloudTrail - Successful brute force attack on S3 Bucket (Kusto)
- AWSCloudTrail - Suspicious command sent to EC2 (Kusto)
- Suspicious access of BEC related documents in AWS S3 buckets (Kusto)
Rule body yaml
AnalysisType: rule
Filename: aws_vpce_s3_external_ip.py
RuleID: "AWS.CloudTrail.VPCE.S3ExternalIP"
DisplayName: "S3 Access Via VPC Endpoint From External IP"
Enabled: true
LogTypes:
- AWS.CloudTrail
Severity: Medium
Tags:
- AWS
- VPC
- S3
- Data Exfiltration
- CloudTrail
- Network Boundary Bridging
- Exfiltration Over Alternative Protocol
Description: |
Detects S3 data access through VPC endpoints from external/public IP addresses, which could indicate data exfiltration attempts.
This rule can be customized with the following overrides:
- S3_DATA_ACCESS_OPERATIONS: List of S3 operations to monitor
Runbook: |
1. Identify the principal and the specific S3 objects being accessed
2. Verify if the external IP address belongs to a legitimate service or entity
3. Check if the access pattern is expected for this user/role
4. Review the contents of the S3 objects to determine sensitivity
5. If unauthorized, determine how the principal obtained access credentials
6. Revoke access immediately if determined to be malicious
7. Consider implementing stricter bucket policies and VPC endpoint policies
Reference: https://www.wiz.io/blog/aws-vpc-endpoint-cloudtrail
SummaryAttributes:
- userIdentity.principalId
- sourceIPAddress
- eventSource
- eventName
- requestParameters
- resources
DedupPeriodMinutes: 60
Tests:
- Name: External IP Access
ExpectedResult: true
Log:
{
"eventType": "AwsVpceEvent",
"eventCategory": "NetworkActivity",
"eventSource": "s3.amazonaws.com",
"eventName": "GetObject",
"userIdentity": {
"type": "IAMUser",
"principalId": "AIDAEXAMPLE",
"accountId": "012345678901"
},
"sourceIPAddress": "8.8.8.8",
"requestParameters": {
"bucketName": "sensitive-data-bucket",
"key": "confidential/file.pdf"
},
"resources": [
{
"type": "AWS::S3::Object",
"ARN": "arn:aws:s3:::sensitive-data-bucket/confidential/file.pdf"
}
],
"eventTime": "2023-01-01T12:00:00Z",
"vpcEndpointId": "vpce-EXAMPLE08c1b6b9b7",
"vpcEndpointAccountId": "012345678901",
"recipientAccountId": "012345678901"
}
- Name: Internal IP Access
ExpectedResult: false
Log:
{
"eventType": "AwsVpceEvent",
"eventCategory": "NetworkActivity",
"eventSource": "s3.amazonaws.com",
"eventName": "GetObject",
"userIdentity": {
"type": "IAMUser",
"principalId": "AIDAEXAMPLE",
"accountId": "012345678901"
},
"sourceIPAddress": "10.0.0.1",
"requestParameters": {
"bucketName": "sensitive-data-bucket",
"key": "confidential/file.pdf"
},
"resources": [
{
"type": "AWS::S3::Object",
"ARN": "arn:aws:s3:::sensitive-data-bucket/confidential/file.pdf"
}
],
"eventTime": "2023-01-01T12:00:00Z",
"vpcEndpointId": "vpce-EXAMPLE08c1b6b9b7",
"vpcEndpointAccountId": "012345678901",
"recipientAccountId": "012345678901"
}
- Name: Not S3 Service
ExpectedResult: false
Log:
{
"eventType": "AwsVpceEvent",
"eventCategory": "NetworkActivity",
"eventSource": "ec2.amazonaws.com",
"eventName": "DescribeInstances",
"userIdentity": {
"type": "IAMUser",
"principalId": "AIDAEXAMPLE",
"accountId": "012345678901"
},
"sourceIPAddress": "8.8.8.8"
}
Detection logic
Condition
not (eventType ne "AwsVpceEvent" or eventCategory ne "NetworkActivity" or eventSource ne "s3.amazonaws.com")
eventName in ["GetObject", "GetObjectVersion", "GetObjectAcl", "GetObjectVersionAcl", "PutObject", "PutObjectAcl", "PutObjectVersionAcl", "CopyObject", "DeleteObject", "DeleteObjects", "DeleteObjectVersion"]
sourceIPAddress is_not_null
This rule also runs imperative logic the parser cannot express as a filter; the conditions above are the structured part it could extract.
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
eventCategory | ne | NetworkActivity |
eventSource | ne | s3.amazonaws.com |
eventType | ne | AwsVpceEvent |
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 |
|---|---|---|
eventName | in |
|
sourceIPAddress | is_not_null |
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 |
|---|---|
eventName | |
eventSource | |
awsRegion | |
recipientAccountId | |
sourceIPAddress | |
userAgent | |
userIdentity | |
actor_user | |
bucketName | requestParameters.bucketName |