Suspicious Sign In After Phishing Link Click
Query
// Parameter
let CampaignStartUtc = datetime(2026-01-13T00:00:00Z);
let LookbackHistory = 29d;
let HistoryStart = CampaignStartUtc - LookbackHistory;
// Filter options - populate these as needed
let SenderEmailAddresses = dynamic([]); // e.g., ["[email protected]", "[email protected]"]
let SenderDomains = dynamic([]); // e.g., ["evil.com", "badactor.net"]
let SubjectKeywords = dynamic([
"bring me some beers",
"craft beer please",
"i prefer a juicy New England IPA ;-)"
]);
// Use and Logic for SenderEmailAddresses, SenderDomains and SubjectKeywords
let UseAndLogic = false; // true = ALL criteria must match, false = AT LEAST ONE must match
let ExcludedApps = dynamic(["TestAppTEST1", "TestAppTEST2"]);
// Affected Users
let PhishClicks = materialize(
EmailEvents
| where TimeGenerated >= CampaignStartUtc
| extend
// We check if a filter is active (not empty)
EmailFilterActive = array_length(SenderEmailAddresses) > 0,
DomainFilterActive = array_length(SenderDomains) > 0,
SubjectFilterActive = array_length(SubjectKeywords) > 0
| extend
// Actual matches
EmailMatches = SenderFromAddress has_any (SenderEmailAddresses),
DomainMatches = SenderFromDomain has_any (SenderDomains),
SubjectMatches = Subject has_any (SubjectKeywords)
| where
(UseAndLogic == true and
(not(EmailFilterActive) or EmailMatches) and
(not(DomainFilterActive) or DomainMatches) and
(not(SubjectFilterActive) or SubjectMatches)
)
or
(UseAndLogic == false and
(
// Logic: If multiple filters are provided, they must ALL match (Targeted Search)
// If only one is provided, only that one must match.
(not(EmailFilterActive) or EmailMatches) and
(not(DomainFilterActive) or DomainMatches) and
(not(SubjectFilterActive) or SubjectMatches)
)
)
| project NetworkMessageId, Subject, SenderFromAddress, SenderFromDomain, RecipientEmailAddress
| join kind=inner (
UrlClickEvents
| where TimeGenerated >= CampaignStartUtc
| project NetworkMessageId, ClickTime=TimeGenerated, Url, AccountUpn
) on NetworkMessageId
| extend TargetUser = coalesce(AccountUpn, RecipientEmailAddress)
| where isnotempty(TargetUser)
| summarize
MinClickTime = min(ClickTime),
ClickedUrl = any(Url),
Sender = any(SenderFromAddress),
SenderDomain = any(SenderFromDomain)
by TargetUser, Subject
);
// Historical IPs
let HistoricalIPs =
SigninLogs
| where TimeGenerated between (HistoryStart .. CampaignStartUtc)
| where ResultType == 0
| join kind=inner hint.strategy=broadcast (
PhishClicks | project UserPrincipalName = TargetUser | distinct UserPrincipalName
) on UserPrincipalName
| summarize IPSeenCount = count() by UserPrincipalName, IPAddress;
// Suspicious Sign-ins
SigninLogs
| where TimeGenerated >= CampaignStartUtc
| where ResultType == 0
| where AppDisplayName !in (ExcludedApps)
| join kind=inner hint.strategy=broadcast PhishClicks on $left.UserPrincipalName == $right.TargetUser
| where TimeGenerated >= MinClickTime
| join kind=leftouter hint.strategy=broadcast HistoricalIPs on UserPrincipalName, IPAddress
| extend IPRiskClassification = case(
isempty(IPSeenCount), "High Risk - New IP",
IPSeenCount < 3, "Medium Risk - Infrequent IP",
"Low Risk"
)
| where IPRiskClassification != "Low Risk"
// Final Aggregation and Reporting
| summarize
FirstSignIn = min(TimeGenerated),
SignInCountBefore = count(),
AccessedApps = make_set(AppDisplayName, 100),
City = any(tostring(LocationDetails.city)),
Country = any(tostring(LocationDetails.countryOrRegion)),
PhishSubject = any(Subject),
PhishUrl = any(ClickedUrl),
PhishSender = any(Sender),
ClickTime = any(MinClickTime)
by TargetUser, IPAddress, IPRiskClassification
| project TargetUser, IPAddress, Country, City, IPRiskClassification, FirstSignIn, ClickTime, SignInCountBefore, AccessedApps, PhishSubject, PhishSender
// Filter for rarely used IPs. to reduce only to NEW used IPAdresses Filter to <1
| where SignInCountBefore <2About this query
Suspicious Sign-in After Phishing Link Click
Query Information
MITRE ATT&CK Technique(s)
| Technique ID | Title | Link |
|---|---|---|
| T1566 | Phishing | https://attack.mitre.org/techniques/T1566 |
Description
This query detects suspicious sign-in activity from a user who has recently clicked on a phishing link. It correlates email click events with sign-in logs, identifying sign-ins from new or infrequent IP addresses after a user has interacted with a suspicious email. The rule uses configurable parameters for sender email addresses, sender domains, and subject keywords to identify phishing emails.
Author <Optional>
- Name: Benjamin Zulliger
- Github: https://github.com/benscha/KQLAdvancedHunting
- LinkedIn: https://www.linkedin.com/in/benjamin-zulliger/
Defender XDR
Explanation
This query is designed to detect suspicious sign-in activities following a user clicking on a phishing link. Here's a simplified breakdown of what the query does:
-
Setup and Parameters:
- The query starts by defining a campaign start date and a lookback period to analyze historical data.
- It allows for filtering based on specific sender email addresses, domains, and subject keywords to identify potential phishing emails. These filters can be combined using "AND" or "OR" logic.
-
Identifying Phishing Clicks:
- It searches for email events where users received emails matching the specified criteria (sender, domain, subject).
- It then correlates these emails with URL click events to identify users who clicked on links in these suspicious emails.
-
Historical IP Analysis:
- The query examines historical sign-in logs to determine the IP addresses from which users have previously signed in successfully.
- It counts how often each IP address has been used by the user in the past.
-
Detecting Suspicious Sign-ins:
- It looks for sign-in attempts after the user clicked on a phishing link.
- The query excludes certain applications from the analysis.
- It classifies IP addresses as "High Risk" if they are new, "Medium Risk" if they are infrequently used, and "Low Risk" if they are commonly used.
-
Final Reporting:
- The query aggregates data to report on suspicious sign-ins, including details like the user's identity, IP address, location, risk classification, and the phishing email details.
- It filters the results to show only those sign-ins from rarely used IP addresses.
Overall, this query helps identify potentially compromised accounts by correlating phishing email interactions with unusual sign-in activities.
Details

Benjamin Zulliger
Released: January 15, 2026
Tables
Keywords
Operators
MITRE Techniques