SOCLIFE
SOCLIFE

Evidence-led security analysis

Published knowledge

Search SOC//LIFE

DetectionsDET-003

Behavior-based detection engineering

Phishing Click Followed by Suspicious Cloud Session Use

Correlates email-origin URL clicks with materially different Entra sign-ins and independent identity or device-risk signals.

Behavior

What it detects

An email-origin URL click is followed by a successful browser sign-in for the same identity inside a short window, and the resulting session shows multiple independent indicators such as source change, a phishing verdict, Entra risk, or unmanaged/non-compliant device context.

Engineering decision

Why this detection

AiTM phishing can succeed through a legitimate authentication flow, so a successful MFA event does not automatically make the resulting session trustworthy.

This analytic keeps the primary detection narrow: correlate the user’s email-origin click with the nearest successful browser sign-in inside thirty minutes, then require several independent source, phishing, identity-risk, or device-trust signals. A different public IP is useful context, but it is never sufficient on its own.

The drilldowns deliberately move from the candidate into message provenance, sign-in history, shared infrastructure, and mailbox activity so an analyst can distinguish session theft from normal VPN, proxy, mobile, VDI, or roaming behavior.

Signal chain

Detection logic

  1. Email-origin URL click
  2. Same normalized user identity
  3. Successful browser sign-in within thirty minutes
  4. Material source or trust-context difference
  5. Independent phishing, identity-risk, or device-trust signal
  6. Candidate for mailbox and cloud-activity drilldown

Primary analytic

Query

KQL and SPL express the same analytical intent using source-specific schemas.
Q-01Detection logic

Correlate phishing clicks with suspicious cloud sessions

What this checks

Generate high-value candidates where an email click is followed by a successful browser sign-in and multiple independent source, phishing, identity-risk, or device-trust signals.

KQL
let lookback = 2h;
let correlation_window = 30m;

let clicks =
    UrlClickEvents
    | where Timestamp >= ago(lookback)
    | where Workload == "Email"
    | where isnotempty(AccountUpn)
    | where ActionType == "ClickAllowed"
        or IsClickedThrough == true
        or ThreatTypes has "Phish"
    | project
        ClickTime = Timestamp,
        AccountUpn = tolower(AccountUpn),
        ClickIP = IPAddress,
        ClickUrl = Url,
        UrlChain,
        ClickAction = ActionType,
        IsClickedThrough,
        ClickThreatTypes = ThreatTypes,
        NetworkMessageId;

let signins =
    EntraIdSignInEvents
    | where Timestamp >= ago(lookback)
    | where ErrorCode == 0
    | where ClientAppUsed == "Browser"
    | where isnotempty(AccountUpn)
    | project
        SignInTime = Timestamp,
        AccountUpn = tolower(AccountUpn),
        SignInIP = IPAddress,
        Application,
        ResourceDisplayName,
        Country,
        State,
        City,
        UserAgent,
        Browser,
        DeviceName,
        DeviceTrustType,
        IsManaged,
        IsCompliant,
        AuthenticationRequirement,
        RiskLevelDuringSignIn,
        RiskLevelAggregated,
        RiskEventTypes,
        RiskState,
        SessionId,
        UniqueTokenId,
        GatewayJA4;

clicks
| join kind=inner signins on AccountUpn
| where SignInTime between (ClickTime .. ClickTime + correlation_window)
| extend TimeDelta = SignInTime - ClickTime
| summarize arg_min(TimeDelta, *) by AccountUpn, SignInTime, SignInIP
| extend
    SourceChanged =
        isnotempty(ClickIP)
        and isnotempty(SignInIP)
        and ClickIP != SignInIP,
    ClickThreatSignal =
        ClickThreatTypes has "Phish",
    RiskSignal =
        RiskLevelDuringSignIn in (10, 50, 100)
        or RiskLevelAggregated in (10, 50, 100)
        or RiskState in (4, 5)
        or isnotempty(RiskEventTypes),
    DeviceTrustSignal =
        IsManaged == 0
        or IsCompliant == 0
