Back to Blog
    Technical

    How to Detect AiTM Phishing Attacks

    ThreatHunter.ai TeamJanuary 25, 20269 min read

    Let me be direct. If you think MFA protects you from phishing, you are wrong. Adversary-in-the-Middle attacks, AiTM attacks, blow right through MFA. They do not crack it. They do not brute force it. They simply steal the authenticated session after your user completes the MFA challenge. And most security teams have zero visibility into this.

    What is an AiTM Attack?

    AiTM phishing puts an attacker's server between your user and the real login page. The user thinks they are logging into Microsoft 365 or Okta or whatever. They enter credentials. They complete MFA. Everything looks normal. But the attacker's proxy server is relaying everything in real time, and when that session cookie gets issued, the attacker grabs it.

    Now the attacker has a fully authenticated session. They can access email, SharePoint, Teams, whatever that user has access to. Your MFA did its job. The user proved who they were. But the attacker walked in right behind them.

    Tools like EVILGINX make this trivial to execute. This is not theoretical. We see these attacks every single week across our customer base.

    Why Traditional Detection Fails

    Here is the problem. Your SIEM sees a successful login. Your identity provider logs show MFA completed. From the perspective of your security stack, nothing is wrong. The authentication was legitimate. The session is valid.

    Traditional security tools look for failed logins, impossible travel on the authentication event itself, known bad IPs. None of that helps here. The login succeeds. The attacker replays the session from infrastructure that is not on any blocklist. By the time they start accessing data, they look like a normal user.

    This is why so many organizations get compromised even with MFA everywhere. They have a blind spot the size of a truck.

    The clock you are up against

    Before the queries, understand the tempo. When Microsoft dissected a campaign that targeted more than 10,000 organizations, they found attackers replaying the stolen session cookie as little as five minutes after the phish, and the cookie carried the MFA claim, so the second factor never fired again. Within hours the attacker had searched finance threads, deleted the original phishing email, and created an inbox rule to hide replies from fraud targets.

    The pattern has not aged. Microsoft's January 2026 analysis of a campaign abusing SharePoint against energy sector targets found the same follow-through: an inbox rule deleting all incoming mail and marking it read, MFA method additions for persistence, and internal phishing sent from the compromised account. Two sign-in IPs from that campaign, 178.130.46.8 and 193.36.221.10, are worth checking your last 90 days for right now.

    And the supply side keeps growing: 1.17 million infostealer logs this year contained enterprise credentials together with active session cookies, Entra ID present in 79% of enterprise identity logs. Whether the token is stolen by a proxy kit or a stealer, the detection problem is identical: a valid, MFA-satisfied session in the wrong hands.

    Five minutes of attacker tempo means detection that pages a human at 4 a.m. or it means nothing. Here is what our hunt teams run.

    The queries

    Prerequisites: Entra ID sign-in logs including non-interactive, and Defender for Cloud Apps / Unified Audit Log ingestion. Token replay lives in the non-interactive logs; if you are not collecting them, start today.

    1. One session, many networks: token replay

    The core AiTM tell: the token minted during the phish gets replayed from attacker infrastructure. Same session identifier, different IP, ASN, or country, in a short window.

    // Defender Advanced Hunting: sessions seen from multiple countries/ASNs
    AADSignInEventsBeta
    | where Timestamp > ago(6h)
    | where ErrorCode == 0
    | summarize
        IpCount = dcount(IPAddress),
        CountryCount = dcount(Country),
        IPs = make_set(IPAddress, 10)
        by SessionId, AccountUpn
    | where IpCount > 1 and CountryCount > 1
    // Sentinel equivalent keyed on UniqueTokenIdentifier
    SigninLogs
    | where TimeGenerated > ago(6h) and ResultType == 0
    | summarize
        IPs = make_set(IPAddress, 10),
        ASNs = make_set(AutonomousSystemNumber, 10),
        Countries = make_set(Location, 10)
        by UserPrincipalName, UniqueTokenIdentifier
    | where array_length(ASNs) > 1 or array_length(Countries) > 1

    False positive sources: mobile users bouncing between Wi-Fi and cellular, VPN churn. Suppress known corporate-egress and carrier ASN pairs per user, not globally.

    2. MFA-satisfied sign-ins from hosting infrastructure

    AiTM kits live on rented servers. An interactive, MFA-passing sign-in from a datacenter ASN is worth a human look every time, because humans do not work from racks.

    let SuspectASNs = dynamic([16509, 14061, 16276, 24940, 20473, 63949]); // AWS, DigitalOcean, OVH, Hetzner, Vultr, Linode - extend from intel
    SigninLogs
    | where TimeGenerated > ago(24h) and ResultType == 0
    | where AutonomousSystemNumber in (SuspectASNs)
    | where AuthenticationRequirement == "multiFactorAuthentication"
    | project TimeGenerated, UserPrincipalName, IPAddress, AutonomousSystemNumber, AppDisplayName, ClientAppUsed, UserAgent

    Add the January 2026 campaign IPs (178.130.46.8, 193.36.221.10) and your own intel to a watchlist, but do not blocklist your way to safety; kits rotate. The durable signal is datacenter ASN plus interactive user workload.

    3. The impossible traveler, done properly

    SigninLogs
    | where TimeGenerated > ago(1d) and ResultType == 0
    | extend lat = todouble(LocationDetails.geoCoordinates.latitude),
             lon = todouble(LocationDetails.geoCoordinates.longitude)
    | where isnotempty(lat)
    | sort by UserPrincipalName asc, TimeGenerated asc
    | serialize
    | extend prevLat = prev(lat), prevLon = prev(lon),
             prevTime = prev(TimeGenerated), prevUser = prev(UserPrincipalName)
    | where UserPrincipalName == prevUser
    | extend km = geo_distance_2points(lon, lat, prevLon, prevLat) / 1000.0,
             hours = datetime_diff('minute', TimeGenerated, prevTime) / 60.0
    | where hours > 0 and (km / hours) > 800   // faster than a commercial flight
    | project TimeGenerated, UserPrincipalName, km, hours, Velocity = km/hours, IPAddress

    4. The follow-through: inbox rules after a suspect sign-in

    In both Microsoft-documented campaigns above, the attacker's first mailbox move was an inbox rule. A new rule within hours of a new-infrastructure sign-in is close to a confirmed hands-on-keyboard signal.

    let RuleEvents = CloudAppEvents
    | where Timestamp > ago(1d)
    | where ActionType in ("New-InboxRule", "Set-InboxRule", "UpdateInboxRules")
    | extend Upn = tostring(RawEventData.UserId);
    let NewInfraSignins = AADSignInEventsBeta
    | where Timestamp > ago(1d) and ErrorCode == 0
    | summarize FirstSeen = min(Timestamp) by AccountUpn, IPAddress;
    RuleEvents
    | join kind=inner (NewInfraSignins) on $left.Upn == $right.AccountUpn
    | where Timestamp between (FirstSeen .. (FirstSeen + 6h))
    | project Timestamp, Upn, ActionType, RawEventData

    Alert outright on rules that delete all incoming mail or mark everything read, the exact configuration from the SharePoint campaign, and on rules moving mail to RSS Subscriptions or referencing "invoice," "payment," or "password." Legitimate users do not build those.

    5. Revoke first, investigate second

    When the above fires: Revoke-MgUserSignInSession, reset credentials, then pull the session's token lineage from the audit log to scope what was touched. Microsoft's five-minute replay finding sets the bar. Every minute of analysis before revocation is attacker access you chose to allow.

    The end state that needs fewer queries

    Detection is our trade, so believe us when we say you should need less of it. Phishing-resistant MFA (FIDO2, Windows Hello) breaks AiTM kits outright because the credential is origin-bound; there is no session to proxy. Conditional Access token protection and compliant-device requirements shrink the replay surface. The queries above cover the gap between where you are and that end state, and the day someone plugs a key into the wrong page anyway.

    Our hunt teams run these detections, tuned per environment, backed by MILBERT, 24/7. If your team cannot staff the 4 a.m. page, that is literally what we are for.