hunt-2025-034 v1.0 by Paul Newton

Potential Token Replay from Different ASNs

Platform

Entra

Data Sources

AADNonInteractiveUserSignInLogs MicrosoftGraphActivityLogs

MITRE ATT&CK

Tactics

Defense Evasion

Techniques

Threat Actors

APT29/CozyBear/MidnightBlizzard

Tags

#entra #cloud #identity #oauth

Hunt Hypothesis

Threat Actors will use token theft as a means of initial access, privilege escalation and persistence. Poorly secured tokens by third parties can increase the risk of token theft and replay.

Potential Token Replay from Different ASNs

Analytic #1

This analytic looks for cases where we see two different ASNs for the initial authentication to an app in Entra, and then a second and different ASN, for the same token, in GraphAPI logs. This could be indicative of token replay. There’s also an exclusion list to bin off noise from Microsoft, any any other ranges you’re not interested in.

Detection Queries

KQL
let ExcludedGraphASNs = dynamic([8075]);
let CIDRASN = (externaldata (CIDR:string, CIDRASN:int, CIDRASNName:string)
['https://firewalliplists.gypthecat.com/lists/kusto/kusto-cidr-asn.csv.zip']
with (ignoreFirstRecord=true));
let GraphLogs =
MicrosoftGraphActivityLogs
| evaluate ipv4_lookup(CIDRASN, IPAddress, CIDR, return_unmatched=true)
| extend GraphASN = CIDRASN
| extend GraphASNName = CIDRASNName
| project SignInActivityId, GraphIPAddress = IPAddress, GraphASN, GraphASNName,
        Location, ResponseStatusCode, UserAgent, RequestMethod, RequestUri, TimeGenerated;
let AADLogs =
AADNonInteractiveUserSignInLogs
| extend TokenDetails = parse_json(TokenProtectionStatusDetails)
| extend signInSessionStatus = tostring(TokenDetails.signInSessionStatus)
| project UniqueTokenIdentifier, AADIPAddress = IPAddress,
        AADLocation = Location, AADASN = AutonomousSystemNumber,
        DeviceName = tostring(parse_json(DeviceDetail).displayName),
        signInSessionStatus, Identity, AppDisplayName;
GraphLogs
| join kind=inner (AADLogs) on $left.SignInActivityId == $right.UniqueTokenIdentifier
| where isnotempty(GraphASN) and isnotempty(AADASN)
| where GraphASN != AADASN
| where GraphASN !in (ExcludedGraphASNs)
| project TimeGenerated, SignInActivityId, Identity, AppDisplayName,
    GraphIPAddress, GraphASN, GraphASNName,
    AADIPAddress, AADASN,
    Location, AADLocation,
    DeviceName, signInSessionStatus,
    RequestMethod, RequestUri, ResponseStatusCode, UserAgent
| order by TimeGenerated desc

Triage Steps

  1. Review the ASN mismatch between the initial authentication location and Graph API access location
  2. Check if the user has legitimate reasons to travel between these geographic locations
  3. Investigate the application involved and verify if it's authorized within your environment
  4. Review the Graph API requests to identify what data was accessed during the suspected replay
  5. Check if the UserAgent indicates automated tooling or custom scripts
  6. Verify the token protection status - unbound tokens are easier to replay
  7. Look for other suspicious activity from the same user or application within the same timeframe
  8. If confirmed malicious, revoke the application's access and reset user credentials

True Positive Example

Example 1

Log Entry:
{
  "log_entry": {
    "TimeGenerated": "06/12/2025, 14:14:07.035",
    "SignInActivityId": "70ObFHiCDUW42aMM2OEcAA",
    "Identity": "Hunter",
    "AppDisplayName": "TruffleHog Test App",
    "GraphIPAddress": "79.135.105.18",
    "GraphASN": 212238,
    "GraphASNName": "Datacamp Limited",
    "AADIPAddress": "85.139.240.135",
    "AADASN": 2856,
    "Location": "UAE North",
    "AADLocation": "GB",
    "DeviceName": "HUNTERWORK",
    "signInSessionStatus": "bound",
    "RequestMethod": "GET",
    "RequestUri": "https://graph.microsoft.com/v1.0/me/followedSites",
    "ResponseStatusCode": 403,
    "UserAgent": "data_exfil_tool"
  }
}
Analysis:

Token replay detected from different ASN. The access token was obtained from ASN 2856 (GB) but replayed from ASN 212238 (UAE North/Datacamp Limited), indicating potential token theft and replay attack.

Example 2

Log Entry:
{
  "log_entry": {
    "TimeGenerated": "06/12/2025, 14:14:05.738",
    "SignInActivityId": "70ObFHiCDUW42aMM2OEcAA",
    "Identity": "Hunter",
    "AppDisplayName": "TruffleHog Test App",
    "GraphIPAddress": "79.135.105.18",
    "GraphASN": 212238,
    "GraphASNName": "Datacamp Limited",
    "AADIPAddress": "85.139.240.135",
    "AADASN": 2856,
    "Location": "UAE North",
    "AADLocation": "GB",
    "DeviceName": "HUNTERWORK",
    "signInSessionStatus": "bound",
    "RequestMethod": "GET",
    "RequestUri": "https://graph.microsoft.com/v1.0/me/drive/recent?%24top=5",
    "ResponseStatusCode": 200,
    "UserAgent": "data_exfil_tool"
  }
}
Analysis:

Successful token replay accessing user's recent files. The attacker successfully accessed sensitive data from a different geographic location using the stolen token.

Example 3

Log Entry:
{
  "log_entry": {
    "TimeGenerated": "06/12/2025, 14:13:54.940",
    "SignInActivityId": "70ObFHiCDUW42aMM2OEcAA",
    "Identity": "Hunter",
    "AppDisplayName": "TruffleHog Test App",
    "GraphIPAddress": "79.135.105.18",
    "GraphASN": 212238,
    "GraphASNName": "Datacamp Limited",
    "AADIPAddress": "85.139.240.135",
    "AADASN": 2856,
    "Location": "UAE North",
    "AADLocation": "GB",
    "DeviceName": "HUNTERWORK",
    "signInSessionStatus": "bound",
    "RequestMethod": "GET",
    "RequestUri": "https://graph.microsoft.com/v1.0/me/drive/root/search(q='%7B%7D')?%24top=5",
    "ResponseStatusCode": 200,
    "UserAgent": "data_exfil_tool"
  }
}
Analysis:

Attacker performing search operations on user's OneDrive from unauthorized location. The custom UserAgent 'data_exfil_tool' combined with ASN mismatch indicates automated data exhilaration activity.