| extend SignalScore =
    iif(SourceChanged, 1, 0)
    + iif(ClickThreatSignal, 1, 0)
    + (2 * iif(RiskSignal, 1, 0))
    + iif(DeviceTrustSignal, 1, 0)
| where SignalScore >= 3
| project
    ClickTime,
    SignInTime,
    TimeDelta,
    AccountUpn,
    SignalScore,
    SourceChanged,
    ClickThreatSignal,
    RiskSignal,
    DeviceTrustSignal,
    ClickIP,
    SignInIP,
    ClickUrl,
    UrlChain,
    ClickThreatTypes,
    NetworkMessageId,
    Application,
    ResourceDisplayName,
    Country,
    State,
    City,
    Browser,
    UserAgent,
    DeviceName,
    DeviceTrustType,
    IsManaged,
    IsCompliant,
    AuthenticationRequirement,
    RiskLevelDuringSignIn,
    RiskLevelAggregated,
    RiskEventTypes,
    RiskState,
    SessionId,
    UniqueTokenId,
    GatewayJA4
| order by SignalScore desc, SignInTime desc
SPL
| multisearch
    [ search index=<m365_security_index> sourcetype=<url_click_events_sourcetype> earliest=-2h
      | eval stage="click"
    ]
    [ search index=<identity_index> sourcetype=<entra_signin_sourcetype> earliest=-2h
      | eval stage="signin"
    ]
| eval
    user=lower(coalesce(user, AccountUpn, user_principal_name)),
    src=coalesce(src, IPAddress, ip_address),
    click_url=if(stage="click", coalesce(url, Url), null()),
    url_chain=if(stage="click", coalesce(url_chain, UrlChain), null()),
    workload=if(stage="click", coalesce(workload, Workload), null()),
    click_action=if(stage="click", coalesce(action, ActionType), null()),
    click_threat_types=if(stage="click", coalesce(threat_types, ThreatTypes), null()),
    clicked_through=if(stage="click", coalesce(IsClickedThrough, is_clicked_through), null()),
    error_code=if(stage="signin", tonumber(coalesce(ErrorCode, error_code)), null()),
    auth_action=if(stage="signin", coalesce(action, auth_action), null()),
    risk_during=if(stage="signin", tonumber(coalesce(RiskLevelDuringSignIn, risk_level_during)), null()),
    risk_aggregated=if(stage="signin", tonumber(coalesce(RiskLevelAggregated, risk_level_aggregated)), null()),
    risk_state=if(stage="signin", tonumber(coalesce(RiskState, risk_state)), null()),
    risk_events=if(stage="signin", coalesce(RiskEventTypes, risk_event_types), null()),
    is_managed=if(stage="signin", tonumber(coalesce(IsManaged, is_managed)), null()),
    is_compliant=if(stage="signin", tonumber(coalesce(IsCompliant, is_compliant)), null()),
    device_trust=if(stage="signin", coalesce(DeviceTrustType, device_trust_type), null()),
    app=if(stage="signin", coalesce(Application, ResourceDisplayName, app), null()),
    session_id=if(stage="signin", coalesce(SessionId, session_id), null()),
    user_agent=if(stage="signin", coalesce(UserAgent, user_agent), null())
| where isnotnull(user)
| where
    (
        stage="click"
        AND lower(workload)="email"
        AND (
            click_action="ClickAllowed"
            OR clicked_through=1
            OR like(lower(click_threat_types), "%phish%")
        )
    )
    OR
    (
        stage="signin"
        AND (auth_action="success" OR error_code=0)
    )
| sort 0 user _time
| streamstats current=f
    last(eval(if(stage="click", _time, null()))) as click_time
    last(eval(if(stage="click", src, null()))) as click_src
    last(eval(if(stage="click", click_url, null()))) as click_url
    last(eval(if(stage="click", url_chain, null()))) as url_chain
    last(eval(if(stage="click", click_threat_types, null()))) as click_threat_types
    by user
