FOCI Cross-Resource Token Pivot — Non-Interactive Sign-In Burst
Platform
Data Sources
Related Blog Post
Read the full blog post about this hunt →Hunt Hypothesis
Microsoft's Family of Client IDs (FOCI) allows a refresh token obtained via one first-party application to be exchanged for access tokens scoped to any other service in the family — Exchange, Teams, Azure Management, Key Vault, Office Management — without re-authenticating the user. After capturing a single device code token, an attacker can silently pivot to every Microsoft service the victim can access with a single API call per resource. This produces a burst of non-interactive sign-ins from the same client_id against multiple distinct resource endpoints within a short window — a pattern that does not occur in legitimate first-party application behaviour. FOCI-capable client IDs include Azure CLI (04b07795-8542-4bc9-aaaa-59d79c0a3df9), Azure PowerShell (1950a258-227b-4e31-a9cf-717495945fc2), and Microsoft Authentication Broker (29d9ed98-a469-4536-ade2-f981bc1d605e).
FOCI Multi-Resource Token Exchange Burst
Analytic #1Detects a single OAuth client_id being used to exchange a refresh token for access tokens against three or more distinct resource endpoints within a 10-minute window, via non-interactive sign-ins. This is the direct behavioural signature of FOCI pivoting — one captured token silently unlocked across multiple Microsoft services.
Detection Queries
let FOCIAppIds = dynamic([
"04b07795-8542-4bc9-aaaa-59d79c0a3df9", // Azure CLI
"1950a258-227b-4e31-a9cf-717495945fc2", // Azure PowerShell
"29d9ed98-a469-4536-ade2-f981bc1d605e", // Microsoft Authentication Broker
"d3590ed6-52b3-4102-aeff-aad2292ab01c", // Microsoft Office
"04b07795-8ddb-461a-bbee-02f9e1bf7b46" // Azure CLI (alt)
]);
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
| where TimeGenerated > ago(1h)
| evaluate ipv4_lookup(CIDRASN, IPAddress, CIDR, return_unmatched=true)
| extend GraphASN = CIDRASN, GraphASNName = CIDRASNName
| project SignInActivityId, GraphIPAddress = IPAddress, GraphASN, GraphASNName,
ResponseStatusCode, UserAgent, RequestMethod, RequestUri;
let AADLogs =
AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(1h)
| where ResultType == 0
| where AppId in (FOCIAppIds)
| evaluate ipv4_lookup(CIDRASN, IPAddress, CIDR, return_unmatched=true)
| extend ASN = CIDRASN, ASNName = CIDRASNName;
AADLogs
| join kind=leftouter (GraphLogs) on $left.CorrelationId == $right.SignInActivityId
| summarize
Resources = make_set(ResourceDisplayName),
ResourceCount = dcount(ResourceDisplayName),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
IPs = make_set(IPAddress),
ASNs = make_set(ASNName),
UserAgents = make_set(UserAgent),
RequestUris = make_set(RequestUri),
SignInCount = count(),
AppIdCount = dcount(AppId)
by UserPrincipalName, AppId, AppDisplayName, bin(TimeGenerated, 10m)
| where ResourceCount >= 3
| where AppIdCount == 1
| extend SpanMinutes = datetime_diff('minute', LastSeen, FirstSeen)
| project FirstSeen, LastSeen, SpanMinutes,
UserPrincipalName, AppDisplayName, AppId,
AppIdCount, ResourceCount, Resources,
IPs, ASNs, UserAgents, RequestUris, SignInCount
| order by FirstSeen desc
Triage Steps
- Review AppId against known FOCI client IDs — Azure CLI (04b07795-8542-4bc9-aaaa-59d79c0a3df9), Azure PowerShell (1950a258-227b-4e31-a9cf-717495945fc2), Microsoft Authentication Broker (29d9ed98-a469-4536-ade2-f981bc1d605e), Microsoft Office (d3590ed6-52b3-4102-aeff-aad2292ab01c)
- Check IPs against the user's normal sign-in baseline — attacker FOCI pivots will originate from the C2 server or residential proxy, not the user's device
- Review Resources — a pivot covering Exchange + Teams + Azure Management in one burst is high confidence; Exchange only may be lower confidence
- Check whether a device code sign-in (interactive, AuthenticationProtocol == "deviceCode") for the same user preceded this burst within the last hour
- If confirmed, revoke all refresh tokens via Entra and audit downstream Graph API activity (mail search, file access, Teams messages) for the affected user
- Review AADNonInteractiveUserSignInLogs for the same user over the prior 30 days to establish whether this AppId has been used before
True Positive Example
{
"log_entry": {
"FirstSeen": "2026-05-01T14:02:00Z",
"LastSeen": "2026-05-01T14:04:37Z",
"SpanMinutes": 2,
"UserPrincipalName": "finance.director@contoso.com",
"AppDisplayName": "Microsoft Azure CLI",
"AppId": "04b07795-8542-4bc9-aaaa-59d79c0a3df9",
"ResourceCount": 6,
"Resources": "[\"Microsoft Graph\",\"Exchange / Outlook\",\"MS Teams\",\"Azure Management\",\"Azure Key Vault\",\"Office Management\"]",
"IPs": "[\"45.142.212.100\"]",
"SignInCount": 6
}
} Six non-interactive token exchanges for a FOCI-eligible client (Azure CLI) against all six AUTHOV-targeted resources within 2 minutes, all from a single datacenter IP not in the user's baseline. A device code sign-in for the same user to Azure CLI was recorded 8 minutes prior. This is the textbook FOCI pivot pattern following a successful device code phish.