Note: You are viewing an old, archived version of this content. The latest version is available in the 'main' branch.
ID: Use_of_Expired_Azure_AD_Credentials_Without_Successful_Login-Azure
OS: N/A
FP Rate: Low
Tactics: TA0006 - Credential Access
Technique | Subtechnique | Technique Name |
---|---|---|
T1528 | Steal Application Access Token | |
|
Log Provider | Event ID | Event Name | ATT&CK Data Source |
---|---|---|---|
AzureAD_SigninLog |
The query searches for logins with an expired access credential (for example an expired cookie) it then matches the IP address from which the expired credential access occurred with the IP addresses of successful logins. If there are logins with expired credentials but no successful logins from an IP this might indicate and attacker has copied the authentication cookie and is re-using it on another machine.
User
Attackers can gain access to Azure AD protected resources by stealing an authentication token or cookie from a web-browser and then injecting that stolen cookie into a different system.
None.
Users could be on a connection with regularly changing IP address that shows the expired login from a different IP compared to successful logins.
Investigate if the user account could be compromised. Especially when the country for the expired logins differs from the country the user normally logs in from.
If an attacker only uses the authentication cookie while it is still valid this technique will not detect it.
- https://github.com/splunk/security_content/blob/626a4fe1a8b5dcf5b526bf5e458d243e0c12f55d/detections/cloud/o365_excessive_sso_logon_errors.yml
- https://stealthbits.com/blog/bypassing-mfa-with-pass-the-cookie/
Language: Kusto
Platform: Sentinel
Query:
// Timeframe to search for failed logins
let timeframe=1d;
// Timeframe to look back for successful logins from the same user by IP
let lookback=7d;
let SuspiciousSignings=(
SigninLogs
| where TimeGenerated >= ago(timeframe)
| where ResourceDisplayName contains "Windows Azure Active Directory"
// 50132 = SsoArtifactInvalidOrExpired - The session is not valid due to password expiration or recent password change.
// 50173 = FreshTokenNeeded - The provided grant has expired due to it being revoked, and a fresh auth token is needed.
// 70008 ExpiredOrRevokedGrant - The refresh token has expired due to inactivity. The token was issued on XXX and was inactive for a certain amount of time.
// 81010 DesktopSsoAuthTokenInvalid - Seamless SSO failed because the user's Kerberos ticket has expired or is invalid.
| where ResultType in (50173, 50132, 70008, 81010)
| summarize FailedCountPerDay=count(),FailedUserAgents=make_set(UserAgent), FailedCountries=make_set(LocationDetails.countryOrRegion),FailedIps=make_set(IPAddress) by UserPrincipalName, Day=bin(TimeGenerated, 1d)
| where FailedCountPerDay >= 1
);
let SuccessLogins=(
SigninLogs
| where TimeGenerated >= ago(lookback)
| where UserPrincipalName in ((SuspiciousSignings | project UserPrincipalName))
| where ResultType == 0
| summarize count() by UserPrincipalName, IPAddress
);
SuspiciousSignings
| mv-expand FailedIp=FailedIps
| extend FailedIp=tostring(FailedIp)
| join kind=leftanti SuccessLogins on $left.FailedIp==$right.IPAddress, UserPrincipalName