Monitor AWS Credential abuse or hijacking
| Id | 32555639-b639-4c2b-afda-c0ae0abefa55 |
| Rulename | Monitor AWS Credential abuse or hijacking |
| Description | Looking for GetCallerIdentity Events where the UserID Type is AssumedRole An attacker who has assumed the role of a legitimate account can call the GetCallerIdentity function to determine what account they are using. A legitimate user using legitimate credentials would not need to call GetCallerIdentity since they should already know what account they are using. More Information: https://duo.com/decipher/trailblazer-hunts-compromised-credentials-in-aws AWS STS GetCallerIdentity API: https://docs.aws.amazon.com/STS/latest/APIReference/API_GetCallerIdentity.html |
| Severity | Low |
| Tactics | Discovery |
| Techniques | T1087 |
| Required data connectors | AWS AWSS3 |
| Kind | Scheduled |
| Query frequency | 1d |
| Query period | 1d |
| Trigger threshold | 0 |
| Trigger operator | gt |
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Amazon Web Services/Analytic Rules/AWS_CredentialHijack.yaml |
| Version | 1.0.3 |
| Arm template | 32555639-b639-4c2b-afda-c0ae0abefa55.json |
AWSCloudTrail
| where EventName =~ "GetCallerIdentity" and UserIdentityType =~ "AssumedRole"
| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by SourceIpAddress, EventName, EventTypeName, UserIdentityType, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, UserIdentityPrincipalid,
UserAgent, UserIdentityUserName, SessionMfaAuthenticated,AWSRegion, EventSource, AdditionalEventData, ResponseElements
| extend timestamp = StartTime
| sort by EndTime desc nulls last
triggerThreshold: 0
entityMappings:
- fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- identifier: CloudAppAccountId
columnName: RecipientAccountId
entityType: Account
- fieldMappings:
- identifier: Address
columnName: SourceIpAddress
entityType: IP
tactics:
- Discovery
version: 1.0.3
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Amazon Web Services/Analytic Rules/AWS_CredentialHijack.yaml
queryPeriod: 1d
kind: Scheduled
relevantTechniques:
- T1087
name: Monitor AWS Credential abuse or hijacking
id: 32555639-b639-4c2b-afda-c0ae0abefa55
status: Available
description: |
'Looking for GetCallerIdentity Events where the UserID Type is AssumedRole
An attacker who has assumed the role of a legitimate account can call the GetCallerIdentity function to determine what account they are using.
A legitimate user using legitimate credentials would not need to call GetCallerIdentity since they should already know what account they are using.
More Information: https://duo.com/decipher/trailblazer-hunts-compromised-credentials-in-aws
AWS STS GetCallerIdentity API: https://docs.aws.amazon.com/STS/latest/APIReference/API_GetCallerIdentity.html '
severity: Low
queryFrequency: 1d
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
- connectorId: AWSS3
dataTypes:
- AWSCloudTrail
query: |
AWSCloudTrail
| where EventName =~ "GetCallerIdentity" and UserIdentityType =~ "AssumedRole"
| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by SourceIpAddress, EventName, EventTypeName, UserIdentityType, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, UserIdentityPrincipalid,
UserAgent, UserIdentityUserName, SessionMfaAuthenticated,AWSRegion, EventSource, AdditionalEventData, ResponseElements
| extend timestamp = StartTime
| sort by EndTime desc nulls last
triggerOperator: gt