| where stage="signin"
    AND isnotnull(click_time)
    AND _time>=click_time
    AND _time<=click_time+1800
| eval
    source_changed=if(isnotnull(click_src) AND isnotnull(src) AND click_src!=src,1,0),
    click_threat_signal=if(like(lower(click_threat_types), "%phish%"),1,0),
    risk_signal=if(
        risk_during IN (10,50,100)
        OR risk_aggregated IN (10,50,100)
        OR risk_state IN (4,5)
        OR isnotnull(risk_events),
        1,0
    ),
    device_trust_signal=if(is_managed=0 OR is_compliant=0,1,0),
    signal_score=source_changed+click_threat_signal+(2*risk_signal)+device_trust_signal,
    delta_seconds=_time-click_time
| where signal_score>=3
| table
    click_time _time delta_seconds user signal_score
    source_changed click_threat_signal risk_signal device_trust_signal
    click_src src click_url url_chain click_threat_types
    app session_id user_agent
    risk_during risk_aggregated risk_state risk_events
    is_managed is_compliant device_trust
| sort - signal_score - _time

What to look for

A post-click browser session where several independent signals converge; the result is a candidate for analyst investigation, not automatic proof of AiTM.

Technical details

Tested signal

Same-user email click followed by successful browser authentication inside thirty minutes with a combined confidence score.

Assumptions

  • Safe Links / UrlClickEvents and EntraIdSignInEvents are available with comparable timestamps and normalized UPNs.
  • Expected VPN, secure web gateway, VDI, mobile, roaming, and approved device transitions are understood before production tuning.
  • Risk and device fields can be absent in some environments; missing fields must not be silently treated as malicious.

Data requirements and relevant fields

network

Email-origin URL clicks with message, URL, source, verdict, and user context.

  • Timestamp
  • Url
  • ActionType
  • AccountUpn
  • Workload
  • NetworkMessageId
  • ThreatTypes
  • IPAddress
  • IsClickedThrough
  • UrlChain
authentication

Successful browser sign-ins with identity, source, risk, device-trust, and session context.

  • Timestamp
  • Application
  • ErrorCode
  • SessionId
  • AccountUpn
  • ResourceDisplayName
  • DeviceName
  • DeviceTrustType
  • IsManaged
  • IsCompliant
  • AuthenticationRequirement
  • RiskLevelAggregated
  • RiskLevelDuringSignIn
  • RiskEventTypes
  • RiskState
  • UserAgent
  • ClientAppUsed
  • Browser
  • IPAddress
  • Country
  • State
  • City
  • GatewayJA4
  • UniqueTokenId
KQL schema

Uses UrlClickEvents and EntraIdSignInEvents. Defender for Office 365 and Microsoft Entra ID P2 telemetry are required.

SPL schema

Replace both index/sourcetype placeholders. Map local click and Entra fields before applying the score, and add a trusted-egress lookup for known VPN/SWG/VDI transitions.

Limitations

  • A different IP alone is insufficient because legitimate network transitions are common.
  • Entra risk can be unset even during malicious activity, so the analytic can miss low-signal session theft.
  • Safe Links may not preserve every user click or correlate Draft/Sent clicks by NetworkMessageId.
  • A short correlation window can miss delayed replay and should be complemented by wider hunting.

KQL uses native Defender XDR and Entra tables. SPL intentionally uses raw normalized vendor scaffolds because Safe Links message IDs, Entra risk, and device-trust fields are not fully represented by CIM.

Analyst workflow

What the analyst should look for

  • Did the user click the message URL?
  • Was the post-click sign-in source expected for this user?
  • Is the source change explained by VPN, secure web gateway, VDI, mobile, or roaming?
  • Was the browser or device managed and compliant?
  • What Entra risk signals were present?
  • Did MFA succeed inside the suspicious sequence?
  • What mailbox or cloud actions followed the sign-in?
  • Is the same suspicious IP used by other identities?
  • Does the same URL or redirect chain appear for other recipients?

