Detection rules › Panther
External Principal Accessing AWS Resources Via VPC Endpoint
This rule detects when a principal from one AWS account accesses resources in a different AWS account using a VPC Endpoint. While cross-account access may be expected in some cases, it could also indicate unauthorized lateral movement between AWS accounts.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment | T1599 Network Boundary Bridging |
| Discovery | T1526 Cloud Service Discovery |
| Exfiltration | T1048 Exfiltration Over Alternative Protocol |
Rule body yaml
AnalysisType: rule
Filename: aws_vpce_external_principal.py
RuleID: AWS.CloudTrail.VPCE.ExternalPrincipal
DisplayName: External Principal Accessing AWS Resources Via VPC Endpoint
Enabled: true
LogTypes:
- AWS.CloudTrail
Tags:
- AWS
- CloudTrail
- VPCEndpoint
- Network Boundary Bridging
- Cloud Service Discovery
- Exfiltration Over Alternative Protocol
Reports:
MITRE ATT&CK:
- TA0005:T1599 # Network Boundary Bridging
- TA0007:T1526 # Cloud Service Discovery
- TA0010:T1048 # Exfiltration Over Alternative Protocol
Severity: Medium
Description: >
This rule detects when a principal from one AWS account accesses resources in a different AWS account using a VPC Endpoint.
While cross-account access may be expected in some cases, it could also indicate unauthorized lateral movement between AWS accounts.
Runbook: |
1. Identify the principal account and the accessed account from the alert context.
2. Verify if the cross-account access is expected and authorized:
- Check if the principal account is part of your organization
- Review IAM policies for the VPC Endpoint to confirm if cross-account access is intentional
- Check resource policies for the accessed service to confirm if the principal should have access
3. If the access is unexpected:
- Review the API calls made by the principal
- Check the VPC Endpoint configuration for potential misconfiguration
- Consider restricting VPC Endpoint access to prevent unauthorized cross-account access
- Investigate for additional signs of unauthorized access
4. Document findings and take appropriate remediation steps based on investigation.
Reference: https://www.wiz.io/blog/aws-vpc-endpoint-cloudtrail
Tests:
- Name: External Principal Access
ExpectedResult: true
Log:
{
"eventVersion": "1.08",
"eventCategory": "NetworkActivity",
"eventType": "AwsVpceEvent",
"eventTime": "2023-03-01T00:00:00Z",
"awsRegion": "us-east-1",
"eventSource": "s3.amazonaws.com",
"eventName": "GetObject",
"sourceIPAddress": "10.0.0.1",
"userIdentity": {
"type": "AWSAccount",
"accountId": "111111111111",
"principalId": "AROAEXAMPLE:session-name"
},
"recipientAccountId": "222222222222",
"requestParameters": {
"bucketName": "example-bucket",
"key": "sensitive-file.txt"
},
"responseElements": null,
"vpcEndpointId": "vpce-EXAMPLE08c1b6b9b7",
"vpcEndpointAccountId": "222222222222"
}
- Name: Same Account Access
ExpectedResult: false
Log:
{
"eventVersion": "1.08",
"eventCategory": "NetworkActivity",
"eventType": "AwsVpceEvent",
"eventTime": "2023-03-01T00:00:00Z",
"awsRegion": "us-east-1",
"eventSource": "s3.amazonaws.com",
"eventName": "GetObject",
"sourceIPAddress": "10.0.0.1",
"userIdentity": {
"type": "AWSAccount",
"accountId": "222222222222",
"principalId": "AROAEXAMPLE:session-name"
},
"recipientAccountId": "222222222222",
"requestParameters": {
"bucketName": "example-bucket",
"key": "sensitive-file.txt"
},
"responseElements": null,
"vpcEndpointId": "vpce-EXAMPLE08c1b6b9b7",
"vpcEndpointAccountId": "222222222222"
}
- Name: Non-VPC Event
ExpectedResult: false
Log:
{
"eventVersion": "1.08",
"eventCategory": "Management",
"eventType": "AwsApiCall",
"eventTime": "2023-03-01T00:00:00Z",
"awsRegion": "us-east-1",
"eventSource": "s3.amazonaws.com",
"eventName": "GetObject",
"sourceIPAddress": "10.0.0.1",
"userIdentity": {
"type": "AWSAccount",
"accountId": "111111111111",
"principalId": "AROAEXAMPLE:session-name"
},
"recipientAccountId": "222222222222",
"requestParameters": {
"bucketName": "example-bucket",
"key": "sensitive-file.txt"
},
"responseElements": null
}
Detection logic
Condition
not (eventType ne "AwsVpceEvent" or eventCategory ne "NetworkActivity")
userIdentity.type eq "AWSAccount"
userIdentity not contains "arn"
userIdentity contains "principalId"
recipientAccountId is_not_null
userIdentity.accountId is_not_null
recipientAccountId cross_field_compare "userIdentity.accountId"
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
eventCategory | ne | NetworkActivity |
eventType | ne | AwsVpceEvent |
userIdentity | contains | arn |
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 |
|---|---|---|
recipientAccountId | cross_field_compare |
|
recipientAccountId | is_not_null | |
userIdentity | contains |
|
userIdentity.accountId | is_not_null | |
userIdentity.type | eq |
|
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 | |
accountId | userIdentity.accountId |