↳ GitHub sourceAnalytics ruleMedium

Expired access credentials being used in Azure

Description

This 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 an attacker has copied the authentication cookie and is re-using it on another machine.
Rule type
Scheduled
Version
1.0.0
Declared status
Available
Query frequency
1d
Query period
7d
Trigger
gt 0

Declared MITRE coverage

Declared sources

Metadata from the source file. No dependencies inferred from KQL.

Connectors

Data types

KQL query

Original query, unchanged.

// 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 period 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

Declared entities

AccountIP

Related content

Links established from declared identifiers and solution manifests.

Source provenance

GitHub

Displayed values come from files in Azure/Azure-Sentinel. They describe the published template, not your workspace configuration.

Source identifier
433c3b0a-7278-4d74-b137-963ac6f9a7e7
Additional source files 2Solutions/FalconFriday/Analytic Rules/ExpiredAccessCredentials.yamlsource ↗Solutions/FalconFriday/Data/Solution_FalconFriday.jsonsolution-membership ↗
GSTEP / CATALOG TRACKING

Added to catalog : 16 Sept 2026 · 05:49 UTC
Last change observed : 16 Sept 2026 · 05:49 UTC

GSTEP sync dates, separate from the source content’s publication dates.