Expected result

A post-click browser session where several independent signals converge; the result is a candidate for analyst investigation, not automatic proof of AiTM.

Investigation pivots

Drilldowns

Use the candidate context to reconstruct what executed, what changed, and what communicated next.
View query — Reconstruct the phishing message
Q-02Drilldown

Reconstruct the phishing message

What this checks

Resolve the source message, sender, recipients, delivery state, and threat context from the NetworkMessageId returned by the candidate.

KQL
let target_message_id = "<NetworkMessageId>";
EmailEvents
| where Timestamp >= ago(7d)
| where NetworkMessageId == target_message_id
| project
    Timestamp,
    NetworkMessageId,
    InternetMessageId,
    SenderFromAddress,
    SenderMailFromAddress,
    SenderDisplayName,
    SenderFromDomain,
    SenderIPv4,
    RecipientEmailAddress,
    Subject,
    EmailDirection,
    DeliveryAction,
    DeliveryLocation,
    LatestDeliveryAction,
    LatestDeliveryLocation,
    ThreatTypes,
    AuthenticationDetails,
    UrlCount,
    AttachmentCount
| order by Timestamp asc
SPL
index=<m365_email_index> sourcetype=<email_events_sourcetype>
earliest=-7d
NetworkMessageId="<NetworkMessageId>"
| eval
    message_id=coalesce(message_id, NetworkMessageId),
    sender=lower(coalesce(sender, SenderFromAddress, SenderMailFromAddress)),
    recipient=lower(coalesce(recipient, RecipientEmailAddress)),
    subject=coalesce(subject, Subject),
    delivery_action=coalesce(delivery_action, LatestDeliveryAction, DeliveryAction),
    delivery_location=coalesce(delivery_location, LatestDeliveryLocation, DeliveryLocation),
    threat_types=coalesce(threat_types, ThreatTypes)
| fields _time message_id sender recipient subject delivery_action delivery_location threat_types
| sort 0 _time

What to look for

The exact source message plus additional recipients or repeated subjects that justify campaign scoping.

Technical details

Tested signal

Message-level evidence for the exact click candidate.

Assumptions

  • The candidate retains NetworkMessageId.
  • Message telemetry is retained for the requested lookback.

Data requirements and relevant fields

email

Message-level sender, recipient, delivery, and threat fields keyed by NetworkMessageId.

  • Timestamp
  • NetworkMessageId
  • InternetMessageId
  • SenderFromAddress
  • SenderMailFromAddress
  • SenderDisplayName
  • SenderFromDomain
  • SenderIPv4
  • RecipientEmailAddress
  • Subject
  • EmailDirection
  • DeliveryAction
  • DeliveryLocation
  • LatestDeliveryAction
  • LatestDeliveryLocation
  • ThreatTypes
  • AuthenticationDetails
  • UrlCount
  • AttachmentCount
KQL schema

Uses documented EmailEvents fields.

SPL schema

Replace the mail index/sourcetype and map message, sender, recipient, delivery, and verdict fields.

Limitations

  • Some mail sources do not retain NetworkMessageId consistently.
  • Sender trust or authentication success does not prove the sender account was uncompromised.

Both variants require message-level identifiers from the local mail-security source.

View query — Rebuild the post-click sign-in timeline
Q-03Drilldown

Rebuild the post-click sign-in timeline

What this checks

Review authentication around the candidate click and sign-in with source, browser, device-trust, risk, and session context.

KQL
let target_user = "<user@domain>";
let pivot_time = datetime(<YYYY-MM-DDTHH:MM:SSZ>);
EntraIdSignInEvents
| where Timestamp between (pivot_time - 30m .. pivot_time + 2h)
| where AccountUpn =~ target_user
| project
    Timestamp,
    AccountUpn,
    ErrorCode,
    Application,
    ResourceDisplayName,
    IPAddress,
    Country,
    State,
    City,
    Browser,
    UserAgent,
    DeviceName,
    DeviceTrustType,
    IsManaged,
    IsCompliant,
    AuthenticationRequirement,
    RiskLevelDuringSignIn,
    RiskLevelAggregated,
    RiskEventTypes,
    RiskState,
    SessionId,
    UniqueTokenId,
    GatewayJA4
