Detect Suspicious Foci Token Logins
Query
// TimeDiff threshold in minutes. Needed for some environments with a lot of BP hits on long time frames. Used in scenario where you expect adversary to quickly request new tokens after first token request.
let maxTimeDiff = 90;
// External lookup to get list of FOCI applications
let FociClientApplications = toscalar(externaldata(client_id: string)
[@"https://raw.githubusercontent.com/secureworks/family-of-client-ids-research/refs/heads/main/known-foci-clients.csv"] with (format="csv", ignoreFirstRecord=true)
//| project-rename FociClientId = client_id
| summarize FociClientId = make_list(client_id)
);
// Get all token requests for Foci clients
let FociTokenRequest = materialize (
AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(6h)
// Filter for sign-ins to home tenant only
| where HomeTenantId == ResourceTenantId
// Lookup for FOCI client
| where AppId in (FociClientApplications)
);
FociTokenRequest
// First get all initial logins without refresh tokens as incomming token type
| where IncomingTokenType == "none"
// Then get logins with refresh tokens for same session
| join kind=inner (
FociTokenRequest
| where IncomingTokenType != "none"
| project-rename
SecondAppDisplayName = AppDisplayName,
SecondRequestTimeGenerated = TimeGenerated,
SecondAppId = AppId
)
on SessionId, UserPrincipalName
// Exclude when First App ID and Second are the same
| where AppDisplayName != SecondAppDisplayName
// Only get requests where refresh token was used after first sign-in
| extend TimeDiff = datetime_diff('minute', SecondRequestTimeGenerated, TimeGenerated)
| where TimeDiff >= 0 //and TimeDiff <= maxTimeDiff // Remove from comment you want to apply time difference restriction
// Only project needed columns
| project
FirstRequestTimeGenerated = TimeGenerated,
FirstResult = ResultType,
FirstResultDescription = ResultDescription,
Identity,
Location,
FirstAppDisplayName = AppDisplayName,
FirstAppId = AppId,
ClientAppUsed,
DeviceDetail,
SecondDeviceDetail = DeviceDetail1,
IPAddress,
LocationDetails,
UserAgent,
SecondRequestTimeGenerated,
SecondResult = ResultType,
SecondResultDescription = ResultDescription1,
SecondAppDisplayName,
SecondAppId,
SeconIncomingTokenType = IncomingTokenType1,
SessionId,
TimeDiff,
AuthenticationProcessingDetails,
SecondAuthenticationProcessingDetails = AuthenticationProcessingDetails1
// Flag logins to the following applications as second login (since they are very popular for attackers and we rather not see logins to these via foci tokens)
| where SecondAppDisplayName in ("Microsoft Azure CLI", "Microsoft Azure PowerShell", "Office 365 Management")
// ENVIRONMENT SPECIFIC FINETUNING - BEGIN
// Most BP triggers are mainly on Microsoft Azure CLI, so we provide two ways of handling these BP detections (strongly depends on environment)
// OPTION 1 - Flag login to Azure CLI using 'Global Administrator' ID in token scope
//| where (SecondAppDisplayName in ("Microsoft Azure PowerShell", "Office 365 Management") or (SecondAppDisplayName == "Microsoft Azure CLI" and SecondAuthenticationProcessingDetails contains "62e90394-69f5-4237-9190-012177145e10"))
// OPTION 2 - Flag login to Azure CLI using 'Global Administrator' ID in token scope from non compliant device
//| where (SecondAppDisplayName in ("Microsoft Azure PowerShell", "Office 365 Management") or (SecondAppDisplayName == "Microsoft Azure CLI" and SecondAuthenticationProcessingDetails contains "62e90394-69f5-4237-9190-012177145e10" and todynamic(SecondDeviceDetail).isCompliant != "true"))
// ENVIRONMENT SPECIFIC FINETUNING - ENDAbout this query
Explanation
This query is designed to detect suspicious logins using FOCI tokens, which are special tokens that allow multiple applications within the same family to share authentication tokens. This can be exploited by attackers to access multiple applications without re-authenticating.
Here's a simplified explanation of the query:
-
Purpose: The query aims to identify suspicious use of FOCI tokens by detecting when a user logs into one application and then quickly logs into another, potentially more sensitive application, using a refresh token.
-
Process:
- It first retrieves all login attempts using FOCI tokens within the last 6 hours.
- It filters these logins to only include those where the initial login did not use a refresh token.
- It then looks for subsequent logins within the same session that did use a refresh token.
- If the second login is to an application commonly targeted by attackers (like Microsoft Azure CLI, Microsoft Azure PowerShell, or Office 365 Management), it flags the event as suspicious.
-
Customization Options:
- The query can be fine-tuned to only alert if the time between the first and second login is within a specified limit (default is 90 minutes).
- It can also be set to only alert if the second login involves a Global Administrator scope or comes from a non-compliant device.
-
Risk: The query helps in identifying potential misuse of FOCI tokens, which could indicate an adversary trying to access sensitive applications without proper authorization.
Overall, this query is a security measure to detect and flag potentially unauthorized access patterns using FOCI tokens in a cloud environment.
Details

Robbe Van den Daele
Released: May 6, 2025
Tables
Keywords
Operators