Detection rules › Panther
AWS IMDS Credential Usage Outside Expected Services
Detects when an EC2 instance identity (credentials obtained via IMDS) is used to make API calls outside of expected internal AWS services like SSM. This indicates that IMDS credentials may have been exfiltrated from a compromised instance and are being used externally for lateral movement or privilege escalation.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation | T1078.004 Valid Accounts: Cloud Accounts |
| Stealth | T1078.004 Valid Accounts: Cloud Accounts |
Rule body yaml
AnalysisType: rule
Filename: aws_imds_credential_exfiltration.py
RuleID: "AWS.CloudTrail.IMDSCredentialExfiltration"
DisplayName: "AWS IMDS Credential Usage Outside Expected Services"
Enabled: true
LogTypes:
- AWS.CloudTrail
Tags:
- AWS
- AWS STS
- Privilege Escalation:Valid Accounts
- Defense Evasion:Valid Accounts
Reports:
MITRE ATT&CK:
- TA0004:T1078.004
- TA0005:T1078.004
Status: Experimental
Severity: High
Description: >
Detects when an EC2 instance identity (credentials obtained via IMDS) is used to
make API calls outside of expected internal AWS services like SSM. This indicates
that IMDS credentials may have been exfiltrated from a compromised instance and
are being used externally for lateral movement or privilege escalation.
Runbook: |
1. Query CloudTrail for all API calls by userIdentity:arn in the 24 hours before and after this alert to identify the full scope of actions taken with the exfiltrated instance credentials
2. Check if sourceIPAddress is external to AWS or outside the expected VPC CIDR ranges for the EC2 instance associated with this role
3. Find all other alerts associated with this userIdentity:arn or sourceIPAddress in the past 7 days to assess whether this is part of a broader compromise campaign
Reference: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
SummaryAttributes:
- userAgent
- sourceIpAddress
- recipientAccountId
- p_any_aws_arns
DedupPeriodMinutes: 60
Threshold: 1
Tests:
- Name: IMDS Credentials Used From Public IP
ExpectedResult: true
Log:
{
"awsRegion": "us-east-1",
"eventName": "DescribeInstances",
"eventSource": "ec2.amazonaws.com",
"eventTime": "2024-01-15T10:30:00Z",
"eventType": "AwsApiCall",
"recipientAccountId": "123456789012",
"sourceIPAddress": "45.33.32.156",
"userAgent": "aws-cli/2.15.0",
"userIdentity": {
"accessKeyId": "ASIAIOSFODNN7EXAMPLE",
"accountId": "123456789012",
"arn": "arn:aws:sts::123456789012:assumed-role/MyAppRole/i-0abcdef1234567890",
"principalId": "AROAEXAMPLE:i-0abcdef1234567890",
"type": "AssumedRole",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"arn": "arn:aws:iam::123456789012:role/MyAppRole"
}
}
}
}
- Name: Normal EC2 Workload From Private IP
ExpectedResult: false
Log:
{
"awsRegion": "us-east-1",
"eventName": "PutObject",
"eventSource": "s3.amazonaws.com",
"eventTime": "2024-01-15T10:30:00Z",
"eventType": "AwsApiCall",
"recipientAccountId": "123456789012",
"sourceIPAddress": "10.0.1.50",
"userAgent": "aws-sdk-java/2.20.0",
"userIdentity": {
"arn": "arn:aws:sts::123456789012:assumed-role/MyAppRole/i-0abcdef1234567890",
"type": "AssumedRole"
}
}
- Name: AWS Service Hostname as Source IP
ExpectedResult: false
Log:
{
"awsRegion": "us-east-1",
"eventName": "Decrypt",
"eventSource": "kms.amazonaws.com",
"eventTime": "2024-01-15T10:30:00Z",
"eventType": "AwsApiCall",
"recipientAccountId": "123456789012",
"sourceIPAddress": "cloudformation.amazonaws.com",
"userIdentity": {
"arn": "arn:aws:sts::123456789012:assumed-role/MyAppRole/i-0abcdef1234567890",
"type": "AssumedRole"
}
}
- Name: Legitimate SSM Activity
ExpectedResult: false
Log:
{
"awsRegion": "us-east-1",
"eventName": "UpdateInstanceInformation",
"eventSource": "ssm.amazonaws.com",
"eventTime": "2024-01-15T10:30:00Z",
"eventType": "AwsApiCall",
"recipientAccountId": "123456789012",
"sourceIPAddress": "10.0.1.50",
"userAgent": "amazon-ssm-agent/3.2.0",
"userIdentity": {
"arn": "arn:aws:sts::123456789012:assumed-role/MyAppRole/i-0abcdef1234567890",
"type": "AssumedRole"
}
}
- Name: AWS Internal Source IP
ExpectedResult: false
Log:
{
"awsRegion": "us-east-1",
"eventName": "SendHeartbeat",
"eventSource": "ec2messages.amazonaws.com",
"eventTime": "2024-01-15T10:30:00Z",
"eventType": "AwsApiCall",
"recipientAccountId": "123456789012",
"sourceIPAddress": "AWS Internal",
"userAgent": "amazon-ssm-agent/3.2.0",
"userIdentity": {
"arn": "arn:aws:sts::123456789012:assumed-role/MyAppRole/i-0abcdef1234567890",
"type": "AssumedRole"
}
}
- Name: RegisterManagedInstance Event
ExpectedResult: false
Log:
{
"awsRegion": "us-east-1",
"eventName": "RegisterManagedInstance",
"eventSource": "ec2.amazonaws.com",
"eventTime": "2024-01-15T10:30:00Z",
"eventType": "AwsApiCall",
"recipientAccountId": "123456789012",
"sourceIPAddress": "10.0.1.50",
"userIdentity": {
"arn": "arn:aws:sts::123456789012:assumed-role/MyAppRole/i-0abcdef1234567890",
"type": "AssumedRole"
}
}
- Name: Non-Instance Role
ExpectedResult: false
Log:
{
"awsRegion": "us-east-1",
"eventName": "DescribeInstances",
"eventSource": "ec2.amazonaws.com",
"eventTime": "2024-01-15T10:30:00Z",
"eventType": "AwsApiCall",
"recipientAccountId": "123456789012",
"sourceIPAddress": "45.33.32.156",
"userAgent": "aws-cli/2.15.0",
"userIdentity": {
"arn": "arn:aws:sts::123456789012:assumed-role/MyCustomRole/session",
"type": "AssumedRole"
}
}
Detection logic
Condition
eventSource not in "ssm.amazonaws.com"
eventName not in "RegisterManagedInstance"
sourceIPAddress not in "AWS Internal"
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 |
|---|---|---|
eventName | eq | RegisterManagedInstance |
eventSource | eq | ssm.amazonaws.com |
sourceIPAddress | eq | AWS Internal |
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 | |
arn | userIdentity.arn |