Detection rules › Kusto

AWSCloudTrail - Login to AWS Management Console without MFA

Status
available
Severity
low
Time window
1d
Group by
AWSRegion, AccountName, AccountUPNSuffix, EventName, EventTypeName, LoginResult, MFAUsed, RecipientAccountId, SessionMfaAuthenticated, SourceIpAddress, UserAgent, UserIdentityAccountId, UserIdentityPrincipalid, UserIdentityUserName, indexId
Source
github.com/Azure/Azure-Sentinel

Identifies successful AWS Management Console sign-ins where CloudTrail records a ConsoleLogin event without multi-factor authentication. The rule looks for logins where MFAUsed is not Yes and the console response is not Failure, which can indicate credential misuse or weak account protection.

MITRE ATT&CK coverage

TacticTechniques
Initial AccessT1078 Valid Accounts
PersistenceT1078 Valid Accounts
Privilege EscalationT1078 Valid Accounts
StealthT1078 Valid Accounts

Rules detecting the same action

Other rules on this platform that filter on the same API call or operation.

Rule body kusto

id: d25b1998-a592-4bc5-8a3a-92b39eedb1bc
name: AWSCloudTrail - Login to AWS Management Console without MFA
description: |
  Identifies successful AWS Management Console sign-ins where CloudTrail records a ConsoleLogin event without
  multi-factor authentication. The rule looks for logins where MFAUsed is not Yes and the console response is not
  Failure, which can indicate credential misuse or weak account protection.
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
  - connectorId: AWSS3
    dataTypes:
      - AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
  - PrivilegeEscalation
  - Persistence
  - InitialAccess
relevantTechniques:
  - T1078
query: |
  AWSCloudTrail
  | where EventName =~ "ConsoleLogin"
  | extend MFAUsed = tostring(parse_json(AdditionalEventData).MFAUsed), LoginResult = tostring(parse_json(ResponseElements).ConsoleLogin), indexId = indexof(tostring(UserIdentityPrincipalid),":")
  | where MFAUsed !~ "Yes" and LoginResult !~ "Failure"
  | where SessionIssuerUserName !contains "AWSReservedSSO"
  | 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 StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by EventName, EventTypeName, LoginResult, MFAUsed, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId,  UserIdentityPrincipalid, UserAgent,
  UserIdentityUserName, SessionMfaAuthenticated, SourceIpAddress, AWSRegion, indexId
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
      - identifier: CloudAppAccountId
        columnName: RecipientAccountId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIpAddress
customDetails:
  EventName: EventName
  LoginResult: LoginResult
  MFAUsed: MFAUsed
  AWSRegion: AWSRegion
  UserAgent: UserAgent
alertDetailsOverride:
  alertDisplayNameFormat: 'AWS Management Console sign-in without MFA: {{AccountName}} from {{SourceIpAddress}}'
  alertDescriptionFormat: 'Successful ConsoleLogin without MFA for {{AccountName}} from {{SourceIpAddress}} in account {{RecipientAccountId}}.'
version: 1.0.6
kind: Scheduled

Stages and Predicates

Stage 1: source

AWSCloudTrail

Stage 2: where

| where EventName =~ "ConsoleLogin"

Stage 3: extend

| extend MFAUsed = tostring(parse_json(AdditionalEventData).MFAUsed), LoginResult = tostring(parse_json(ResponseElements).ConsoleLogin), indexId = indexof(tostring(UserIdentityPrincipalid),":")

Stage 4: where

| where MFAUsed !~ "Yes" and LoginResult !~ "Failure"

Stage 5: where

| where SessionIssuerUserName !contains "AWSReservedSSO"

Stage 6: extend (4 consecutive steps)

| 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]), "")

Stage 7: summarize

| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by EventName, EventTypeName, LoginResult, MFAUsed, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId,  UserIdentityPrincipalid, UserAgent,
UserIdentityUserName, SessionMfaAuthenticated, SourceIpAddress, AWSRegion, indexId

Exclusions

Top-level NOT(...) conjuncts: predicates this rule actively suppresses.

FieldKindExcluded values
SessionIssuerUserNamecontainsAWSReservedSSO

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
EventNameeq
  • ConsoleLogin
LoginResultne
  • Failure
MFAUsedne
  • Yes

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
AWSRegionsummarize
AccountNamesummarize
AccountUPNSuffixsummarize
EndTimeUtcsummarize
EventNamesummarize
EventTypeNamesummarize
LoginResultsummarize
MFAUsedsummarize
RecipientAccountIdsummarize
SessionMfaAuthenticatedsummarize
SourceIpAddresssummarize
StartTimeUtcsummarize
UserAgentsummarize
UserIdentityAccountIdsummarize
UserIdentityPrincipalidsummarize
UserIdentityUserNamesummarize
indexIdsummarize