MFA war das Versprechen. Es hat nur nie den Token geschützt.
Die meisten Organisationen im DACH-Mittelstand haben Multi-Faktor-Authentifizierung ausgerollt und betrachten die Identitätsschicht damit als abgesichert. Das ist nachvollziehbar — und im Kern falsch. MFA schützt den Moment der Anmeldung. Sie schützt nicht das Artefakt, das nach erfolgreicher Anmeldung entsteht: den Token, mit dem sich der Client fortan gegenüber Entra ID und den nachgelagerten Ressourcen ausweist.
Genau hier setzt Adversary-in-the-Middle an. Der Angreifer fängt nicht das Passwort und den zweiten Faktor ab, um sie später erneut einzugeben — das würde an moderner MFA scheitern. Er positioniert sich als transparenter Reverse-Proxy zwischen Opfer und echtem Anmeldedienst, lässt das Opfer die komplette Authentifizierung inklusive MFA durchführen und greift die dabei ausgestellte Session ab. Was er stiehlt, ist kein Geheimnis, das man rotieren könnte, sondern ein bereits eingelöster Berechtigungsnachweis.
Der Effekt ist unangenehm konkret. Ein gültiges Primary Refresh Token, kurz PRT, ist für den Ressourcenserver von einer legitimen Sitzung nicht zu unterscheiden. Es wurde korrekt ausgestellt, es trägt die richtigen Claims, und es übersteht jede erneute Conditional-Access-Auswertung, die keine frische Interaktion erzwingt. Der Angreifer muss sich nie wieder anmelden. Er nutzt einfach weiter.
| Annahme | Realität bei AiTM |
|---|---|
| MFA verhindert Kontenübernahme | MFA wird während des Proxy-Flows regulär durchlaufen — der Angreifer erbt die Sitzung danach |
| Ein neuer Standort löst eine erneute Prüfung aus | Nicht-interaktive Token-Nutzung triggert oft keine erneute interaktive Kontrolle |
| Passwort-Reset beendet den Zugriff | Ein Refresh Token bleibt bis zum Ablauf oder expliziten Widerruf gültig — unabhängig vom Passwort |
| Der Vorfall ist im Sign-in-Log sichtbar | Die relevante Aktivität landet größtenteils im nicht-interaktiven Log, das selten aktiv ausgewertet wird |
Die letzte Zeile ist der eigentliche Punkt dieses Artikels. Die Telemetrie ist vorhanden. Sie liegt nur nicht dort, wo die meisten Detections hinschauen.
Vom Phishing-Proxy zum Primary Refresh Token
Der Ablauf ist seit den ersten breit dokumentierten AiTM-Kampagnen stabil geblieben, weil er an der Mechanik der Sitzung ansetzt und nicht an einer einzelnen Schwachstelle. Das Opfer erhält einen Link, der auf eine vom Angreifer betriebene Proxy-Infrastruktur zeigt. Diese Infrastruktur spiegelt die echte Anmeldeseite in Echtzeit — jede Eingabe wird an den legitimen Endpunkt weitergereicht, jede Antwort zurück an das Opfer.
Aus Sicht des Opfers funktioniert alles korrekt: die vertraute Oberfläche, die MFA-Aufforderung, die Bestätigung. Aus Sicht von Entra ID ist die Anmeldung ebenfalls valide, denn sie kommt tatsächlich vom echten Nutzer, nur eben über einen zwischengeschalteten Kanal. Am Ende dieses Flows hält der Angreifer die ausgestellten Session-Cookies beziehungsweise, im Fall eines registrierten Geräts, Material zur Nutzung eines PRT.
Warum das PRT die interessantere Beute ist
Session-Cookies sind an den Browser gebunden und laufen vergleichsweise zügig ab. Ein Primary Refresh Token ist der Generalschlüssel der Geräteebene: langlebig, an das Gerät und den Nutzer gekoppelt, und in der Lage, fortlaufend Access Tokens für beliebige Ressourcen auszustellen, ohne dass eine neue interaktive Anmeldung nötig wäre. Wer ein PRT kontrolliert, braucht die Anmeldeseite nie wieder zu sehen.
Der entscheidende Perspektivwechsel für die Detection: Nach dem initialen Diebstahl verschwindet der Angreifer aus dem interaktiven Anmeldepfad. Alle weiteren Zugriffe erscheinen als nicht-interaktive Anmeldungen — Token-Erneuerungen und Ressourcenzugriffe, die im Betrieb völlig unauffällig aussehen, weil genau so auch legitime Clients arbeiten.
// Frisch registrierte Domain, Link angeklicktEmailUrlInfo| join kind=inner EmailEvents on NetworkMessageId| where UrlDomain !in (KnownGoodDomains)| where isnotempty(ClickAction)| project Timestamp, RecipientEmailAddress, Url, UrlDomain// Nicht-interaktive Nutzung ohne frische AnmeldungAADNonInteractiveUserSignInLogs| where TimeGenerated > ago(24h)| join kind=leftanti ( SigninLogs | project UserPrincipalName, IPAddress ) on UserPrincipalName, IPAddress| summarize Zugriffe = count() by UserPrincipalName, IPAddress| where Zugriffe > 5// Neue MFA-Methode kurz nach Risiko-LoginSigninLogs| where RiskLevelDuringSignIn in ("high","medium")| project SigninTime = TimeGenerated, UserPrincipalName| join kind=inner ( AuditLogs | where OperationName has "authentication method" | project AuditTime = TimeGenerated, UserPrincipalName = tostring(TargetResources[0].userPrincipalName) ) on UserPrincipalName| where AuditTime between (SigninTime .. (SigninTime + 30m))// Frisch registrierte Domain, Link angeklicktEmailUrlInfo| join kind=inner EmailEvents on NetworkMessageId| where UrlDomain !in (KnownGoodDomains)| where isnotempty(ClickAction)| project Timestamp, RecipientEmailAddress, Url, UrlDomain// Nicht-interaktive Nutzung ohne frische AnmeldungAADNonInteractiveUserSignInLogs| where TimeGenerated > ago(24h)| join kind=leftanti ( SigninLogs | project UserPrincipalName, IPAddress ) on UserPrincipalName, IPAddress| summarize Zugriffe = count() by UserPrincipalName, IPAddress| where Zugriffe > 5// Neue MFA-Methode kurz nach Risiko-LoginSigninLogs| where RiskLevelDuringSignIn in ("high","medium")| project SigninTime = TimeGenerated, UserPrincipalName| join kind=inner ( AuditLogs | where OperationName has "authentication method" | project AuditTime = TimeGenerated, UserPrincipalName = tostring(TargetResources[0].userPrincipalName) ) on UserPrincipalName| where AuditTime between (SigninTime .. (SigninTime + 30m))Was Conditional Access noch sieht — und was nicht
Conditional Access ist eine präventive Kontrolle am Anmeldeereignis. Ihre Wirksamkeit gegen AiTM hängt vollständig davon ab, ob die Bedingung im Moment der Token-Nutzung überhaupt erneut ausgewertet wird. Bei einem gestohlenen, bereits ausgestellten Token ist das häufig nicht der Fall — und dann greift die Richtlinie ins Leere.
Nicht jede Kontrolle ist gleich wirkungslos. Die Unterscheidung, welche Maßnahme den Diebstahl erschwert und welche nur den ersten Login absichert, ist die Grundlage jeder ehrlichen Risikobewertung.
| Kontrolle | Wirkung auf AiTM | Einordnung |
|---|---|---|
| Standard-MFA | Wird im Proxy-Flow mitgeführt | Kein Schutz gegen den Sitzungsdiebstahl selbst |
| Phishing-resistente MFA (FIDO2, Passkeys, WHfB) | Bindet die Authentifizierung an die Origin | Bricht den Proxy — die wirksamste Einzelmaßnahme |
| Device Compliance / Managed Device | Erschwert Token-Nutzung von fremder Hardware | Hoch wirksam, sofern durchgängig erzwungen |
| Token Protection (Sign-in Session) | Bindet das Token an das ausstellende Gerät | Direkt gegen Replay gerichtet, aber noch begrenzt abgedeckt |
| Sign-in-Frequency | Erzwingt periodisch neue Interaktion | Verkleinert das Zeitfenster, schließt es nicht |
Die praktische Konsequenz: Wer AiTM adressieren will, verschiebt Budget von generischer MFA-Abdeckung hin zu phishing-resistenten Verfahren und Gerätebindung. Alles andere ist Detektion nach dem Ereignis — notwendig, aber nachgelagert. Und genau diese Detektion ist der Teil, den die meisten Setups gar nicht erst instrumentieren.
Die Signale liegen im nicht-interaktiven Log
Wenn die Prävention umgangen ist, bleibt die Frage: Woran erkennt man den Missbrauch? Die Antwort beginnt mit einer Verschiebung des Blickfelds. Die meisten Detections werten SigninLogs aus — das interaktive Anmeldelog. Die relevante Aktivität nach dem Token-Diebstahl liegt jedoch in AADNonInteractiveUserSignInLogs, das um Größenordnungen mehr Einträge führt und deshalb oft aus Kostengründen gar nicht erst ausgewertet wird.
Der erste Ansatz sucht nach nicht-interaktiven Anmeldungen, denen keine passende interaktive Anmeldung von derselben Identität und IP gegenübersteht. Ein legitimer Client, der ein Token erneuert, hat sich vorher irgendwann interaktiv angemeldet. Ein gestohlenes Token, das von fremder Infrastruktur genutzt wird, hat das nicht.
1// Interaktive und nicht-interaktive Anmeldungen zusammenfuehren,2// um Token-Nutzung ohne frische Authentifizierung zu finden3let lookback = 24h;4let interaktiv =5 SigninLogs6 | where TimeGenerated > ago(lookback)7 | project UserPrincipalName, IPAddress;8AADNonInteractiveUserSignInLogs9| where TimeGenerated > ago(lookback)10| join kind=leftanti interaktiv on UserPrincipalName, IPAddress11| summarize Ressourcen = make_set(ResourceDisplayName),12 Anmeldungen = count()13 by UserPrincipalName, IPAddress14| where Anmeldungen > 515| order by Anmeldungen descDer Filter auf mehr als fünf Anmeldungen unterdrückt vereinzeltes Rauschen. In der Praxis lohnt es sich, zusätzlich bekannte Service-IPs und Ausgangs-Proxies des eigenen Netzes auszuschließen, damit legitime zentrale Gateways nicht dauerhaft als Treffer erscheinen.
Der zweite Ansatz betrachtet die geografische Streuung erfolgreicher MFA-Anmeldungen in einem engen Zeitfenster. Ein Token, das an mehreren Orten gleichzeitig verwendet wird, ist ein starkes Signal — unabhängig davon, ob der eigentliche Diebstahl schon sichtbar war.
1SigninLogs2| where TimeGenerated > ago(7d)3| where AuthenticationRequirement == "multiFactorAuthentication"4| extend Land = tostring(LocationDetails.countryOrRegion)5| summarize Laender = make_set(Land), IPs = dcount(IPAddress)6 by UserPrincipalName, bin(TimeGenerated, 1h)7| where array_length(Laender) > 1 and IPs > 28| project TimeGenerated, UserPrincipalName, Laender, IPs9| order by TimeGenerated descBeide Abfragen liefern Kandidaten, keine Urteile. Ihre Aussagekraft und ihr False-Positive-Risiko unterscheiden sich deutlich — und diese Einordnung gehört in die Detection-Dokumentation, nicht in den Kopf einer einzelnen Person.
| Signal | Quelle | Aussagekraft | FP-Risiko |
|---|---|---|---|
| Token-Nutzung ohne interaktive Anmeldung | AADNonInteractive… | Hoch | Mittel |
| Geografische Streuung pro Stunde | SigninLogs | Mittel | Mittel |
| MFA-Methode kurz nach Risiko-Login registriert | AuditLogs | Sehr hoch | Niedrig |
| Ressourcenzugriff ohne registriertes Gerät | AADNonInteractive… | Hoch | Niedrig |
In Sentinel wird aus Kandidaten ein Vorfall
Einzelsignale erzeugen Alarm-Müdigkeit. Der Mehrwert entsteht in der Korrelation: Eine risikobehaftete Anmeldung für sich ist ein Kandidat. Eine risikobehaftete Anmeldung, gefolgt von der Registrierung einer neuen MFA-Methode durch dieselbe Identität innerhalb weniger Minuten, ist ein Muster, das legitime Nutzer praktisch nie erzeugen — und Angreifer fast immer, weil sie sich Persistenz sichern wollen.
1let verdaechtig =2 SigninLogs3 | where TimeGenerated > ago(1d)4 | where RiskLevelDuringSignIn in ("high", "medium")5 | project SigninTime = TimeGenerated, UserPrincipalName, IPAddress;6verdaechtig7| join kind=inner (8 AuditLogs9 | where OperationName has "authentication method"10 | extend UserPrincipalName =11 tostring(TargetResources[0].userPrincipalName)12 | project AuditTime = TimeGenerated, UserPrincipalName, OperationName13) on UserPrincipalName14| where AuditTime between (SigninTime .. (SigninTime + 30m))15| project SigninTime, AuditTime, UserPrincipalName, IPAddress, OperationName16| order by SigninTime descDas Zeitfenster von 30 Minuten ist bewusst eng gewählt. Es korreliert nicht irgendeine spätere Methodenregistrierung, sondern nur solche, die dem verdächtigen Login unmittelbar folgt. Dadurch bleibt die Regel scharf genug, um ohne Tragästchen als Analytics Rule mit hoher Priorität zu laufen.
Ein zweites, komplementäres Signal zielt direkt auf das Token: Ressourcenzugriffe auf Exchange Online oder Microsoft Graph, die von keinem registrierten Gerät ausgehen. Legitime verwaltete Clients tragen eine Geräte-ID; ein von fremder Infrastruktur genutztes Token häufig nicht.
1// Ressourcenzugriff ohne registriertes Geraet2// innerhalb des PRT-Gueltigkeitsfensters3AADNonInteractiveUserSignInLogs4| where TimeGenerated > ago(1h)5| where ResourceDisplayName in ("Office 365 Exchange Online",6 "Microsoft Graph")7| extend DeviceId = tostring(DeviceDetail.deviceId)8| where isempty(DeviceId)9| summarize Zugriffe = count(),10 Ressourcen = make_set(ResourceDisplayName)11 by UserPrincipalName, IPAddress12| where Zugriffe > 10Diese Abfrage ist bewusst als Near-Real-Time-Kandidat formuliert — enges Zeitfenster, klarer Schwellwert. In einer Umgebung mit durchgängiger Geräteregistrierung ist das leere DeviceId-Feld ein bemerkenswert sauberes Signal.
Was heute Konsequenzen hat
Detektion ohne Reaktionsplan ist Dokumentation eines Vorfalls, nicht seine Eindämmung. Bei bestätigtem Token-Diebstahl reicht das Zurücksetzen des Passworts nicht — das Refresh Token bleibt gültig. Die Reihenfolge der Gegenmaßnahmen ist entscheidend und sollte vor dem ersten Ernstfall festgelegt sein.
- Refresh Tokens widerrufen. Der einzige Schritt, der ein gestohlenes PRT tatsächlich entwertet. Passwort-Reset allein lässt den Angreifer im System.
- Registrierte MFA-Methoden prüfen. Neu hinzugefügte Methoden nach dem Vorfall sind Persistenz und gehören entfernt, bevor der Zugang wiederhergestellt wird.
- Geräteregistrierungen kontrollieren. Ein während des Vorfalls neu registriertes Gerät verlängert den Zugriff über die Token-Invalidierung hinaus.
- Blast Radius bestimmen. Welche Ressourcen hat das Token in seinem Gültigkeitsfenster berührt? Diese Frage steuert den Umfang der Aufarbeitung.
Präventiv führt der Weg zu phishing-resistenter Authentifizierung, Token Protection und konsequenter Gerätebindung — jede Maßnahme verkleinert das Fenster, in dem ein gestohlenes Token überhaupt nutzbar ist. Keine davon macht Detektion überflüssig; sie verschieben nur das Gleichgewicht zurück in Richtung Verteidiger.
Conditional Access schützt die Tür. AiTM steigt durch das Fenster ein, das schon offenstand, weil das Token nach der Anmeldung niemand mehr bewacht. Die Signale existieren — man muss nur im nicht-interaktiven Log nachsehen und die Ereignisse zusammenführen, bevor sie einzeln im Rauschen verschwinden.