T1528 Microsoft Sentinel · KQL

Detect Steal Application Access Token in Microsoft Sentinel

Adversaries may steal application access tokens as a means of acquiring credentials to access remote systems and resources. Application access tokens — including OAuth 2.0 tokens, Kubernetes service account tokens, cloud provider temporary credentials (Azure Managed Identity via IMDS, AWS STS instance role credentials, GCP service account tokens), and CI/CD pipeline secrets — authorize API requests on behalf of users or services. Token theft enables adversaries to impersonate legitimate identities, access cloud resources and SaaS platforms with the victim's permissions, and move laterally without requiring plaintext passwords. Real-world examples include APT29 stealing OAuth tokens via malicious application consent phishing, APT28 creating fraudulent OAuth apps masquerading as Google services, and threat actors exploiting compromised containers to extract Kubernetes service account tokens via the pod filesystem.

MITRE ATT&CK

Tactic
Credential Access
Technique
T1528 Steal Application Access Token
Canonical reference
https://attack.mitre.org/techniques/T1528/

KQL Detection Query

Microsoft Sentinel (KQL)
kusto
// T1528 — Steal Application Access Token
// Multi-vector detection covering IMDS token requests, Kubernetes token access,
// OAuth token cache file access, and high-privilege OAuth consent grants.

// --- Vector 1: Cloud metadata service (IMDS) token requests from unexpected processes ---
let LegitIMDSAgents = dynamic([
  "waagent.exe", "WindowsAzureGuestAgent.exe", "WaAppAgent.exe",
  "MonAgentCore.exe", "HealthService.exe", "MMAExtensionHeartbeatService.exe",
  "AzureAttestService.exe", "azd.exe", "AzureCLI.exe"
]);
let IMDSTokenRequests = DeviceNetworkEvents
| where Timestamp > ago(24h)
| where RemoteIP == "169.254.169.254"
| where RemoteUrl has_any ("metadata/identity", "latest/meta-data/iam/security-credentials", "computeMetadata/v1/instance/service-accounts", "metadata/instance")
| where InitiatingProcessFileName !in~ (LegitIMDSAgents)
| extend Vector = "IMDS Token Request"
| extend TokenPlatform = case(
    RemoteUrl has "metadata/identity", "Azure Managed Identity",
    RemoteUrl has "iam/security-credentials", "AWS Instance Role",
    RemoteUrl has "service-accounts", "GCP Service Account",
    "Cloud IMDS")
| extend RiskDetail = strcat("Unexpected process '", InitiatingProcessFileName, "' queried ", TokenPlatform, " IMDS endpoint")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName,
          InitiatingProcessCommandLine, RemoteUrl, Vector, TokenPlatform, RiskDetail;

// --- Vector 2: Kubernetes service account token file reads ---
let LegitK8sProcesses = dynamic(["kubelet", "pause", "containerd-shim", "containerd-shim-runc-v2", "runc", "cri-o", "dockerd"]);
let K8sTokenAccess = DeviceFileEvents
| where Timestamp > ago(24h)
| where FolderPath has_any (
    "/var/run/secrets/kubernetes.io/serviceaccount",
    "/run/secrets/kubernetes.io",
    "/var/run/secrets/tokens")
| where FileName in~ ("token", "ca.crt")
| where InitiatingProcessFileName !in~ (LegitK8sProcesses)
| extend Vector = "Kubernetes Service Account Token"
| extend TokenPlatform = "Kubernetes"
| extend RiskDetail = strcat("Process '", InitiatingProcessFileName, "' read K8s service account token: ", FolderPath, "/", FileName)
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName,
          InitiatingProcessCommandLine, FileName, FolderPath, Vector, TokenPlatform, RiskDetail;

// --- Vector 3: OAuth / cloud CLI token cache access by unexpected processes ---
let LegitTokenOwners = dynamic([
  "gcloud", "gcloud.exe", "aws", "aws.exe", "az", "az.exe",
  "gh", "gh.exe", "git", "git.exe", "Code.exe", "code",
  "terraform", "terraform.exe", "kubectl", "kubectl.exe"
]);
let TokenCachePatterns = dynamic([
  "application_default_credentials.json",
  "accessTokens.json", "azureProfile.json",
  "msal_token_cache.json", "msal_token_cache.bin",
  "TokenCache.dat", ".git-credentials", "hosts.yml"
]);
let TokenCacheAccess = DeviceFileEvents
| where Timestamp > ago(24h)
| where FileName has_any (TokenCachePatterns)
    or (FolderPath has_any (".config/gcloud", ".azure", "TokenCache") and FileName endswith ".json")
| where ActionType in~ ("FileRead", "FileAccessed", "FileModified")
| where InitiatingProcessFileName !in~ (LegitTokenOwners)
| extend Vector = "OAuth Token Cache Access"
| extend TokenPlatform = case(
    FolderPath has ".config/gcloud" or FileName has "gcloud", "GCP",
    FolderPath has ".aws" or FileName has "aws", "AWS",
    FolderPath has ".azure" or FileName has "azure" or FileName has "msal", "Azure",
    FolderPath has ".gh" or FileName has "hosts.yml", "GitHub",
    "OAuth/Cloud")
| extend RiskDetail = strcat("Process '", InitiatingProcessFileName, "' accessed ", TokenPlatform, " token cache: ", FileName)
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName,
          InitiatingProcessCommandLine, FileName, FolderPath, Vector, TokenPlatform, RiskDetail;

