Detection rules › Panther

S3 Access Via VPC Endpoint From External IP

Severity
medium
Log types
AWS.CloudTrail
Tags
AWS, VPC, S3, Data Exfiltration, CloudTrail, Network Boundary Bridging, Exfiltration Over Alternative Protocol
Reference
https://www.wiz.io/blog/aws-vpc-endpoint-cloudtrail
Source
github.com/panther-labs/panther-analysis

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.

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.

FieldKindExcluded values
eventCategoryneNetworkActivity
eventSourcenes3.amazonaws.com
eventTypeneAwsVpceEvent

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.

FieldKindValues
eventNamein
  • CopyObject
  • DeleteObject
  • DeleteObjectVersion
  • DeleteObjects
  • GetObject
  • GetObjectAcl
  • GetObjectVersion
  • GetObjectVersionAcl
  • PutObject
  • PutObjectAcl
  • PutObjectVersionAcl
sourceIPAddressis_not_null
  • (no value, null check)

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.

FieldSource
eventName
eventSource
awsRegion
recipientAccountId
sourceIPAddress
userAgent
userIdentity
actor_user
bucketNamerequestParameters.bucketName