| order by Timestamp asc
SPL
index=<identity_index> sourcetype=<entra_signin_sourcetype>
earliest=<pivot_minus_30m> latest=<pivot_plus_2h>
| eval
    user=lower(coalesce(user, AccountUpn, user_principal_name)),
    src=coalesce(src, IPAddress, ip_address),
    app=coalesce(app, Application, ResourceDisplayName),
    browser=coalesce(browser, Browser),
    user_agent=coalesce(user_agent, UserAgent),
    device=coalesce(device, DeviceName),
    session_id=coalesce(session_id, SessionId),
    error_code=tonumber(coalesce(ErrorCode, error_code)),
    risk_level=tonumber(coalesce(RiskLevelDuringSignIn, RiskLevelAggregated, risk_level)),
    risk_state=tonumber(coalesce(RiskState, risk_state)),
    is_managed=tonumber(coalesce(IsManaged, is_managed)),
    is_compliant=tonumber(coalesce(IsCompliant, is_compliant))
| where user="<user@domain>"
| fields
    _time user src app browser user_agent device session_id
    error_code risk_level risk_state is_managed is_compliant
| sort 0 _time

What to look for

A coherent post-click timeline showing whether the suspicious session differs from nearby expected sessions.

Technical details

Tested signal

Identity-session context around the candidate time.

Assumptions

  • The candidate provides a target user and pivot time.
  • Entra sign-in timestamps are comparable with click telemetry.

Data requirements and relevant fields

authentication

Sign-in events around a selected user and pivot time.

  • Timestamp
  • Application
  • ErrorCode
  • SessionId
  • AccountUpn
  • ResourceDisplayName
  • DeviceName
  • DeviceTrustType
  • IsManaged
  • IsCompliant
  • AuthenticationRequirement
  • RiskLevelAggregated
  • RiskLevelDuringSignIn
  • RiskEventTypes
  • RiskState
  • UserAgent
  • Browser
  • IPAddress
  • Country
  • State
  • City
  • GatewayJA4
  • UniqueTokenId
KQL schema

Uses EntraIdSignInEvents and a bounded candidate-centered time window.

SPL schema

Replace time placeholders with the local candidate window and map Entra fields to the normalized aliases.

Limitations

  • The placeholder pivot time must be replaced with the candidate timestamp.
  • IP geolocation and device names can be incomplete or misleading in brokered cloud access.

This is an investigation query. Replace target_user and pivot_time with values returned by the detection.

View query — Find other identities using the suspicious sign-in IP
Q-04Drilldown

Find other identities using the suspicious sign-in IP

What this checks

Determine whether the candidate source is isolated to one identity or appears across multiple cloud accounts.

KQL
let suspicious_ip = "<suspicious_signin_ip>";
EntraIdSignInEvents
| where Timestamp >= ago(7d)
| where IPAddress == suspicious_ip
| summarize
    FirstSeen=min(Timestamp),
    LastSeen=max(Timestamp),
    SignIns=count(),
    Users=make_set(AccountUpn, 100),
    Applications=make_set(ResourceDisplayName, 30),
    Countries=make_set(Country, 20),
    RiskEvents=make_set(RiskEventTypes, 30)
    by IPAddress
SPL
index=<identity_index> sourcetype=<entra_signin_sourcetype>
earliest=-7d
| eval
    user=lower(coalesce(user, AccountUpn, user_principal_name)),
    src=coalesce(src, IPAddress, ip_address),
    app=coalesce(app, Application, ResourceDisplayName),
    risk=coalesce(RiskEventTypes, risk_event_types)
