In this section
DE3.2 Phishing Detection Beyond Safe Links
Section 3.1 mapped the NE initial access attack surface and built the detection requirement matrix. DE3-001 targets T1566.002, phishing via link, and addresses the architectural blind spot where Safe Links evaluates URL reputation at delivery and click time but cannot detect time-bombed URLs that activate after the reputation check completes. This section builds the complete production rule.
Scenario
Rachel reconstructs the CHAIN-HARVEST entry point. The phishing email arrived at 09:23, carrying a URL to a compromised WordPress site. Safe Links approved it, clean domain, no sandbox detonation flags. At 11:00, the attacker activated the conditional redirect. At 12:47, s.chen clicked the link. Safe Links re-evaluated the rewritten URL and allowed the click: the redirect served the clean page to automated scanners and the AiTM proxy only to real browsers. By 12:48, s.chen's session token was captured. Rachel's question for the SOC: what signal exists between 12:47 and 12:48 that distinguishes this click from the 400 legitimate email link clicks that happened the same hour?
The architectural blind spot
Safe Links performs two checks. At delivery, it rewrites the URL through Microsoft's detonation sandbox and compares the destination against a reputation database of known-malicious domains. At click time, it re-evaluates the rewritten URL before the user's browser follows the redirect. Both checks rely on the same underlying mechanism: URL reputation. If the URL points to a domain that Microsoft hasn't classified as malicious, both checks pass.
AiTM phishing kits exploit this by creating a clean domain days before the campaign launch. The domain hosts a legitimate-looking page, often a cloned Microsoft 365 sign-in or a simple document preview. At delivery, Safe Links detonates the URL and finds a clean page. Hours later, the attacker enables a conditional redirect: automated scanners (identified by user agent, IP range, or request headers) still see the clean page, but real browsers are redirected to the AiTM proxy infrastructure. The user clicks, Safe Links re-checks and sees the clean page, but the user's browser follows the redirect to the proxy.
This is not a Safe Links failure, it's an architectural limitation. Safe Links evaluates what the URL points to. Time-bombed URLs point to different things at different times. No reputation-based system can catch a URL that is genuinely clean at evaluation time and genuinely malicious at click time, because the URL's behavior changes between the two events.
DE3-001 addresses this by shifting the detection point. Instead of evaluating the URL's reputation, the rule evaluates what happens after the click. The detection hypothesis: if a user clicks a URL from an inbound email and within 60 minutes has a sign-in from a new IP address, a new device, or a new location, the click likely led to credential capture. The URL might be clean, but the post-click behavior is not.
Figure DE3.2a. DE3-001 detection architecture. The rule correlates two independent data sources. UrlClickEvents and SigninLogs, using AccountUpn as the join key and a 60-minute correlation window.
Understanding UrlClickEvents
The UrlClickEvents table is populated by Defender for Office 365 and records every URL click processed by Safe Links, from email, Teams messages, and Office 365 apps. Three columns matter for DE3-001.
ActionType records the Safe Links verdict. ClickAllowed means Safe Links permitted the click, filter on this because you're looking for clicks that were approved but led to credential theft. ClickBlocked means Safe Links stopped it. For DE3-001, only allowed clicks matter.
IsClickedThrough records whether the user clicked through a warning page. Users who click through warnings on URLs from external senders are either ignoring security training or have been socially engineered, this is a high-value signal.
UrlChain records the full redirect chain. The UrlChainId groups all URLs in the same chain, and UrlChainPosition records each hop, position 0 is the original URL, higher positions are redirects. AiTM kits typically produce 2–3 hop chains through filtering domains.
Run this query against your Defender XDR advanced hunting to see how many allowed clicks involved redirect chains in the last seven days:
// Allowed clicks with redirect chains — baseline your environment
UrlClickEvents
| where Timestamp > ago(7d)
| where ActionType == "ClickAllowed"
| extend ChainLength = array_length(todynamic(UrlChain))
| summarize
TotalClicks = count(),
WithRedirects = countif(ChainLength > 1),
MaxChainLength = max(ChainLength),
ClickedThrough = countif(IsClickedThrough == 1)
| extend RedirectPct = round(WithRedirects * 100.0 / TotalClicks, 1)
In most M365 environments, 15–30% of allowed clicks involve at least one redirect. Marketing emails, newsletter tracking, and URL shorteners all produce chains. DE3-001's challenge isn't detecting redirects, it's distinguishing phishing redirects from legitimate ones through the post-click correlation.
Building the correlation query
DE3-001 joins two independent data sources. The left side is UrlClickEvents, filtered to allowed clicks from inbound emails. The right side is SigninLogs, filtered to successful authentications from new infrastructure. The join key is the user's UPN, and the correlation window is 60 minutes between click and sign-in.
The 60-minute window reflects the AiTM attack timeline. The user clicks the link, enters credentials on the proxy page (typically within 2–5 minutes), and the attacker uses the captured session token to authenticate (typically within 5–30 minutes of capture). A 60-minute window catches the vast majority of AiTM sequences while limiting the false positive surface. Extending to 120 minutes catches slower attacks but doubles the correlation volume.
// DE3-001: Phishing click-through with post-click auth anomaly
// Hypothesis: AiTM phishing produces allowed clicks followed by
// sign-ins from new infrastructure within 60 minutes
let CorrelationWindow = 60m;
let LookbackPeriod = 14d;
// Step 1: Identify allowed clicks from inbound emails
let SuspiciousClicks =
UrlClickEvents
| where Timestamp > ago(1h)
| where ActionType == "ClickAllowed"
| where Workload == "Email"
| extend ChainLength = array_length(todynamic(UrlChain))
| project ClickTime = Timestamp, AccountUpn, Url,
UrlChain, ChainLength, IsClickedThrough,
NetworkMessageId;
// Step 2: Build per-user baseline of known sign-in infrastructure
let KnownInfra =
SigninLogs
| where TimeGenerated > ago(LookbackPeriod)
| where ResultType == 0
| summarize
KnownIPs = make_set(IPAddress, 100),
KnownDevices = make_set(DeviceDetail.deviceId, 50),
KnownCountries = make_set(LocationDetails.countryOrRegion, 20)
by UserPrincipalName;
// Step 3: Find post-click sign-ins from new infrastructure
SuspiciousClicks
| join kind=inner (
SigninLogs
| where TimeGenerated > ago(2h)
| where ResultType == 0
| project SignInTime = TimeGenerated, UserPrincipalName,
SignInIP = IPAddress, SignInApp = AppDisplayName,
SignInDevice = DeviceDetail.deviceId,
SignInCountry = LocationDetails.countryOrRegion,
AuthMethod = AuthenticationDetails
) on $left.AccountUpn == $right.UserPrincipalName
| where SignInTime between (ClickTime .. ClickTime + CorrelationWindow)
// Step 4: Filter to sign-ins from previously unseen infrastructure
| join kind=leftouter KnownInfra
on $left.AccountUpn == $right.UserPrincipalName
| where KnownIPs !has SignInIP
or KnownCountries !has tostring(SignInCountry)
| project
ClickTime, AccountUpn, Url, ChainLength,
IsClickedThrough, SignInTime, SignInIP,
SignInCountry, SignInApp, SignInDevice,
TimeDelta = SignInTime - ClickTime,
NetworkMessageId
Walk through the architecture. Step 1 collects allowed clicks from email, filtering on Workload == "Email" excludes Teams and Office app clicks with different FP profiles. Step 2 builds a per-user baseline of known sign-in infrastructure over 14 days, using make_set to collect unique IPs, devices, and countries. Fourteen days captures most legitimate variation while remaining short enough that an attacker who compromised the account two weeks ago hasn't poisoned the baseline. Step 3 correlates clicks with sign-ins on UPN and the 60-minute window. Step 4 filters to sign-ins from infrastructure the user hasn't used before: the behavioral signal that distinguishes AiTM capture from legitimate link clicks.
Anti-Pattern
"Safe Links provides complete phishing protection, we passed our security audit." Compliance frameworks assess whether Safe Links is enabled and configured, not whether it detects every phishing variant. AiTM kits that use time-bombed URLs pass Safe Links evaluation by design. Your audit passed because Safe Links is correctly configured. Your users remain vulnerable because correctly configured Safe Links has an architectural blind spot that custom detection rules address.
Enriching with email context
The raw correlation query identifies click-to-credential sequences, but the SOC analyst triaging the alert needs email context to assess severity. Join the EmailEvents table using NetworkMessageId to add sender information, subject line, and delivery details.
// Enrich DE3-001 alerts with sender and delivery context
// Append to the main query output via NetworkMessageId join
| join kind=leftouter (
EmailEvents
| where EmailDirection == "Inbound"
| project NetworkMessageId,
SenderFromAddress, SenderIPv4,
Subject, DeliveryAction,
AuthenticationDetails
) on NetworkMessageId
| extend SenderDomain = tostring(
split(SenderFromAddress, "@")[1])
| project
ClickTime, AccountUpn, Subject,
SenderFromAddress, SenderDomain,
Url, ChainLength, IsClickedThrough,
SignInTime, SignInIP, SignInCountry,
SignInApp, TimeDelta
The enrichment adds three triage accelerators. SenderFromAddress lets the analyst check whether the sender domain is a known partner, a look-alike, or a compromised account. The Subject line reveals social engineering patterns — "Action Required: Review Document" and "Your password expires today" increase analyst confidence. AuthenticationDetails from EmailEvents shows SPF, DKIM, and DMARC results, phishing from compromised accounts passes all three, while look-alike domains fail DMARC.
QR code phishing variant
DE3-001's correlation logic applies to QR code phishing. Instead of a clickable URL, the attacker embeds a QR code in an image or PDF attachment. The user scans it with their phone, bypassing Safe Links entirely because the click happens on a mobile device outside the Defender for Office 365 evaluation path.
The EmailUrlInfo table captures QR code URLs through the UrlLocation column. When UrlLocation == "QRCode", Defender has extracted a URL from a QR code in the email. This extraction happens at delivery. Defender scans images and PDFs for QR codes, but doesn't block them through Safe Links because the user doesn't click within the email client.
// QR code phishing — emails with embedded QR URLs
// and urgency-themed subject lines
let UrgencyKeywords = dynamic([
"authenticate", "verify", "confirm",
"password", "expire", "urgent",
"reset", "suspend", "QR"]);
EmailEvents
| where Timestamp > ago(1d)
| where EmailDirection == "Inbound"
| where DeliveryAction == "Delivered"
| where Subject has_any (UrgencyKeywords)
| join kind=inner (
EmailUrlInfo
| where UrlLocation == "QRCode"
) on NetworkMessageId
| project
Timestamp, SenderFromAddress, Subject,
RecipientEmailAddress, Url, UrlDomain,
NetworkMessageId
This hunting query identifies inbound emails with QR code URLs and urgency-themed subjects. It won't work as a production rule because UrlClickEvents doesn't capture phone-based QR scans. Instead, pair it with a modified DE3-001 that correlates QR-bearing email receipt with sign-in anomalies from mobile devices within 30 minutes. The looser correlation produces more false positives, deploy as a hunting query first and promote only after measuring FP rate.
The 12-section specification
Before reading the full specification below, pause and write your own version of the hypothesis statement for DE3-001. Your hypothesis should answer three questions: what behavioral pattern does this rule detect, what data sources provide the evidence, and why does this pattern indicate compromise rather than legitimate activity? Compare your version to the specification: the hypothesis drives every other decision in the rule.
The complete rule specification follows the 12-section format from DE1. Each section is annotated with the reasoning behind the design decision, not just the value.
1. Rule name and ID: DE3-001-Phishing-Click-Auth-Anomaly. The name encodes the detection logic, click followed by authentication anomaly, not the attack name. An analyst reading the alert title should understand the detection signal without opening the rule definition.
2. Hypothesis: "Users who click allowed URLs from inbound emails and subsequently authenticate from previously unseen infrastructure within 60 minutes have likely had their credentials captured by an AiTM phishing proxy. The click-to-new-infrastructure correlation distinguishes credential theft from legitimate link clicks because legitimate clicks do not produce authentication from new IP addresses."
3. MITRE ATT&CK: T1566.002 (Phishing: Spearphishing Link). Sub-technique is critical, this rule targets link-based phishing specifically, not attachment phishing (T1566.001, covered in DE3-008) or service-based phishing (T1566.003).
4. Data sources: Primary: UrlClickEvents, SigninLogs. Enrichment: EmailEvents. The rule requires Defender for Office 365 P2 (for UrlClickEvents) and Entra ID P1 (for SigninLogs with location data). Without both licenses, the correlation breaks.
5. KQL query: The annotated production query above. Deploy in Defender XDR custom detection or Sentinel analytics: both support the same KQL syntax for these tables.
6. Entity mapping: Map AccountUpn to Account, SignInIP to IP, and NetworkMessageId to MailMessage. These mappings drive the incident graph so the analyst sees user, IP, and email connected in one view.
7. Frequency and lookback: Every 1 hour with a 2-hour lookback. The 1-hour overlap ensures click-to-sign-in sequences that span a frequency boundary aren't missed.
8. Severity and confidence: High severity. Two-source behavioral correlation with baseline comparison should produce a low enough false positive rate. If your environment exceeds 5 false positives per week, reduce to Medium and extend the baseline to 30 days.
9. Trigger logic and grouping: Group by AccountUpn with a 1-hour grouping window. Campaign targeting that hits the same user multiple times produces a single incident with multiple alerts.
12. Tuning plan: Week 1, deploy as Informational to measure FP volume. Week 2, add VPN exclusions, adjust baseline window. Week 3, promote to High if under 5 FPs per week. Month 2, extend to Teams clicks. Quarter 2, add QR code variant as a separate Medium-severity rule.
Testing DE3-001 in your environment
Run the baseline query (first KQL block above) in your Defender XDR advanced hunting. Record your environment's total allowed clicks, redirect percentage, and click-through count for the last 7 days. Then run the full DE3-001 correlation query with a 24-hour lookback instead of 1 hour. If you get results, examine the TimeDelta column, true positives typically show 3–15 minutes between click and sign-in. False positives from VPN rotation typically show 30–55 minutes. That timing pattern helps you tune the correlation window for your environment.
Anti-Pattern
Some teams respond to phishing bypasses by building blocklists of known phishing domains. This creates a detection rule that catches yesterday's attack. AiTM kits register new domains for every campaign: the domain that hit you last week will never be used again. DE3-001 works because it detects the behavioral consequence of phishing (credential capture producing a new-infrastructure sign-in), not the indicator (the URL). Behavioral rules survive attacker infrastructure rotation. Indicator-based rules expire with every campaign.
Redirect chain analysis for threat hunting
Beyond the production rule, redirect chain data in UrlClickEvents supports periodic threat hunting. AiTM kits produce distinctive patterns: the initial URL redirects to a filtering domain, then to the AiTM proxy. Marketing chains stay within the same organization's domains (sendgrid.net → company.com), while phishing chains cross unrelated domains (compromised-wordpress.com → random-filter.xyz → aitm-proxy.com).
// Hunt for multi-hop redirect chains with cross-domain hops
UrlClickEvents
| where Timestamp > ago(7d)
| where ActionType == "ClickAllowed"
| extend Chain = todynamic(UrlChain)
| where array_length(Chain) > 2
| mv-expand ChainUrl = Chain
| extend HopDomain = tostring(
parse_url(tostring(ChainUrl)).Host)
| summarize
UniqueHopDomains = dcount(HopDomain),
HopDomainList = make_set(HopDomain)
by Url, AccountUpn, NetworkMessageId
| where UniqueHopDomains > 2
| sort by UniqueHopDomains desc
Run this weekly as a threat hunt. Chains with three or more unique, unrelated domains warrant manual investigation. Cross-reference hop domains against threat intelligence feeds and newly registered domain lists: a chain passing through a domain registered in the last 72 hours that terminates at an authentication page is almost certainly a phishing kit.
# Revoke sessions for users flagged by DE3-001
# Run immediately upon confirmed true positive
$AffectedUsers = @("s.chen@northgate-eng.com")
foreach ($User in $AffectedUsers) {
# Revoke all refresh tokens — forces re-authentication
Revoke-MgUserSignInSession -UserId $User
# Reset password to invalidate captured credentials
$NewPassword = [System.Web.Security.Membership]::GeneratePassword(16, 4)
Update-MgUser -UserId $User `
-PasswordProfile @{
Password = $NewPassword
ForceChangePasswordNextSignIn = $true
}
Write-Host "Revoked sessions and reset password for $User"
}
Session revocation alone is insufficient if the attacker has already established persistence. CHAIN-HARVEST's attacker created a forwarding rule within 5 minutes of gaining access, revoking the session stops the active connection, but checking for inbox rules stops the backdoor.