Token replay is the post-compromise technique used in AiTM phishing, session hijacking, and token theft attacks. The attacker obtains a valid session or refresh token and replays it to access the victim’s resources without needing their credentials or MFA.
This subsection teaches the specific KQL patterns for detecting and investigating token replay. You will use these patterns extensively in Module 13 (AiTM Investigation) and Module 16 (Token Replay and Session Hijacking).
Where token replay appears
Token replay events appear in AADNonInteractiveUserSignInLogs — not in SigninLogs. The attacker presents a stolen token directly to the token endpoint, which generates a non-interactive sign-in event. There is no login page, no credential prompt, no MFA challenge.
The detection signature
Token replay = successful non-interactive sign-ins from an IP that has no corresponding interactive sign-in from the same user and location. The legitimate user signed in interactively from London. The attacker's token replay appears as a non-interactive sign-in from Lagos. No interactive sign-in from Lagos exists for that user — the token was not earned, it was stolen.
This query builds a baseline of the user’s known IPs from interactive sign-ins (30-day window), then finds non-interactive sign-ins from IPs not in that baseline. Results are strong indicators of token replay.
This hunts across all users: find non-interactive sign-ins from IPs that have never been associated with that user interactively. The EventCount > 5 filter reduces noise from single token refreshes that may be benign.
What to do when you find token replay
Revoke all sessions — this invalidates the stolen token immediately
Force password reset — in case the attacker also has the credentials
Check for post-compromise activity — inbox rules (CloudAppEvents), email access (MailItemsAccessed via Purview), lateral phishing (EmailEvents)
Check device compliance — would a “require compliant device” policy have blocked this?
Document the timeline — when was the token stolen, when was it first used, what was accessed
Try it yourself
Run the per-user token replay detection query in the demo environment (or your tenant) for any user. Compare the IPs in SigninLogs to those in AADNonInteractiveUserSignInLogs. Are there any non-interactive IPs that do not appear in the interactive logs?
In a clean environment, most non-interactive IPs will match interactive IPs — the same device refreshing tokens from the same network. Any mismatch is worth investigating. In the demo environment, you may find mismatches from service-to-service authentication (Microsoft backend infrastructure refreshing tokens on behalf of the user) — these typically resolve to Microsoft-owned IP ranges and are benign.
Check your understanding
1. Why does token replay appear in AADNonInteractiveUserSignInLogs instead of SigninLogs?
The attacker chose to use non-interactive mode
Token replay is blocked from interactive sign-ins
The attacker presents a stolen token directly to the token endpoint without going through a login page — there is no user interaction, so the event is non-interactive by definition
Interactive sign-ins require a user at a login page. Token replay bypasses the login page entirely — the attacker sends the stolen token directly to the token endpoint. The authentication system records this as a non-interactive event because no user interface was involved.
2. Your token replay detection finds non-interactive sign-ins from an IP in a country where the user has never signed in interactively. What is your first containment action?
Revoke all sessions for the affected user — this immediately invalidates the stolen token
Block the IP address in the firewall
Reset the user's password
Session revocation is the fastest containment for token replay. It invalidates all current tokens immediately, forcing re-authentication. Password reset is also important (the attacker may have the credentials too), but session revocation stops the active session first. Blocking the IP helps but is secondary — the attacker can change IPs.