// --- Vector 4: High-privilege OAuth consent grants in Azure AD ---
let HighRiskOAuthScopes = dynamic([
  "Mail.ReadWrite", "Mail.Read", "Files.ReadWrite.All",
  "User.Read.All", "Directory.ReadWrite.All",
  "offline_access", "full_access_as_user",
  "EWS.AccessAsUser.All", "Contacts.ReadWrite"
]);
let OAuthConsentGrants = AuditLogs
| where TimeGenerated > ago(24h)
| where OperationName in ("Consent to application", "Add app role assignment to service principal", "Add delegated permission grant", "Add OAuth2PermissionGrant")
| where Result == "success"
| extend InitiatedByUser = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatedByIP = tostring(InitiatedBy.user.ipAddress)
| extend TargetApp = tostring(TargetResources[0].displayName)
| extend RawProps = tostring(TargetResources[0].modifiedProperties)
| where RawProps has_any (HighRiskOAuthScopes)
| extend Vector = "OAuth High-Privilege Consent Grant"
| extend TokenPlatform = "Azure AD / Microsoft 365"
| extend RiskDetail = strcat("User '", InitiatedByUser, "' consented to high-privilege OAuth app '", TargetApp, "' from IP: ", InitiatedByIP)
| project TimeGenerated, InitiatedByUser, InitiatedByIP, TargetApp, OperationName, RawProps, Vector, TokenPlatform, RiskDetail;

// --- Union all vectors ---
union
  (IMDSTokenRequests  | project Timestamp, Source=DeviceName,  Actor=AccountName,       Process=InitiatingProcessFileName, CommandLine=InitiatingProcessCommandLine, Vector, TokenPlatform, RiskDetail),
  (K8sTokenAccess     | project Timestamp, Source=DeviceName,  Actor=AccountName,       Process=InitiatingProcessFileName, CommandLine=InitiatingProcessCommandLine, Vector, TokenPlatform, RiskDetail),
  (TokenCacheAccess   | project Timestamp, Source=DeviceName,  Actor=AccountName,       Process=InitiatingProcessFileName, CommandLine=InitiatingProcessCommandLine, Vector, TokenPlatform, RiskDetail),
  (OAuthConsentGrants | project Timestamp=TimeGenerated, Source=InitiatedByIP, Actor=InitiatedByUser, Process=TargetApp, CommandLine=RawProps, Vector, TokenPlatform, RiskDetail)
| sort by Timestamp desc
high severity medium confidence

Multi-vector detection for application access token theft using Microsoft Defender for Endpoint and Microsoft Sentinel tables. Covers four attack patterns: (1) unexpected processes querying cloud IMDS endpoints (169.254.169.254) for managed identity tokens across Azure, AWS, and GCP; (2) processes other than legitimate container runtimes reading Kubernetes service account token files from /var/run/secrets; (3) unexpected processes accessing cloud CLI OAuth token cache files (gcloud, az, aws, gh credentials); (4) high-privilege OAuth consent grants in Azure AD audit logs indicating potential OAuth phishing app abuse. Results are unioned into a common schema and sorted by time for triage.

Data Sources

Network: Network Connection CreationFile: File AccessApplication Log: Application Log ContentCloud Service: Cloud Service MetadataAzure Active Directory: Audit Logs

Required Tables

DeviceNetworkEventsDeviceFileEventsAuditLogs

False Positives & Tuning

  • Security scanning tools and vulnerability assessment agents that enumerate IMDS endpoints as part of cloud posture checks (e.g., Prisma Cloud, Wiz, Orca)
  • Developer workstations where developers legitimately use multiple cloud CLIs (gcloud, az, aws) and IDEs that access token caches on behalf of the user
  • Legitimate Kubernetes operators and custom controllers that mount and read service account tokens as part of their normal authentication flow to the Kubernetes API
  • CI/CD pipeline agents (GitHub Actions runner, GitLab Runner, Jenkins agent) that access cloud credentials and token caches as part of authorized deployment workflows
  • IT administration scripts that use OAuth tokens for legitimate bulk operations (e.g., Microsoft Graph scripts for user provisioning, Azure automation runbooks)
Download portable Sigma rule (.yml)

Other platforms for T1528


Testing Methodology

Validate this detection against 4 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1Query Azure IMDS Endpoint for Managed Identity Token

    Expected signal: Sysmon Event ID 3: Network Connection from powershell.exe to 169.254.169.254:80. DeviceNetworkEvents in MDE: RemoteIP=169.254.169.254, InitiatingProcessFileName=powershell.exe. The RemoteUrl field will contain the metadata identity path.

  2. Test 2Read Kubernetes Service Account Token from Pod Filesystem

    Expected signal: Linux auditd syscall audit event for openat/read on /var/run/secrets/kubernetes.io/serviceaccount/token with the process name (cat or the shell). Sysmon for Linux Event ID 11 (if deployed) for file access. The token value (JWT) will be visible in any memory or command output capture.

  3. Test 3Enumerate and Exfiltrate Azure CLI Token Cache

    Expected signal: Sysmon Event ID 11: File access/creation event with TargetFilename matching *msal_token_cache.json and Image=powershell.exe. DeviceFileEvents in MDE: FileName=msal_token_cache.json, ActionType=FileRead or FileAccessed, InitiatingProcessFileName=powershell.exe.

  4. Test 4Register Malicious OAuth App and Simulate Consent Phishing Link

    Expected signal: Azure AD AuditLogs OperationName='Add application' followed by 'Update application' with permissions modification. The registered app will appear in AuditLogs with the requesting user's UPN and source IP. If a test user clicks the generated consent URL, AuditLogs will show OperationName='Consent to application' with the scopes granted.

Unlock Pro Content

Get the full detection package for T1528 including response playbook, investigation guide, and atomic red team tests.

Response PlaybookInvestigation GuideHunting QueriesAtomic Red Team TestsTuning Guidance

Related Detections