Forensic Detection & Investigation of AiTM Attacks
While Adversary-in-the-Middle (AiTM) reverse proxy frameworks (such as Evilginx) succeed in bypassing interactive Multi-Factor Authentication (MFA), they leave behind an unmistakable, highly specific forensic footprint in cloud telemetry. Because the attack fundamentally decouples the initial interactive authentication handshake from subsequent adversary operations, it produces structural anomalies in IP routing, session identifiers, client device attributes, and token refresh sequences.
For the digital forensic investigator and SOC analyst, detecting an ongoing AiTM compromise does not require breaking TLS or having access to the threat actor’s proxy server. Instead, it relies on cross-telemetry correlation between interactive sign-ins, non-interactive token replays, risk detections, and downstream workload modifications.
This guide details the architectural signatures of AiTM attacks across Microsoft Entra ID and Purview logs, walks through an end-to-end reconstructed intrusion timeline, provides production-grade KQL hunting queries, and outlines the rapid containment protocol.
1. The Dual-IP & Dual-Session Forensic Signature
Section titled “1. The Dual-IP & Dual-Session Forensic Signature”The defining structural signature of an AiTM attack is the Dual-IP Ingress Anomaly:
graph TD subgraph "Phase 1: Victim Authentication (Handshake Ingress)" V_ACT[Victim executes interactive login via Evilginx] V_IP[IP #1: Reverse Proxy IP / Victim IP<br/>App: My Apps / OfficeHome<br/>ResultType = 0 (Success)<br/>MFA Satisfied = True] V_ACT --> V_IP end
subgraph "Phase 2: Token Capture & Exfiltration" V_IP --> PROXY_EXT[Evilginx captures ESTSAUTH & ESTSAUTHPERSISTENT<br/>Exported to Attacker Session Store] end
subgraph "Phase 3: Adversary Replay (Operational Ingress)" PROXY_EXT --> ATT_ACT[Attacker imports cookies into browser/script] ATT_IP[IP #2: Adversary Infrastructure IP<br/>Residential Proxy / VPN / VPS<br/>App: Exchange Online / Graph API / SharePoint<br/>Non-Interactive Token Refresh] ATT_ACT --> ATT_IP end
subgraph "Forensic Anomaly Detection" V_IP -.->|Correlation Gap:<br/>Different ASN / ISP / Country<br/>User-Agent Shift<br/>Device State: Compliant -> Unknown| DISCREPANCY{Forensic Delta} ATT_IP -.-> DISCREPANCY endForensic Indicators of the Dual-IP Footprint:
Section titled “Forensic Indicators of the Dual-IP Footprint:”- Interactive vs Non-Interactive Disconnect:
- The initial login appears in
SigninLogs(Interactive) under IP #1 (either the victim’s public egress or the hosting provider of the reverse proxy). - Within 2 to 15 minutes,
NonInteractiveUserSignInLogsorCloudAppEventsrecords activity for the same user under IP #2 (a commercial residential proxy, bulletproof hosting, or TOR exit node).
- The initial login appears in
- Device State Degradation:
- The initial sign-in may reflect a managed corporate device (
isCompliant: true,trustType: Workplace/AzureAdJoined). - The replayed session drops all device telemetry (
isCompliant: false,deviceId: null,trustType: null) because session cookies do not carry hardware device certificates.
- The initial sign-in may reflect a managed corporate device (
- User-Agent Incongruence:
- A victim authenticating on an updated macOS/Safari or Windows/Chrome browser suddenly manifests non-interactive requests originating from Linux/Python-requests, headless Chromium, or an outdated operating system version.
2. Telemetry Sources & Key Correlation Fields
Section titled “2. Telemetry Sources & Key Correlation Fields”Investigating an AiTM attack requires correlating fields across multiple log tables in Microsoft Sentinel or Defender XDR:
| Telemetry Table | Primary Correlation Fields | Forensic Evidence Uncovered |
|---|---|---|
SigninLogs | CorrelationId, IPAddress, UserAgent, MfaDetail, ConditionalAccessStatus, AppDisplayName | Identifies the initial interactive authentication handshake through the reverse proxy. |
NonInteractiveUserSignInLogs | OriginalRequestId, IPAddress, UserAgent, ResourceDisplayName, AutonomousSystemNumber | Reveals the adversary replaying stolen bearer tokens from a foreign IP/ASN to access cloud apps. |
UserRiskEvents | RiskEventType, RiskLevel, Source, IpAddress, DetectionTimingType | Captures Entra ID Protection real-time alerts: Atypical travel, Unfamiliar sign-in properties, Anomalous token. |
CloudAppEvents | AccountDisplayName, IPAddress, ActionType, RawEventData | Documents downstream actions: MailItemsAccessed, New-InboxRule, FileDownloaded. |
sequenceDiagram autonumber participant V as Victim Browser participant P as AiTM Proxy (IP 198.51.100.10) participant E as Entra ID (STS) participant A as Attacker (IP 203.0.113.50) participant M as Exchange Online
V->>P: Enters credentials + satisfies MFA P->>E: Proxies authentication request E-->>P: Issues ESTSAUTH Session Cookie (Logged in SigninLogs: IP 198.51.100.10) P->>A: Extracts and delivers ESTSAUTH to Attacker Note over A: 4 minutes later... A->>E: Presents ESTSAUTH from IP 203.0.113.50 to acquire Exchange token E-->>A: Issues Access Token (Logged in NonInteractiveUserSignInLogs: IP 203.0.113.50) A->>M: Connects to Mailbox API (Logged in CloudAppEvents: MailItemsAccessed)3. Reconstructed Incident Timeline: Anatomy of an AiTM Intrusion
Section titled “3. Reconstructed Incident Timeline: Anatomy of an AiTM Intrusion”The following chronological sequence represents a typical production BEC intrusion executed via Evilginx:
| Timestamp (UTC) | Telemetry Table | Observed Event / Value | Forensic Deduction |
|---|---|---|---|
| 14:02:11 | EmailEvents | Delivered: Phishing lure with subject "URGENT: Q1 Payroll Verification". | Initial delivery via compromised partner tenant (see Fiche 20). |
| 14:04:35 | UrlClickEvents | ActionType = UrlAllowed, Url = https://login.target-portal.net/auth. | Victim clicked link on desktop; Safe Links did not detonate new domain. |
| 14:05:18 | SigninLogs | ResultType = 0, IP = 198.51.100.10 (Hetzner VPS), App = OfficeHome, MFA = Satisfied. | Victim authenticated through Evilginx reverse proxy. |
| 14:08:42 | UserRiskEvents | RiskEventType = atypicalTravel, RiskLevel = High. | Microsoft engine flags impossible speed between victim country and proxy IP. |
| 14:09:15 | NonInteractiveUserSignInLogs | ResultType = 0, IP = 203.0.113.50 (Residential ISP, Nigeria), Resource = Exchange Online. | Adversary token replay. Stolen ESTSAUTH used from attacker infrastructure. |
| 14:10:02 | CloudAppEvents | ActionType = MailItemsAccessed, OperationProperties[MailAccessType] = Sync. | Attacker uses Graph API/ActiveSync to bulk sync \Inbox (see Fiche 17). |
| 14:12:30 | CloudAppEvents | ActionType = New-InboxRule, RuleName = ..., DeleteMessage = True. | Persistence & Defense Evasion. Attacker silences incoming security warnings. |
4. Production KQL Hunting Queries
Section titled “4. Production KQL Hunting Queries”4.1 Correlating Interactive Logins with Fast Foreign Token Replays
Section titled “4.1 Correlating Interactive Logins with Fast Foreign Token Replays”Detect instances where an interactive sign-in is immediately followed by non-interactive access from a different country or autonomous system (ASN) within 60 minutes:
let TimeDelta = 60m;let InteractiveSignins = SigninLogs| where TimeGenerated >= ago(7d)| where ResultType == 0| project InteractiveTime=TimeGenerated, UserPrincipalName, InteractiveIP=IPAddress, InteractiveCountry=Location, InteractiveASN=AutonomousSystemNumber, InteractiveUA=UserAgent, CorrelationId;let NonInteractiveSignins = NonInteractiveUserSignInLogs| where TimeGenerated >= ago(7d)| where ResultType == 0| project NonInteractiveTime=TimeGenerated, UserPrincipalName, ReplayIP=IPAddress, ReplayCountry=Location, ReplayASN=AutonomousSystemNumber, ReplayUA=UserAgent, ResourceDisplayName;InteractiveSignins| join kind=inner (NonInteractiveSignins) on UserPrincipalName| where NonInteractiveTime between (InteractiveTime .. (InteractiveTime + TimeDelta))| where InteractiveIP != ReplayIP and (InteractiveCountry != ReplayCountry or InteractiveASN != ReplayASN)| project UserPrincipalName, InteractiveTime, InteractiveIP, InteractiveCountry, InteractiveASN, NonInteractiveTime, ReplayIP, ReplayCountry, ReplayASN, ResourceDisplayName| sort by InteractiveTime desc4.2 Detecting Sudden Device Compliance Drops in Replayed Sessions
Section titled “4.2 Detecting Sudden Device Compliance Drops in Replayed Sessions”Identify sessions that began on a compliant or joined corporate device, but immediately transitioned to unmanaged endpoints during workload queries:
SigninLogs| where TimeGenerated >= ago(7d)| where ResultType == 0| extend DeviceState = tostring(DeviceDetail.isCompliant)| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName, DeviceState, UserAgent| join kind=inner ( NonInteractiveUserSignInLogs | where TimeGenerated >= ago(7d) | where ResultType == 0 | extend ReplayDeviceState = tostring(DeviceDetail.isCompliant) | project ReplayTime=TimeGenerated, UserPrincipalName, ReplayIP=IPAddress, ReplayApp=ResourceDisplayName, ReplayDeviceState, ReplayUA=UserAgent) on UserPrincipalName| where DeviceState == "true" and (ReplayDeviceState == "false" or isempty(ReplayDeviceState))| where ReplayTime between (TimeGenerated .. (TimeGenerated + 30m))| where IPAddress != ReplayIP| project UserPrincipalName, TimeGenerated, IPAddress, DeviceState, ReplayTime, ReplayIP, ReplayDeviceState, ReplayApp| sort by TimeGenerated desc4.3 Hunting Evilginx Infrastructure via Hosting Provider ASNs
Section titled “4.3 Hunting Evilginx Infrastructure via Hosting Provider ASNs”Flag interactive authentications originating from known cloud VPS providers (DigitalOcean, Hetzner, Linode, OVH) that should never serve as legitimate employee egress networks:
let KnownHostingASNs = dynamic([14061, 24940, 63949, 16276, 51167]); // DigitalOcean, Hetzner, Linode, OVH, ContaboSigninLogs| where TimeGenerated >= ago(14d)| where ResultType == 0| where AutonomousSystemNumber in (KnownHostingASNs)| project TimeGenerated, UserPrincipalName, IPAddress, AutonomousSystemNumber, Location, AppDisplayName, UserAgent| sort by TimeGenerated desc5. Rapid Triage & Remediation Playbook for AiTM
Section titled “5. Rapid Triage & Remediation Playbook for AiTM”When an AiTM compromise is confirmed, the SOC/DFIR team must execute remediation rapidly. Merely changing the user’s password does not immediately invalidate active session cookies or PRTs in non-CAE-enforced environments.
graph TD ALERT[AiTM Compromise Confirmed] --> STEP1[1. Invalidate All Refresh Tokens<br/>Revoke-MgUserSignSession] STEP1 --> STEP2[2. Reset User Password<br/>Forces Kerberos/NTLM hash rotation] STEP2 --> STEP3[3. Audit & Clean Authentication Methods<br/>Verify registered phones, FIDO keys, Authenticator instances] STEP3 --> STEP4[4. Audit Mailbox Rules & Forwarding<br/>Remove any created inbox rules / forwarding addresses] STEP4 --> STEP5[5. Review OAuth Consents & Service Principals<br/>Ensure no rogue application was granted offline_access]PowerShell Rapid Remediation Script:
Section titled “PowerShell Rapid Remediation Script:”# Prerequisites: Microsoft.Graph.Users, ExchangeOnlineManagement# Connect-MgGraph -Scopes "User.ReadWrite.All"# Connect-ExchangeOnline
$victimUPN = "alice.dupont@target.com"Write-Host "[!] INITIATING RAPID CONTAINMENT FOR AiTM VICTIM: $victimUPN" -ForegroundColor Red
# 1. Terminate all active sessions immediatelyRevoke-MgUserSignSession -UserId $victimUPNWrite-Host "[+] Active Entra ID sessions and refresh tokens REVOKED." -ForegroundColor Green
# 2. Inspect registered MFA methods for backdoors$mfaMethods = Get-MgUserAuthenticationMethod -UserId $victimUPNWrite-Host "[*] Auditing registered authentication methods:" -ForegroundColor Yellow$mfaMethods | Select-Object Id, AdditionalProperties | Format-List
# 3. Check for malicious forwarding and inbox rules$rules = Get-InboxRule -Mailbox $victimUPN$suspiciousRules = $rules | Where-Object { $_.ForwardTo -or $_.RedirectTo -or $_.DeleteMessage }if ($suspiciousRules) { Write-Warning "[!] Malicious Inbox Rules Detected:" $suspiciousRules | Format-Table Name, ForwardTo, RedirectTo, DeleteMessage # Uncomment to auto-remove: # $suspiciousRules | ForEach-Object { Remove-InboxRule -Mailbox $victimUPN -Identity $_.Identity -Confirm:$false }}
# 4. Check for external mailbox forwarding configuration$mbx = Get-Mailbox -Identity $victimUPNif ($mbx.ForwardingSmtpAddress -or $mbx.ForwardingAddress) { Write-Error "[!] Mailbox level forwarding configured to: $($mbx.ForwardingSmtpAddress) / $($mbx.ForwardingAddress)" # Set-Mailbox -Identity $victimUPN -ForwardingSmtpAddress $null -ForwardingAddress $null}6. Cross-Reference & Investigation Navigation
Section titled “6. Cross-Reference & Investigation Navigation”- Previous Fiche: 21. Adversary-in-the-Middle (AiTM) Reverse Proxy Mechanics
- Next Fiche: 23. OAuth Illicit Consent Grant & Application Phishing
- Related Guides:
- 09. Entra ID Sign-in Logs Analysis
- 10. Entra ID Audit Logs Deep Dive
- 12. Entra ID Protection & Risk Detection Forensics
- 17. Mailbox Auditing & MailItemsAccessed Deep Dive
- 19. Microsoft 365 Account Compromise Kill Chain
- 28. Mailbox Delegation & Permissions Abuse
- 29. Malicious Inbox Rules & Forwarding