14.4 Tracing Token Lifecycle

3-5 hours · Module 14

Tracing Token Lifecycle

Follow the stolen token from its original issuance through renewal, resource access, and eventual revocation or expiry. This timeline reconstruction tells you: when the compromise occurred, how long the attacker had access, and what resources they accessed.


Step 1: Find the original token issuance

The original token was issued during the AiTM authentication. Find it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Original token issuance  the authentication event where the token was created
SigninLogs
| where UserPrincipalName == "r.williams@northgateeng.com"
| where TimeGenerated between(datetime(2026-02-27T00:00:00Z) .. datetime(2026-02-28T23:59:00Z))
| where ResultType == "0"
| where AuthenticationRequirement == "multiFactorAuthentication"
| project TimeGenerated, IPAddress, AppDisplayName,
    AuthenticationRequirement, TokenIssuerType,
    Browser = tostring(DeviceDetail.browser),
    CorrelationId
| order by TimeGenerated asc

The first successful MFA sign-in from a non-corporate IP during the AiTM window is the token issuance event. Note the CorrelationId — it links to subsequent token renewal events.


Step 2: Trace token renewals

Each time the application uses the refresh token to obtain a new access token, a non-interactive sign-in is logged:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Token renewal events  non-interactive sign-ins using the stolen token
AADNonInteractiveUserSignInLogs
| where UserPrincipalName == "r.williams@northgateeng.com"
| where TimeGenerated > datetime(2026-02-27T00:00:00Z)
| where IPAddress !in ("192.0.2.10", "192.0.2.15")
| project TimeGenerated, IPAddress, AppDisplayName,
    ResourceDisplayName,
    TokenIssuerType,
    UserAgent = tostring(DeviceDetail.browser)
| order by TimeGenerated asc

What to examine: The chronological list of non-interactive sign-ins from non-corporate IPs shows every resource the attacker accessed and when. Gaps in the timeline indicate periods when the attacker was not active — the token existed but was not being used. Resumed activity after a gap confirms the refresh token is still valid.


Step 3: Identify the revocation event (if any)

1
2
3
4
5
6
7
// Token revocation audit  was Revoke-MgUserSignInSession executed?
AuditLogs
| where TimeGenerated > datetime(2026-02-28T00:00:00Z)
| where OperationName has "Revoke user session" or OperationName has "Invalidate all refresh tokens"
| where TargetResources[0].userPrincipalName == "r.williams@northgateeng.com"
| project TimeGenerated, OperationName,
    RevokedBy = tostring(InitiatedBy.user.userPrincipalName)

If revocation was logged: Check whether non-interactive sign-ins from the attacker IP continued AFTER the revocation timestamp. If they did: the revocation failed, a different token was issued after revocation (re-compromise), or an OAuth application is authenticating independently of user tokens.

If no revocation was logged: The containment in Module 12 may have missed this user — or the revocation command failed. This is the eradication gap.


Step 4: Check for OAuth application persistence

This is the most likely explanation for access 20 days after containment:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// OAuth applications with access to r.williams' resources
AuditLogs
| where TimeGenerated > datetime(2026-02-26T00:00:00Z)
| where OperationName == "Consent to application"
| where Result == "success"
| extend ConsentUser = tostring(InitiatedBy.user.userPrincipalName)
| where ConsentUser == "r.williams@northgateeng.com"
| extend AppName = tostring(TargetResources[0].displayName)
| extend AppId = tostring(TargetResources[0].id)
| extend IPAddress = tostring(InitiatedBy.user.ipAddress)
| project TimeGenerated, ConsentUser, AppName, AppId, IPAddress

If an OAuth consent was granted during the compromise window from a non-corporate IP: the attacker created an application with API access to r.williams’ mailbox. This application authenticates with its own client credentials — completely independent of r.williams’ password and tokens. Password reset and token revocation have zero effect on it.

This is the persistence mechanism that survives everything. The application has its own identity, its own tokens, and its own access grants. To remove it, you must revoke the consent (Entra ID → Enterprise Applications → remove the application or the user’s consent).

Subsection artifact: The 4-step token lifecycle tracing methodology. This is the core analytical technique of the token investigation playbook.


Knowledge check


The complete token chain — from authentication to revocation

Understanding the full token chain clarifies why specific containment actions target specific token types:

Authentication event (T=0). User enters credentials + completes MFA. Entra ID issues: a primary refresh token (PRT) bound to the device, a refresh token for the authenticated session, and an access token for the first resource requested. In an AiTM attack, the proxy captures the refresh token and session cookie at this moment.

Resource access (T=0 to T+90 minutes). The application presents the access token to the resource server (Exchange, SharePoint). The resource server validates the token and grants access. The access token is valid for 60-90 minutes. During this window, the token works without any further check (unless CAE is enabled).

Token renewal (T+60-90 minutes). The access token expires. The application presents the refresh token to Entra ID and requests a new access token. Entra ID validates the refresh token (is it expired? is it revoked?) and issues a new access token. This renewal cycle repeats every 60-90 minutes for as long as the refresh token is valid (up to 90 days).

Password reset (T+X). An administrator resets the user’s password. This invalidates the password — future authentication with the old password fails. But existing refresh tokens are NOT invalidated. The next token renewal succeeds because the refresh token is still valid. The attacker retains access.

Token revocation (T+Y). An administrator explicitly revokes refresh tokens via Revoke-MgUserSignInSession. All refresh tokens are invalidated. The next token renewal FAILS. But the current access token (issued before revocation) remains valid until it expires naturally (up to 90 minutes). This is the revocation gap.

Access token expiry (T+Y+90 minutes maximum). The last access token expires. The application attempts renewal with the revoked refresh token — it fails. The attacker loses access. Without CAE, this is the earliest moment the attacker is fully locked out. With CAE, the lock-out occurs within minutes of revocation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Trace the complete token lifecycle for a compromised user
// Shows: initial authentication, each token renewal, and the point where access stops
union
    (SigninLogs | where UserPrincipalName == "r.williams@northgateeng.com"
        | where TimeGenerated > ago(30d)
        | project TimeGenerated, Type = "Interactive", IPAddress,
            App = AppDisplayName, Result = ResultType),
    (AADNonInteractiveUserSignInLogs | where UserPrincipalName == "r.williams@northgateeng.com"
        | where TimeGenerated > ago(30d)
        | project TimeGenerated, Type = "NonInteractive", IPAddress,
            App = AppDisplayName, Result = ResultType)
| order by TimeGenerated asc

This combined query shows the complete sign-in history — interactive AND non-interactive. The pattern you see: one interactive sign-in (authentication), followed by dozens of non-interactive sign-ins (token renewals). After revocation: non-interactive sign-ins start failing. After account re-enablement: a new interactive sign-in from the user’s legitimate device.

Check your understanding

1. You revoked all tokens and reset the password on 28 February. On 20 March, the attacker accesses the mailbox via a non-interactive sign-in. What is the most likely persistence mechanism?

An OAuth application consent granted during the original compromise. The application authenticates with its own client credentials — independent of the user's password and tokens. Password reset and token revocation do not affect it. The application has persistent API access to the mailbox. Revoke the consent in Entra ID → Enterprise Applications.
The refresh token was not actually revoked
The attacker guessed the new password
MFA was not re-registered