| where src="<suspicious_signin_ip>"
| stats
    min(_time) as first_seen
    max(_time) as last_seen
    count as signins
    dc(user) as unique_users
    values(user) as users
    values(app) as applications
    values(risk) as risk_events
    by src
| convert ctime(first_seen) ctime(last_seen)

What to look for

Several identities using the same unexplained source address near the campaign window.

Technical details

Tested signal

Multiple identity sign-ins from the same suspicious source address.

Assumptions

  • The suspicious IP is taken from the detection candidate rather than guessed.
  • Shared corporate egress and trusted identity providers are understood before escalation.

Data requirements and relevant fields

authentication

Sign-in activity keyed by source IP with user, application, country, and risk context.

  • Timestamp
  • AccountUpn
  • ResourceDisplayName
  • IPAddress
  • Country
  • RiskEventTypes
KQL schema

Uses documented EntraIdSignInEvents source, account, application, country, and risk fields.

SPL schema

Map the local Entra source to user, src, app, and risk before grouping.

Limitations

  • NAT, VPN, proxy, and cloud identity infrastructure can legitimately place many users behind one IP.
  • A public IP can change ownership over time.

This is an IOC-free drilldown driven by the current candidate's source IP.

View query — Review mailbox actions after the suspicious session
Q-05Drilldown

Review mailbox actions after the suspicious session

What this checks

Review cloud and Exchange Online activity for the affected identity after the candidate session.

KQL
let target_user = "<user@domain>";
let pivot_time = datetime(<YYYY-MM-DDTHH:MM:SSZ>);
CloudAppEvents
| where Timestamp between (pivot_time .. pivot_time + 4h)
| where AccountId =~ target_user
| project
    Timestamp,
    AccountId,
    Application,
    ActionType,
    ActivityType,
    IPAddress,
    CountryCode,
    City,
    Isp,
    UserAgent,
    ObjectName,
    ObjectType,
    UncommonForUser,
    RawEventData
| order by Timestamp asc
SPL
index=<m365_audit_index> sourcetype=<m365_audit_sourcetype>
earliest=<pivot_time> latest=<pivot_plus_4h>
| eval
    user=lower(coalesce(user, UserId, AccountId, account_id)),
    action=coalesce(action, Operation, ActionType),
    src=coalesce(src, ClientIP, IPAddress, src_ip),
    object=coalesce(object, ObjectId, ObjectName)
| where user="<user@domain>"
| fields _time user src action object Workload Application Parameters
| sort 0 _time

What to look for

Unexpected mailbox-rule changes, message operations, sending, authentication changes, or other cloud actions aligned with the suspicious source/session.

Technical details

Tested signal

Mailbox or cloud operations occurring after suspicious authentication.

Assumptions

  • CloudAppEvents or equivalent Microsoft 365 audit data is available.
  • The candidate provides a user and pivot time.

Data requirements and relevant fields

saas audit

Post-authentication Microsoft 365 activity with user, action, source, object, uncommon-user context, and raw details.

  • Timestamp
  • ActionType
  • Application
  • AccountId
  • IPAddress
  • CountryCode
  • City
  • Isp
  • UserAgent
  • ActivityType
  • ObjectName
  • ObjectType
  • RawEventData
  • UncommonForUser
KQL schema

Uses CloudAppEvents and a four-hour candidate-centered investigation window.

SPL schema

Map the Microsoft 365 audit source to user, action, src, object, workload/application, and parameters.

Limitations

  • ActionType coverage depends on Microsoft 365 connectors and licensing.
  • RawEventData schemas vary by action and should be parsed only when structured fields are insufficient.

The query deliberately leaves ActionType broad for investigation. Use structured fields first and parse action-specific RawEventData only when necessary.

Legitimate resemblance

What the analyst should confirm

