14.9 Detection Engineering

3-5 hours · Module 14

Detection Engineering

Five analytics rules that detect token replay patterns. These complement the AiTM detection rules (M12.13) and BEC detection rules (M13.10) — together they cover the full attack chain from credential theft through token abuse to financial fraud.

Required role: Microsoft Sentinel Contributor.


Rule 1: Multiple IPs Within Single User Session Window

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Scheduled Rule: Same user, successful sign-ins from 2+ IPs within 10 minutes
// Schedule: 5 minutes / Lookback: 15 minutes
// Severity: High
// MITRE: T1550.001 (Application Access Token)
let KnownCorpIPs = _GetWatchlist('CorporateExternalIPs') | project SearchKey;
SigninLogs
| where TimeGenerated > ago(15m)
| where ResultType == "0"
| where IPAddress !in (KnownCorpIPs)
| summarize
    IPs = make_set(IPAddress, 10),
    IPCount = dcount(IPAddress),
    Apps = make_set(AppDisplayName, 5),
    FirstSignin = min(TimeGenerated),
    LastSignin = max(TimeGenerated)
    by UserPrincipalName
| where IPCount > 1
| extend GapMinutes = datetime_diff('minute', LastSignin, FirstSignin)
| where GapMinutes < 10
| project UserPrincipalName, IPs, IPCount, GapMinutes, Apps

Entity mapping: Account → UserPrincipalName (FullName). Custom details: IPs, IPCount, GapMinutes.


Rule 2: Non-Interactive Sign-In from Non-Corporate IP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Scheduled Rule: Non-interactive auth from external IP  token replay indicator
// Schedule: 15 minutes / Lookback: 20 minutes
// Severity: Medium
// MITRE: T1550.001
let KnownCorpIPs = _GetWatchlist('CorporateExternalIPs') | project SearchKey;
AADNonInteractiveUserSignInLogs
| where ResultType == "0"
| where IPAddress !in (KnownCorpIPs)
| where not(ipv4_is_private(IPAddress))
| extend UserAgent = tostring(DeviceDetail.browser)
// Filter for suspicious user agents (scripted access)
| where UserAgent has_any ("python", "curl", "wget", "PowerShell",
    "Go-http-client", "axios", "node-fetch")
    or isempty(UserAgent)
| project TimeGenerated, UserPrincipalName, IPAddress,
    AppDisplayName, ResourceDisplayName, UserAgent

Entity mapping: Account → UserPrincipalName (FullName). IP → IPAddress (Address). Why this rule matters: Scripted access from external IPs is almost always token abuse. Legitimate M365 applications use recognisable user agents. Python/curl/empty user agents from non-corporate IPs are attacker tools.


Rule 3: Token Used After Password Reset

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Scheduled Rule: Successful sign-in after password reset without new MFA
// Schedule: 15 minutes / Lookback: 1 hour
// Severity: High
// MITRE: T1550.001
let PasswordResets = AuditLogs
| where OperationName has "Reset password"
| where Result == "success"
| extend ResetUser = tostring(TargetResources[0].userPrincipalName)
| extend ResetTime = TimeGenerated
| project ResetUser, ResetTime;
SigninLogs
| where ResultType == "0"
| where AuthenticationRequirement == "singleFactorAuthentication"
| join kind=inner PasswordResets
    on $left.UserPrincipalName == $right.ResetUser
| where TimeGenerated > ResetTime
| where TimeGenerated < (ResetTime + 2h)
| project UserPrincipalName, ResetTime, SigninTime = TimeGenerated,
    IPAddress, AppDisplayName, AuthenticationRequirement,
    MinutesAfterReset = datetime_diff('minute', TimeGenerated, ResetTime)

What this detects: A successful sign-in that occurs AFTER a password reset but does NOT require MFA. This means the sign-in used a pre-existing token (issued before the reset) — the token survived the password change. If the user genuinely re-authenticated with their new password, MFA would be required. Single-factor auth after reset = token replay.


Rule 4: Refresh Token Usage from New Device

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Scheduled Rule: Non-interactive sign-in from device ID not seen in 90 days
// Schedule: 1 hour / Lookback: 1 hour 15 minutes
// Severity: Medium
// MITRE: T1528 (Steal Application Access Token)
let KnownDevices = AADNonInteractiveUserSignInLogs
| where TimeGenerated between(ago(90d) .. ago(1d))
| where ResultType == "0"
| summarize KnownDeviceIds = make_set(tostring(DeviceDetail.deviceId), 100)
    by UserPrincipalName;
AADNonInteractiveUserSignInLogs
| where ResultType == "0"
| extend DeviceId = tostring(DeviceDetail.deviceId)
| where isnotempty(DeviceId)
| join kind=inner KnownDevices on UserPrincipalName
| where DeviceId !in (KnownDeviceIds)
| project TimeGenerated, UserPrincipalName, IPAddress,
    DeviceId, AppDisplayName, ResourceDisplayName

Rule 5: Session Anomaly — High-Volume Resource Access from Single Token

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Scheduled Rule: Anomalous resource access volume  token being used for bulk operations
// Schedule: 1 hour / Lookback: 1 hour 15 minutes
// Severity: Medium
// MITRE: T1078.004 (Valid Accounts: Cloud Accounts)
AADNonInteractiveUserSignInLogs
| where ResultType == "0"
| where IPAddress !in (_GetWatchlist('CorporateExternalIPs') | project SearchKey)
| summarize
    ResourceCount = dcount(ResourceDisplayName),
    Resources = make_set(ResourceDisplayName, 10),
    SigninCount = count()
    by UserPrincipalName, IPAddress
| where ResourceCount > 5 and SigninCount > 20
| project UserPrincipalName, IPAddress, ResourceCount, SigninCount, Resources

What this detects: A single IP accessing many different resources for one user in a short period. Legitimate users typically access 2-3 resources per hour. An attacker enumerating all available resources with a stolen token accesses 5+ resources in rapid succession.


Deployment checklist

For each rule: create in Sentinel Analytics, configure entity mapping, set MITRE technique, test against 30 days data, enable, monitor 7 days.

Subsection artifact: 5 deployable token replay detection rules. These complement the M11 AiTM rules and M12 BEC rules — together, the three detection packs cover the complete attack chain.

Compliance mapping: NIST CSF DE.AE-2 (Anomalous activity detected). ISO 27001 A.8.16 (Monitoring).