Analytic rule catalog
[Entra ID] Suspicious Continuous OAuth Token Usage
Back
| Id | 67802748-435b-4f80-9f61-b9a9ac6ea15c |
| Rulename | [Entra ID] Suspicious Continuous OAuth Token Usage |
| Description | Detects repeated use of the same OAuth token across different IPs or locations over time. This can indicate token theft or session abuse. |
| Severity | High |
| Tactics | CredentialAccess |
| Techniques | T1606 |
| Required data connectors | AzureActiveDirectory |
| Kind | Scheduled |
| Query frequency | 1d |
| Query period | 1d |
| Trigger threshold | 0 |
| Trigger operator | gt |
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/eDCRule/Analytic%20Rules/%5BEntra%20ID%5D%20Suspicious%20Continuous%20OAuth%20Token%20Usage.yaml |
| Version | 1.0.0 |
| Arm template | 67802748-435b-4f80-9f61-b9a9ac6ea15c.json |
// =====================
// Low-noise Token Reuse Detector (fixed timespan calc)
// =====================
let GapDays = 1;
let MinIPChanges = 2;
let MinLocChanges = 2;
let ExcludeApps = dynamic([
"Microsoft Authentication Broker",
"Microsoft Teams",
"Office 365"
]);
let Src =
union isfuzzy=true
AADNonInteractiveUserSignInLogs,
SigninLogs
| where ResultType == 0
| where isnotempty(UniqueTokenIdentifier);
let Past =
Src
| where TimeGenerated between (ago(14d) .. ago(1d))
| summarize
PastLastSeen = max(TimeGenerated),
PastUPNs = make_set(UserPrincipalName, 20),
PastIPs = make_set(IPAddress, 50),
PastLocs = make_set(tostring(Location), 50),
PastApps = make_set(AppDisplayName, 50)
by UniqueTokenIdentifier;
let Last24h =
Src
| where TimeGenerated >= ago(1d)
//| where AppDisplayName !in (ExcludeApps)
| summarize
CurrentFirstSeen = min(TimeGenerated),
CurrentLastSeen = max(TimeGenerated),
CurrentUPNs = make_set(UserPrincipalName, 20),
CurrentIPs = make_set(IPAddress, 50),
CurrentLocs = make_set(tostring(Location), 50),
CurrentApps = make_set(AppDisplayName, 50),
IPCount = dcount(IPAddress),
LocCount = dcount(tostring(Location))
by UniqueTokenIdentifier, ResultType;
Last24h
| join kind=inner Past on UniqueTokenIdentifier
| extend Gap = CurrentFirstSeen - PastLastSeen
| extend GapThreshold = totimespan(strcat(GapDays, "d"))
| where Gap >= GapThreshold
| where IPCount >= MinIPChanges or LocCount >= MinLocChanges
| extend NewIPs = set_difference(CurrentIPs, PastIPs)
| extend NewLocs = set_difference(CurrentLocs, PastLocs)
| where array_length(NewIPs) > 0 or array_length(NewLocs) > 0
| extend
CurrentUPNCount = array_length(CurrentUPNs),
PastUPNCount = array_length(PastUPNs)
| project
UniqueTokenIdentifier,
PastLastSeen,
CurrentFirstSeen,
CurrentLastSeen,
Gap,
GapThreshold,
CurrentUPNs,
CurrentUPNCount,
CurrentIPs,
IPCount,
NewIPs,
CurrentLocs,
LocCount,
NewLocs,
CurrentApps,
PastApps,
PastUPNs,
ResultType
| order by Gap desc, CurrentLastSeen desc
| extend UserNames = strcat_array(CurrentUPNs, ",")
| extend NewIP = strcat_array(NewIPs, ",")
| extend FirstNewIP = tostring(NewIPs[0])
| extend NewLoc = strcat_array(NewLocs, ",")
| extend PastUPN = strcat_array(PastUPNs, ",")
| extend Source_Network_IPLocation = ""
| project
Alert_Time_TW = datetime_utc_to_local(CurrentLastSeen, 'Asia/Taipei'),
Alert_Time_UTC0 = CurrentLastSeen,
Alert_Category_en = "Entra ID",
Alert_SubCategory_en = "Anomaly Network Access User",
Alert_Name_en = "Abnormal Sign-in Token Reuse",
Alert_Description_en=strcat(
"At Taiwan time: ",
format_datetime(datetime_utc_to_local(CurrentLastSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
", detected suspected Token Reuse behavior (UniqueTokenIdentifier appeared repeatedly and the interval reached the threshold).",
"Token:",
iff(isnotempty(UniqueTokenIdentifier), UniqueTokenIdentifier, "<NoTokenId>"),
", users (last 2 days): ",
iff(isnotempty(tostring(UserNames)), tostring(UserNames), "<NoUserNames>"),
", previous last seen: ",
format_datetime(datetime_utc_to_local(PastLastSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
", current first seen: ",
format_datetime(datetime_utc_to_local(CurrentFirstSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
", gap: ",
tostring(Gap),
" days",
", IP count: ",
tostring(IPCount),
", current sign-in IP: ",
iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIP>"),
", location count: ",
tostring(LocCount),
", current sign-in location: ",
iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLoc>"),
"."
),
Alert_TriageStep_en=strcat(
" 1. Check whether this is truly token reuse. The reused IP is: ",
iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIPs>"),
", current sign-in location: ",
iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLocs>"),
" to determine whether it is an uncommon source, cross-country/cross-region, or anonymous cloud egress.",
" 2. Check whether multiple accounts share the same token. Current triggering account count: ",
tostring(CurrentUPNCount),
", accounts that previously triggered this token: ",
tostring(PastUPN),
"; if the UPN count is greater than 1, prioritize suspicion of token leakage or proxy/automation abuse.",
" 3. Check the applications currently involved in sign-in: ",
iff(isnotempty(tostring(CurrentApps)), tostring(CurrentApps), "<NoCurrentApps>"),
"previous sign-in applications: ",
iff(isnotempty(tostring(PastApps)), tostring(PastApps), "<NoPastApps>"),
" to determine whether they include administrative/highly sensitive applications or abnormally newly added apps."
),
Alert_Containment_en=strcat(
"1. Immediately revoke sign-in tokens/sessions and force re-sign-in for users (last 2 days): ",
iff(isnotempty(tostring(UserNames)), tostring(UserNames), "<NoUserNames>"),
" to block continued access using the reused token. ",
"2. If the current sign-in IP or location is abnormal, immediately block the current sign-in IP: ",
iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIP>"),
", current sign-in location: ",
iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLoc>"),
", and tighten Conditional Access (MFA/compliant device/named location). ",
"3. Immediately require account security recovery: reset the password, re-register MFA, and check for suspicious devices or added authentication methods. "
),
Alert_Remediation_en=strcat(
"1. Strengthen risk-based access: establish Conditional Access policies to block new IPs/new locations. ",
"2. Implement token protection and endpoint governance: promote compliant devices, reduce long-lived token risk, and restrict access from unmanaged devices or legacy clients. ",
"3. Inventory automation and applications: regularly check for abnormally added applications and high-privilege applications, and remove unnecessary permissions and old credentials. ",
"4. Establish automated response: automatically revoke tokens, block IPs, notify users for confirmation, and create incident tickets for follow-up investigation when alerts trigger."
),
Event_Code = ResultType,
//Event_Description = ResultDescription,
Event_TimeRange_Start_UTC0 = CurrentFirstSeen,
Event_TimeRange_End_UTC0 = CurrentLastSeen,
Event_TimeRange_Start_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),
Event_TimeRange_End_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),
Source_Identity_FullName = UserNames,
Source_Network_IPAddress = NewIP,
Source_Network_IPLocation = Source_Network_IPLocation,
Target_Identity_FullName = UniqueTokenIdentifier,
//Target_Network_IPAddress = UniqueTokenIdentifier,
//Target_Resource_ID = "",
Target_Resource_Name = "Microsoft Entra ID",
Target_Resource_Type = "Microsoft Entra ID"
kind: Scheduled
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/eDCRule/Analytic%20Rules/%5BEntra%20ID%5D%20Suspicious%20Continuous%20OAuth%20Token%20Usage.yaml
version: 1.0.0
status: Available
requiredDataConnectors:
- dataTypes:
- AADNonInteractiveUserSignInLogs
- SigninLogs
connectorId: AzureActiveDirectory
alertDetailsOverride:
alertDynamicProperties: []
relevantTechniques:
- T1606
query: |
// =====================
// Low-noise Token Reuse Detector (fixed timespan calc)
// =====================
let GapDays = 1;
let MinIPChanges = 2;
let MinLocChanges = 2;
let ExcludeApps = dynamic([
"Microsoft Authentication Broker",
"Microsoft Teams",
"Office 365"
]);
let Src =
union isfuzzy=true
AADNonInteractiveUserSignInLogs,
SigninLogs
| where ResultType == 0
| where isnotempty(UniqueTokenIdentifier);
let Past =
Src
| where TimeGenerated between (ago(14d) .. ago(1d))
| summarize
PastLastSeen = max(TimeGenerated),
PastUPNs = make_set(UserPrincipalName, 20),
PastIPs = make_set(IPAddress, 50),
PastLocs = make_set(tostring(Location), 50),
PastApps = make_set(AppDisplayName, 50)
by UniqueTokenIdentifier;
let Last24h =
Src
| where TimeGenerated >= ago(1d)
//| where AppDisplayName !in (ExcludeApps)
| summarize
CurrentFirstSeen = min(TimeGenerated),
CurrentLastSeen = max(TimeGenerated),
CurrentUPNs = make_set(UserPrincipalName, 20),
CurrentIPs = make_set(IPAddress, 50),
CurrentLocs = make_set(tostring(Location), 50),
CurrentApps = make_set(AppDisplayName, 50),
IPCount = dcount(IPAddress),
LocCount = dcount(tostring(Location))
by UniqueTokenIdentifier, ResultType;
Last24h
| join kind=inner Past on UniqueTokenIdentifier
| extend Gap = CurrentFirstSeen - PastLastSeen
| extend GapThreshold = totimespan(strcat(GapDays, "d"))
| where Gap >= GapThreshold
| where IPCount >= MinIPChanges or LocCount >= MinLocChanges
| extend NewIPs = set_difference(CurrentIPs, PastIPs)
| extend NewLocs = set_difference(CurrentLocs, PastLocs)
| where array_length(NewIPs) > 0 or array_length(NewLocs) > 0
| extend
CurrentUPNCount = array_length(CurrentUPNs),
PastUPNCount = array_length(PastUPNs)
| project
UniqueTokenIdentifier,
PastLastSeen,
CurrentFirstSeen,
CurrentLastSeen,
Gap,
GapThreshold,
CurrentUPNs,
CurrentUPNCount,
CurrentIPs,
IPCount,
NewIPs,
CurrentLocs,
LocCount,
NewLocs,
CurrentApps,
PastApps,
PastUPNs,
ResultType
| order by Gap desc, CurrentLastSeen desc
| extend UserNames = strcat_array(CurrentUPNs, ",")
| extend NewIP = strcat_array(NewIPs, ",")
| extend FirstNewIP = tostring(NewIPs[0])
| extend NewLoc = strcat_array(NewLocs, ",")
| extend PastUPN = strcat_array(PastUPNs, ",")
| extend Source_Network_IPLocation = ""
| project
Alert_Time_TW = datetime_utc_to_local(CurrentLastSeen, 'Asia/Taipei'),
Alert_Time_UTC0 = CurrentLastSeen,
Alert_Category_en = "Entra ID",
Alert_SubCategory_en = "Anomaly Network Access User",
Alert_Name_en = "Abnormal Sign-in Token Reuse",
Alert_Description_en=strcat(
"At Taiwan time: ",
format_datetime(datetime_utc_to_local(CurrentLastSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
", detected suspected Token Reuse behavior (UniqueTokenIdentifier appeared repeatedly and the interval reached the threshold).",
"Token:",
iff(isnotempty(UniqueTokenIdentifier), UniqueTokenIdentifier, "<NoTokenId>"),
", users (last 2 days): ",
iff(isnotempty(tostring(UserNames)), tostring(UserNames), "<NoUserNames>"),
", previous last seen: ",
format_datetime(datetime_utc_to_local(PastLastSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
", current first seen: ",
format_datetime(datetime_utc_to_local(CurrentFirstSeen, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
", gap: ",
tostring(Gap),
" days",
", IP count: ",
tostring(IPCount),
", current sign-in IP: ",
iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIP>"),
", location count: ",
tostring(LocCount),
", current sign-in location: ",
iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLoc>"),
"."
),
Alert_TriageStep_en=strcat(
" 1. Check whether this is truly token reuse. The reused IP is: ",
iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIPs>"),
", current sign-in location: ",
iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLocs>"),
" to determine whether it is an uncommon source, cross-country/cross-region, or anonymous cloud egress.",
" 2. Check whether multiple accounts share the same token. Current triggering account count: ",
tostring(CurrentUPNCount),
", accounts that previously triggered this token: ",
tostring(PastUPN),
"; if the UPN count is greater than 1, prioritize suspicion of token leakage or proxy/automation abuse.",
" 3. Check the applications currently involved in sign-in: ",
iff(isnotempty(tostring(CurrentApps)), tostring(CurrentApps), "<NoCurrentApps>"),
"previous sign-in applications: ",
iff(isnotempty(tostring(PastApps)), tostring(PastApps), "<NoPastApps>"),
" to determine whether they include administrative/highly sensitive applications or abnormally newly added apps."
),
Alert_Containment_en=strcat(
"1. Immediately revoke sign-in tokens/sessions and force re-sign-in for users (last 2 days): ",
iff(isnotempty(tostring(UserNames)), tostring(UserNames), "<NoUserNames>"),
" to block continued access using the reused token. ",
"2. If the current sign-in IP or location is abnormal, immediately block the current sign-in IP: ",
iff(isnotempty(tostring(NewIP)), tostring(NewIP), "<NoNewIP>"),
", current sign-in location: ",
iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), "<NoNewLoc>"),
", and tighten Conditional Access (MFA/compliant device/named location). ",
"3. Immediately require account security recovery: reset the password, re-register MFA, and check for suspicious devices or added authentication methods. "
),
Alert_Remediation_en=strcat(
"1. Strengthen risk-based access: establish Conditional Access policies to block new IPs/new locations. ",
"2. Implement token protection and endpoint governance: promote compliant devices, reduce long-lived token risk, and restrict access from unmanaged devices or legacy clients. ",
"3. Inventory automation and applications: regularly check for abnormally added applications and high-privilege applications, and remove unnecessary permissions and old credentials. ",
"4. Establish automated response: automatically revoke tokens, block IPs, notify users for confirmation, and create incident tickets for follow-up investigation when alerts trigger."
),
Event_Code = ResultType,
//Event_Description = ResultDescription,
Event_TimeRange_Start_UTC0 = CurrentFirstSeen,
Event_TimeRange_End_UTC0 = CurrentLastSeen,
Event_TimeRange_Start_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),
Event_TimeRange_End_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),
Source_Identity_FullName = UserNames,
Source_Network_IPAddress = NewIP,
Source_Network_IPLocation = Source_Network_IPLocation,
Target_Identity_FullName = UniqueTokenIdentifier,
//Target_Network_IPAddress = UniqueTokenIdentifier,
//Target_Resource_ID = "",
Target_Resource_Name = "Microsoft Entra ID",
Target_Resource_Type = "Microsoft Entra ID"
tactics:
- CredentialAccess
queryFrequency: 1d
id: 67802748-435b-4f80-9f61-b9a9ac6ea15c
triggerThreshold: 0
triggerOperator: gt
name: '[Entra ID] Suspicious Continuous OAuth Token Usage'
queryPeriod: 1d
severity: High
entityMappings:
- fieldMappings:
- columnName: Source_Identity_FullName
identifier: Name
entityType: Account
- fieldMappings:
- columnName: Source_Network_IPAddress
identifier: Address
entityType: IP
description: |
Detects repeated use of the same OAuth token across different IPs or locations over time. This can indicate token theft or session abuse.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"apiVersion": "2024-01-01-preview",
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/67802748-435b-4f80-9f61-b9a9ac6ea15c')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/67802748-435b-4f80-9f61-b9a9ac6ea15c')]",
"properties": {
"alertDetailsOverride": {
"alertDynamicProperties": []
},
"alertRuleTemplateName": "67802748-435b-4f80-9f61-b9a9ac6ea15c",
"customDetails": null,
"description": "Detects repeated use of the same OAuth token across different IPs or locations over time. This can indicate token theft or session abuse.\n",
"displayName": "[Entra ID] Suspicious Continuous OAuth Token Usage",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "Source_Identity_FullName",
"identifier": "Name"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "Source_Network_IPAddress",
"identifier": "Address"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/eDCRule/Analytic%20Rules/%5BEntra%20ID%5D%20Suspicious%20Continuous%20OAuth%20Token%20Usage.yaml",
"query": "// =====================\n// Low-noise Token Reuse Detector (fixed timespan calc)\n// =====================\nlet GapDays = 1;\nlet MinIPChanges = 2;\nlet MinLocChanges = 2;\nlet ExcludeApps = dynamic([\n \"Microsoft Authentication Broker\",\n \"Microsoft Teams\",\n \"Office 365\"\n ]);\nlet Src =\n union isfuzzy=true\n AADNonInteractiveUserSignInLogs,\n SigninLogs\n | where ResultType == 0\n | where isnotempty(UniqueTokenIdentifier);\nlet Past =\n Src\n | where TimeGenerated between (ago(14d) .. ago(1d))\n | summarize\n PastLastSeen = max(TimeGenerated),\n PastUPNs = make_set(UserPrincipalName, 20),\n PastIPs = make_set(IPAddress, 50),\n PastLocs = make_set(tostring(Location), 50),\n PastApps = make_set(AppDisplayName, 50)\n by UniqueTokenIdentifier;\nlet Last24h =\n Src\n | where TimeGenerated >= ago(1d)\n //| where AppDisplayName !in (ExcludeApps)\n | summarize\n CurrentFirstSeen = min(TimeGenerated),\n CurrentLastSeen = max(TimeGenerated),\n CurrentUPNs = make_set(UserPrincipalName, 20),\n CurrentIPs = make_set(IPAddress, 50),\n CurrentLocs = make_set(tostring(Location), 50),\n CurrentApps = make_set(AppDisplayName, 50),\n IPCount = dcount(IPAddress),\n LocCount = dcount(tostring(Location))\n by UniqueTokenIdentifier, ResultType;\nLast24h\n| join kind=inner Past on UniqueTokenIdentifier\n| extend Gap = CurrentFirstSeen - PastLastSeen\n| extend GapThreshold = totimespan(strcat(GapDays, \"d\"))\n| where Gap >= GapThreshold\n| where IPCount >= MinIPChanges or LocCount >= MinLocChanges\n| extend NewIPs = set_difference(CurrentIPs, PastIPs)\n| extend NewLocs = set_difference(CurrentLocs, PastLocs)\n| where array_length(NewIPs) > 0 or array_length(NewLocs) > 0\n| extend\n CurrentUPNCount = array_length(CurrentUPNs),\n PastUPNCount = array_length(PastUPNs)\n| project\n UniqueTokenIdentifier,\n PastLastSeen,\n CurrentFirstSeen,\n CurrentLastSeen,\n Gap,\n GapThreshold,\n CurrentUPNs,\n CurrentUPNCount,\n CurrentIPs,\n IPCount,\n NewIPs,\n CurrentLocs,\n LocCount,\n NewLocs,\n CurrentApps,\n PastApps,\n PastUPNs,\n ResultType\n| order by Gap desc, CurrentLastSeen desc\n| extend UserNames = strcat_array(CurrentUPNs, \",\")\n| extend NewIP = strcat_array(NewIPs, \",\")\n| extend FirstNewIP = tostring(NewIPs[0])\n| extend NewLoc = strcat_array(NewLocs, \",\")\n| extend PastUPN = strcat_array(PastUPNs, \",\")\n| extend Source_Network_IPLocation = \"\"\n| project\n Alert_Time_TW = datetime_utc_to_local(CurrentLastSeen, 'Asia/Taipei'),\n Alert_Time_UTC0 = CurrentLastSeen,\n Alert_Category_en = \"Entra ID\",\n Alert_SubCategory_en = \"Anomaly Network Access User\",\n Alert_Name_en = \"Abnormal Sign-in Token Reuse\",\n Alert_Description_en=strcat(\n \"At Taiwan time: \",\n format_datetime(datetime_utc_to_local(CurrentLastSeen, \"Asia/Taipei\"), \"yyyy-MM-dd HH:mm:ss\"),\n \", detected suspected Token Reuse behavior (UniqueTokenIdentifier appeared repeatedly and the interval reached the threshold).\",\n \"Token:\",\n iff(isnotempty(UniqueTokenIdentifier), UniqueTokenIdentifier, \"<NoTokenId>\"),\n \", users (last 2 days): \",\n iff(isnotempty(tostring(UserNames)), tostring(UserNames), \"<NoUserNames>\"),\n \", previous last seen: \",\n format_datetime(datetime_utc_to_local(PastLastSeen, \"Asia/Taipei\"), \"yyyy-MM-dd HH:mm:ss\"),\n \", current first seen: \",\n format_datetime(datetime_utc_to_local(CurrentFirstSeen, \"Asia/Taipei\"), \"yyyy-MM-dd HH:mm:ss\"),\n \", gap: \",\n tostring(Gap),\n \" days\",\n \", IP count: \",\n tostring(IPCount),\n \", current sign-in IP: \",\n iff(isnotempty(tostring(NewIP)), tostring(NewIP), \"<NoNewIP>\"),\n \", location count: \",\n tostring(LocCount),\n \", current sign-in location: \",\n iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), \"<NoNewLoc>\"),\n \".\"\n ),\n Alert_TriageStep_en=strcat(\n \" 1. Check whether this is truly token reuse. The reused IP is: \",\n iff(isnotempty(tostring(NewIP)), tostring(NewIP), \"<NoNewIPs>\"),\n \", current sign-in location: \",\n iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), \"<NoNewLocs>\"),\n \" to determine whether it is an uncommon source, cross-country/cross-region, or anonymous cloud egress.\",\n \" 2. Check whether multiple accounts share the same token. Current triggering account count: \",\n tostring(CurrentUPNCount),\n \", accounts that previously triggered this token: \",\n tostring(PastUPN),\n \"; if the UPN count is greater than 1, prioritize suspicion of token leakage or proxy/automation abuse.\",\n \" 3. Check the applications currently involved in sign-in: \",\n iff(isnotempty(tostring(CurrentApps)), tostring(CurrentApps), \"<NoCurrentApps>\"),\n \"previous sign-in applications: \",\n iff(isnotempty(tostring(PastApps)), tostring(PastApps), \"<NoPastApps>\"),\n \" to determine whether they include administrative/highly sensitive applications or abnormally newly added apps.\"\n ),\n Alert_Containment_en=strcat(\n \"1. Immediately revoke sign-in tokens/sessions and force re-sign-in for users (last 2 days): \",\n iff(isnotempty(tostring(UserNames)), tostring(UserNames), \"<NoUserNames>\"),\n \" to block continued access using the reused token. \",\n \"2. If the current sign-in IP or location is abnormal, immediately block the current sign-in IP: \",\n iff(isnotempty(tostring(NewIP)), tostring(NewIP), \"<NoNewIP>\"),\n \", current sign-in location: \",\n iff(isnotempty(tostring(NewLoc)), tostring(NewLoc), \"<NoNewLoc>\"),\n \", and tighten Conditional Access (MFA/compliant device/named location). \",\n \"3. Immediately require account security recovery: reset the password, re-register MFA, and check for suspicious devices or added authentication methods. \"\n ),\n Alert_Remediation_en=strcat(\n \"1. Strengthen risk-based access: establish Conditional Access policies to block new IPs/new locations. \",\n \"2. Implement token protection and endpoint governance: promote compliant devices, reduce long-lived token risk, and restrict access from unmanaged devices or legacy clients. \",\n \"3. Inventory automation and applications: regularly check for abnormally added applications and high-privilege applications, and remove unnecessary permissions and old credentials. \",\n \"4. Establish automated response: automatically revoke tokens, block IPs, notify users for confirmation, and create incident tickets for follow-up investigation when alerts trigger.\"\n ),\n Event_Code = ResultType,\n //Event_Description = ResultDescription,\n Event_TimeRange_Start_UTC0 = CurrentFirstSeen,\n Event_TimeRange_End_UTC0 = CurrentLastSeen,\n Event_TimeRange_Start_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),\n Event_TimeRange_End_TW = datetime_utc_to_local(CurrentFirstSeen, 'Asia/Taipei'),\n Source_Identity_FullName = UserNames,\n Source_Network_IPAddress = NewIP,\n Source_Network_IPLocation = Source_Network_IPLocation,\n Target_Identity_FullName = UniqueTokenIdentifier,\n //Target_Network_IPAddress = UniqueTokenIdentifier,\n //Target_Resource_ID = \"\",\n Target_Resource_Name = \"Microsoft Entra ID\",\n Target_Resource_Type = \"Microsoft Entra ID\"\n",
"queryFrequency": "P1D",
"queryPeriod": "P1D",
"severity": "High",
"status": "Available",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"CredentialAccess"
],
"techniques": [
"T1606"
],
"templateVersion": "1.0.0",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}