Similar activity can be legitimate. Confirm the approved purpose and expected context before escalating.
  • A secure web gateway, VPN, mobile carrier, VDI platform, or roaming transition changes the public source between click and sign-in.

    The transition matches an approved egress path, device/browser context remains expected, identity risk is absent, and no suspicious mailbox or cloud activity follows.
  • A user signs in from an unmanaged or non-compliant personal device after a legitimate email click.

    The URL and sender are verified legitimate, the source and user agent fit the user's history, no identity-risk signal is present, and subsequent cloud activity is expected.
  • A legitimate phishing simulation or security-awareness platform produces email-click and threat-verdict telemetry.

    The message, URL, campaign owner, recipient scope, simulation window, and post-click authentication behavior match the approved exercise and no attacker-controlled session appears.

Confirmed match

Action after a confirmed match

  • Revoke active sessions and tokens.
  • Reset credentials after session revocation.
  • Review and remove attacker-added or modified MFA authentication methods.
  • Review and remove suspicious mailbox rules.
  • Review mailbox reads, deletions, sent mail, and other cloud activity.
  • Purge confirmed follow-on phishing where supported.
  • Hunt other exposed users and shared suspicious infrastructure.

Threat hunt

Could this be happening elsewhere?

Hunt for this behavior across the environment.
View threat hunt

Technical boundary

Telemetry and limitations

Email

Message-level Microsoft 365 mail telemetry used to reconstruct source messages and recipient scope.

Required fields
  • Timestamp
  • NetworkMessageId
  • InternetMessageId
  • SenderFromAddress
  • SenderMailFromAddress
  • SenderDisplayName
  • SenderFromDomain
  • SenderIPv4
  • RecipientEmailAddress
  • Subject
  • EmailDirection
  • DeliveryAction
  • DeliveryLocation
  • LatestDeliveryAction
  • LatestDeliveryLocation
  • ThreatTypes
  • AuthenticationDetails
  • UrlCount
  • AttachmentCount
Network

Safe Links URL-click telemetry with user, message identifier, URL, redirect chain, action, source IP, and threat context.

Required fields
  • Timestamp
  • Url
  • ActionType
  • AccountUpn
  • Workload
  • NetworkMessageId
  • ThreatTypes
  • DetectionMethods
  • IPAddress
  • IsClickedThrough
  • UrlChain
  • ReportId
  • AppName
Authentication

Entra browser sign-ins with source, application, risk, device-trust, and session context.

Required fields
  • Timestamp
  • Application
  • ErrorCode
  • SessionId
  • AccountUpn
  • ResourceDisplayName
  • DeviceName
  • DeviceTrustType
  • IsManaged
  • IsCompliant
  • AuthenticationRequirement
  • RiskLevelAggregated
  • RiskLevelDuringSignIn
  • RiskEventTypes
  • RiskState
  • UserAgent
  • ClientAppUsed
  • Browser
  • IPAddress
  • Country
  • State
  • City
  • GatewayJA4
  • UniqueTokenId
Saas Audit

Microsoft 365 / Exchange Online audit activity used to investigate mailbox and cloud actions after suspicious authentication.

Required fields
  • Timestamp
  • ActionType
  • Application
  • AccountObjectId
  • AccountId
  • IPAddress
  • CountryCode
  • City
  • Isp
  • UserAgent
  • ActivityType
  • ObjectName
  • ObjectType
  • RawEventData
  • UncommonForUser

Blind spots

  • Missing Safe Links or equivalent click telemetry removes the direct email-click correlation.
  • Entra risk can remain unset, reducing confidence for otherwise suspicious session replay.
  • Delayed replay can fall outside the thirty-minute detection window.
  • Trusted proxies, identity brokers, and privacy services can obscure the true source and device context.
  • Mailbox activity can occur through connectors or APIs that are not fully represented in the selected audit source.

Behavior mapping

MITRE ATT&CK

Mappings describe the behavior examined by this analytic. They do not prove attribution, deployment, or technique-wide coverage.

Review boundary

Sources and limits

External sources
5

Exact fields, retention, and operational thresholds remain environment-specific.