Correlate SharePoint email link clicks with subsequent Azure AD sign-ins from a different IP address to detect possible phishing, token theft, or adversary-in-the-middle activity
IdentityPhishingAiTMSuspiciousSignInUrlClickCorrelationSharePointTokenTheftphishing_detection_sharepoint_email.kqlSe på GitHub
KQL
let WhitelistIP = dynamic(["0.0.0.0"]);
let LookBack = 30m;
let Clicks = UrlClickEvents
| where TimeGenerated >= ago(LookBack)
| where Workload == "Email"
| where Url has "sharepoint.com"
| project ClickTime = TimeGenerated, AccountUpn, Url, UrlChain, ClickIP = IPAddress;
let SignIns = union isfuzzy=true
(SigninLogs
| where TimeGenerated >= ago(LookBack)
| project
SignInTime = TimeGenerated,
UserPrincipalName,
SignInIP = tostring(IPAddress),
Timestamp = TimeGenerated,
ReportId = tostring(Id),
ResourceDisplayName,
IncomingTokenType,
IsInteractive,
SignInSource = "SigninLogs"),
(AADNonInteractiveUserSignInLogs
| where TimeGenerated >= ago(LookBack)
| project
SignInTime = TimeGenerated,
UserPrincipalName,
SignInIP = tostring(IPAddress),
Timestamp = TimeGenerated,
ReportId = tostring(Id),
ResourceDisplayName,
IncomingTokenType,
IsInteractive,
SignInEventTypes,
SignInSource = "AADNonInteractiveUserSignInLogs")
| where isnotempty(SignInIP)
| where not(set_has_element(WhitelistIP, SignInIP));
Clicks
| join kind=inner SignIns on $left.AccountUpn == $right.UserPrincipalName
| where isnotempty(ClickIP) and isnotempty(SignInIP)
| where SignInTime > ClickTime
| where ClickIP != SignInIP
| extend DelayMinutes = datetime_diff("minute", SignInTime, ClickTime)
| summarize arg_min(DelayMinutes, *) by AccountUpn, ClickTime, Url, ClickIP
| project
Timestamp,
ReportId,
AccountUpn,
ClickTime,
SignInTime,
DelayMinutes,
ClickIP,
SignInIP,
SignInSource,
IsInteractive,
IncomingTokenType,
SignInEventTypes,
ResourceDisplayName,
Url,
UrlChain