{
  "technique_id": "T1528",
  "name": "Steal Application Access Token",
  "priority": "high",
  "status": "draft",
  "version": "0.1.0",
  "last_reviewed": "2026-07-23",
  "generated_by": "SOC Response Atlas by Basyrix",
  "tactics": [
    "Credential Access"
  ],
  "platforms": [
    "Containers",
    "IaaS",
    "Identity Provider",
    "Office Suite",
    "SaaS"
  ],
  "summary": "Adversaries can steal application access tokens as a means of acquiring credentials to access remote systems and resources. Application access tokens are used to make authorized API requests on behalf of a user or service and are commonly used as a way to access resources in cloud and container-based applications and software-as-a-service (SaaS)...",
  "soc_recommendation": "Investigate Steal Application Access Token activity in the context of Credential Access: confirm scope, affected host/identity, and whether it matches expected administrative behaviour before deciding this is benign.",
  "d3fend_mappings": [
    {
      "id": "D3-CCSA",
      "name": "Credential Compromise Scope Analysis",
      "relationship": "detect",
      "practical_action": "Monitor for Credential Compromise Scope Analysis indicators relevant to this technique.",
      "tooling": [
        "Entra ID"
      ]
    },
    {
      "id": "D3-CH",
      "name": "Credential Hardening",
      "relationship": "harden",
      "practical_action": "Apply Credential Hardening to reduce this technique's viability before an incident occurs.",
      "tooling": [
        "Entra ID"
      ]
    },
    {
      "id": "D3-CR",
      "name": "Credential Revocation",
      "relationship": "evict",
      "practical_action": "Use Credential Revocation to remove the adversary's foothold once this technique is confirmed.",
      "tooling": [
        "Entra ID"
      ]
    },
    {
      "id": "D3-CTS",
      "name": "Credential Transmission Scoping",
      "relationship": "isolate",
      "practical_action": "Apply Credential Transmission Scoping to contain the blast radius once this technique is observed.",
      "tooling": [
        "Entra ID"
      ]
    }
  ],
  "investigation_steps": {
    "microsoft": [
      "Review Defender for Endpoint / Defender XDR alerts and timeline for the affected host or identity.",
      "Check Sentinel analytics rules and incidents correlated with this technique.",
      "Review Entra ID sign-in and audit logs if the technique involves an identity or cloud resource."
    ],
    "generic": [
      "Confirm whether the observed steal application access token activity matches expected administrative or application behaviour.",
      "Identify the host, account, or resource where the activity occurred and its business criticality.",
      "Check for related alerts before and after this activity to reconstruct the broader intrusion timeline.",
      "Real detection reference: \"Contrast ADR - DLP SQL Injection Correlation\" -- Detects successful SQL injection attacks identified by Contrast ADR and correlates them with WAF/DLP logs. This rule identifies critical database security breaches that have bypassed initial defenses and may result in data exfiltration or unauthorized database access.'"
    ]
  },
  "evidence_to_collect": [
    "Host or resource affected",
    "Account or identity involved",
    "Timestamp of the activity",
    "Related process, file, or network artifact",
    "Any preceding or follow-on alerts"
  ],
  "response_actions": [
    {
      "name": "Contain the affected host or account",
      "category": "Containment",
      "risk": "Medium",
      "automation_safe": false,
      "approval_required": true,
      "tool": "Defender for Endpoint / Entra ID",
      "notes": "Requires approval -- confirm malicious intent before isolating a host or disabling an account."
    },
    {
      "name": "Collect and preserve evidence",
      "category": "Investigation",
      "risk": "Low",
      "automation_safe": true,
      "approval_required": false,
      "tool": "Defender for Endpoint",
      "notes": "Safe -- read-only evidence collection."
    }
  ],
  "queries": {
    "kql": [
      {
        "name": "1aac7737-d52f-483d-b225-6a27c1b29a9e — Contrast ADR - DLP SQL Injection Correlation",
        "description": "Detects successful SQL injection attacks identified by Contrast ADR and correlates them with WAF/DLP logs. This rule identifies critical database security breaches that have bypassed initial defenses and may result in data exfiltration or unauthorized database access.' (Source: Microsoft's official Azure-Sentinel Detections (MIT licensed).)",
        "query": "ContrastADRAttackEvents_CL\n| where result =~ \"EXPLOITED\" and rule =~ \"SQL-INJECTION\"\n| project-rename hostname = host_hostname\n//please add your DLP logs table in place of ContrastWAFLogs_CL and hostname colomun in place of hostname below and uncomment the queries below\n//| join kind= inner (ContrastWAFLogs_CL | where TimeGenerated >= ago(5m)) on hostname"
      },
      {
        "name": "f948a32f-226c-4116-bddd-d95e91d97eb9 — Suspicious application consent similar to O365 Attack Toolkit",
        "description": "This will alert when a user consents to provide a previously-unknown Azure application with the same OAuth permissions used by the MDSec O365 Attack Toolkit (https://github.com/mdsecactivebreach/o365-attack-toolkit). The default permissions/scope for the MDSec O365 Attack toolkit change sometimes but often include contacts.read, user.read, mail.read, notes.read.all, mailboxsettings.readwrite, files.readwrite.all, mail.send, files.read, and files.read.all. Consent to applications with these permissions should be rare, especially as the knownApplications list is expanded, especially as the knownApplications list is expanded. Public contributions to expand this filter are welcome! For further information on AuditLogs please see https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities.' (Source: Microsoft's official Azure-Sentinel Detections (MIT licensed).)",
        "query": "let detectionTime = 1d;\nlet joinLookback = 14d;\nlet threshold = 5;\nlet o365_attack_regex = \"contacts.read|user.read|mail.read|notes.read.all|mailboxsettings.readwrite|Files.ReadWrite.All|mail.send|files.read|files.read.all\";\nlet o365_attack = dynamic([\"contacts.read\", \"user.read\", \"mail.read\", \"notes.read.all\", \"mailboxsettings.readwrite\", \"Files.ReadWrite.All\", \"mail.send\", \"files.read\", \"files.read.all\"]);\nAuditLogs\n| where TimeGenerated > ago(detectionTime)\n| where LoggedByService =~ \"Core Directory\"\n| where Category =~ \"ApplicationManagement\"\n| where OperationName =~ \"Consent to application\"\n| mv-apply TargetResource = TargetResources on \n  (\n      where TargetResource.type =~ \"ServicePrincipal\"\n      | extend AppDisplayName = tostring(TargetResource.displayName),\n               AppClientId = tostring(TargetResource.id),\n               props = TargetResource.modifiedProperties\n  )\n| where AppClientId !in ((externaldata(knownAppClientId:string, knownAppDisplayName:string)[@\"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/Microsoft.OAuth.KnownApplications.csv\"] with (format=\"csv\"))) // NOTE: a MATCH from this list will cause the alert to NOT fire - please modify for your environment!\n| mv-apply ConsentFull = props on \n  (\n      where ConsentFull.displayName =~ \"ConsentAction.Permissions\"\n  )\n| parse ConsentFull with * \"ConsentType: \" GrantConsentType \", Scope: \" GrantScope1 \", CreatedDateTime\" * \"]\" *\n| where GrantConsentType != \"AllPrincipals\" // NOTE: we are ignoring if OAuth application was granted to all users via an admin - but admin due diligence should be audited occasionally\n| where ConsentFull has_any (o365_attack)  \n| extend GrantScopeCount = countof(tolower(GrantScope1), o365_attack_regex, 'regex')\n| where GrantScopeCount > threshold\n| extend GrantInitiatedByAppName = tostring(InitiatedBy.app.displayName)\n| extend GrantInitiatedByAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)\n| extend GrantInitiatedByUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)\n| extend GrantInitiatedByAadUserId = tostring(InitiatedBy.user.id)\n| extend GrantIpAddress = iff(isnotempty(InitiatedBy.user.ipAddress), tostring(InitiatedBy.user.ipAddress), tostring(InitiatedBy.app.ipAddress))\n| extend GrantInitiatedBy = iff(isnotempty(GrantInitiatedByUserPrincipalName), GrantInitiatedByUserPrincipalName, GrantInitiatedByAppName)\n| mv-apply AdditionalDetail = AdditionalDetails on \n  (\n      where AdditionalDetail.key =~ \"User-Agent\"\n      | extend GrantUserAgent = AdditionalDetail.value\n  )\n| project TimeGenerated, GrantConsentType, GrantScope1, GrantInitiatedBy, AppDisplayName, GrantInitiatedByUserPrincipalName, GrantInitiatedByAadUserId, GrantInitiatedByAppName, GrantInitiatedByAppServicePrincipalId, GrantIpAddress, GrantUserAgent, AppClientId, OperationName, ConsentFull, CorrelationId\n| join kind = leftouter (AuditLogs\n  | where TimeGenerated > ago(joinLookback)\n  | where LoggedByService =~ \"Core Directory\"\n  | where Category =~ \"ApplicationManagement\"\n  | where OperationName =~ \"Add service principal\"\n  | mv-apply TargetResource = TargetResources on \n      (\n          where TargetResource.type =~ \"ServicePrincipal\"\n          | extend props = TargetResource.modifiedProperties,\n                  AppClientId = tostring(TargetResource.id)\n      )\n  | mv-apply Property = props on \n      (\n          where Property.displayName =~ \"AppAddress\" and Property.newValue has \"AddressType\"\n          | extend AppReplyURLs = trim('\"',tostring(Property.newValue))\n      )\n  | distinct AppClientId, tostring(AppReplyURLs)\n) on AppClientId\n| join kind = innerunique (AuditLogs\n      | where TimeGenerated > ago(joinLookback)\n      | where LoggedByService =~ \"Core Directory\"\n      | where Category =~ \"ApplicationManagement\"\n      | where OperationName =~ \"Add OAuth2PermissionGrant\" or OperationName =~ \"Add delegated permission grant\"\n          | mv-apply TargetResource = TargetResources on \n          (\n              where TargetResource.type =~ \"ServicePrincipal\" and array_length(TargetResource.modifiedProperties) > 0 and isnotnull(TargetResource.displayName)\n              | extend GrantAuthentication = tostring(TargetResource.displayName)\n          )\n      | extend GrantOperation = OperationName\n      | project GrantAuthentication, GrantOperation, CorrelationId\n  ) on CorrelationId\n| project TimeGenerated, GrantConsentType, GrantScope1, GrantInitiatedBy, AppDisplayName, AppReplyURLs, GrantInitiatedByUserPrincipalName, GrantInitiatedByAadUserId, GrantInitiatedByAppName, GrantInitiatedByAppServicePrincipalId, GrantIpAddress, GrantUserAgent, AppClientId, GrantAuthentication, OperationName, GrantOperation, CorrelationId, ConsentFull\n| extend Name = tostring(split(GrantInitiatedByUserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(GrantInitiatedByUserPrincipalName,'@',1)[0])"
      },
      {
        "name": "8dcf7238-a7d0-4cfd-8d0c-b230e3cd9182 — Trust Monitor Event",
        "description": "This query identifies when a new trust monitor event is detected.' (Source: Microsoft's official Azure-Sentinel Detections (MIT licensed).)",
        "query": "let timeframe = ago(5m);\nDuoSecurityTrustMonitor_CL\n| where TimeGenerated >= timeframe\n| extend AccountName = tostring(split(surfaced_auth_user_name_s, \"@\")[0]), AccountUPNSuffix = tostring(split(surfaced_auth_user_name_s, \"@\")[1])"
      }
    ],
    "spl": [],
    "esql": [
      {
        "name": "2b9a3b7a-0891-4a89-abbe-dca753c403cd — Multi-Cloud CLI Token and Credential Access Commands",
        "description": "(ESQL) Correlates process telemetry for shells and major cloud/Kubernetes CLIs when command lines match token or credential material access patterns (GCP, Azure, AWS, GitHub, kubectl, DigitalOcean, OCI). Flags hosts where multiple cloud targets appear within a five-minute window. (Source: Elastic's official detection-rules repository (Elastic License v2).)",
        "query": "FROM logs-endpoint.events.process-*, logs-system.security-*, logs-windows.sysmon_operational-* METADATA _id, _index, _version\n| WHERE event.category == \"process\" AND KQL(\"\"\" event.type : \"start\" and not event.action : \"fork\" \"\"\")\n  AND process.command_line IS NOT NULL \n  AND (\n    TO_LOWER(process.name) IN (\n      \"cmd.exe\", \"powershell.exe\", \"pwsh.exe\", \n      \"sh\", \"bash\", \"zsh\", \"dash\", \"fish\", \"ksh\",\n      \"gcloud\", \"gcloud.cmd\", \"az\", \"az.cmd\", \"azd\", \"azd.exe\",\n      \"gh\", \"gh.exe\", \"aws\", \"aws.exe\",\n      \"kubectl\", \"kubectl.exe\",\n      \"doctl\", \"doctl.exe\",\n      \"oci\", \"oci.exe\"\n    ) OR\n    TO_LOWER(process.parent.name) IN (\n      \"cmd.exe\", \"powershell.exe\", \"pwsh.exe\",\n      \"sh\", \"bash\", \"zsh\", \"dash\", \"fish\", \"ksh\", \"bun\", \"bun.exe\", \n      \"node\", \"node.exe\", \"java\", \"java.exe\"\n    )\n  )\n  AND process.command_line RLIKE \"\"\".*(config-helper\\s.*--format|auth\\s+print-access-token|auth\\s+print-identity-token|auth\\s+application-default\\s+print|get-access-token\\s.*--output|Get-AzAccessToken|azd\\s+auth\\s+token|az\\s+account\\s+get-access-token|gh\\s+auth\\s+(token|status)|aws\\s+sts\\s+(get-session-token|get-caller-identity|assume-role)|aws\\s+configure\\s+(export-credentials|list)|kubectl\\s+config\\s+view\\s.*--raw|kubectl\\s+get\\s+secret|doctl\\s+auth\\s+(list|init)|oci\\s+session\\s+authenticate|oci\\s+iam\\s.*token).*\"\"\"\n| EVAL cloud_target = CASE(\n    process.command_line RLIKE \".*(gcloud|config-helper|print-access-token|print-identity-token).*\", \"GCP\",\n    process.command_line RLIKE \".*(azd auth|az account|Get-AzAccessToken).*\", \"AZURE\",\n    process.command_line RLIKE \".*(aws sts|aws configure).*\", \"AWS\",\n    process.command_line RLIKE \".*(gh auth).*\", \"GITHUB\",\n    process.command_line RLIKE \".*(kubectl config|kubectl get secret).*\", \"KUBERNETES\",\n    process.command_line RLIKE \".*(doctl).*\", \"DIGITALOCEAN\",\n    process.command_line RLIKE \".*(oci session|oci iam).*\", \"ORACLE\"\n  )\n| WHERE cloud_target IS NOT NULL // drop unclassified events before aggregation\n| STATS\n    Esql.cloud_targets = VALUES(cloud_target),\n    Esql.unique_clouds = COUNT_DISTINCT(cloud_target),\n    Esql.process_command_line_values = VALUES(process.command_line),\n    Esql.process_parent_executable_values = VALUES(process.parent.executable),\n    Esql.first_seen = MIN(@timestamp),\n    Esql.last_seen = MAX(@timestamp),\n    Esql.event_count = COUNT(*)\n  BY host.name, host.id, user.name\n| WHERE Esql.unique_clouds >= 2\n| KEEP Esql.*, user.name, host.name, host.id"
      },
      {
        "name": "3db029b3-fbb7-4697-ad07-33cbfd5bd080 — Entra ID OAuth Device Code Flow with Concurrent Sign-ins",
        "description": "(ESQL) Identifies Entra ID device code authentication flows where multiple user agents are observed within the same session. This pattern is indicative of device code phishing, where an attacker's polling client (e.g., Python script) and the victim's browser both appear in the same authentication session... (Source: Elastic's official detection-rules repository (Elastic License v2).)",
        "query": "from logs-azure.signinlogs-* metadata _id, _version, _index\n\n| where event.category == \"authentication\" and data_stream.dataset == \"azure.signinlogs\" and\n        azure.signinlogs.properties.original_transfer_method == \"deviceCodeFlow\"\n\n// Track events with deviceCode authentication protocol (browser auth) vs polling client\n| eval is_device_code_auth = case(azure.signinlogs.properties.authentication_protocol == \"deviceCode\", 1, 0)\n\n| stats Esql.count_logon = count(*),\n        Esql.device_code_auth_count = sum(is_device_code_auth),\n        Esql.timestamp_values = values(@timestamp),\n        Esql.source_ip_count_distinct = count_distinct(source.ip),\n        Esql.user_agent_count_distinct = count_distinct(user_agent.original),\n        Esql.user_agent_values = values(user_agent.original),\n        Esql.authentication_protocol_values = values(azure.signinlogs.properties.authentication_protocol),\n        Esql.azure_signinlogs_properties_client_app_values = values(azure.signinlogs.properties.app_display_name),\n        Esql.azure_signinlogs_properties_app_id_values = values(azure.signinlogs.properties.app_id),\n        Esql.azure_signinlogs_properties_resource_display_name_values = values(azure.signinlogs.properties.resource_display_name),\n        Esql.azure_signinlogs_properties_auth_requirement_values = values(azure.signinlogs.properties.authentication_requirement),\n        Esql.azure_signinlogs_properties_tenant_id = values(azure.tenant_id),\n        Esql.azure_signinlogs_properties_status_error_code_values = values(azure.signinlogs.properties.status.error_code),\n        Esql.message_values = values(message),\n        Esql.azure_signinlogs_properties_resource_id_values = values(azure.signinlogs.properties.resource_id),\n        Esql.source_ip_values = values(source.ip)\n        by azure.signinlogs.properties.session_id, azure.signinlogs.identity\n\n// Require: 2+ events, at least one deviceCode auth protocol event, and either 2+ IPs or 2+ user agents\n| where Esql.count_logon >= 2 and Esql.device_code_auth_count >= 1 and (Esql.source_ip_count_distinct >= 2 or Esql.user_agent_count_distinct >= 2)\n| keep\n       Esql.*,\n       azure.signinlogs.properties.session_id,\n       azure.signinlogs.identity"
      }
    ]
  },
  "automation": {
    "safe": [
      "Add recommendation as Sentinel incident comment.",
      "Run enrichment queries.",
      "Create ServiceNow SecOps task."
    ],
    "approval_required": [
      "Contain or disable the affected host/account.",
      "Any change to production configuration."
    ]
  },
  "escalation_criteria": [
    "Steal Application Access Token activity observed on a privileged account or critical system.",
    "Activity follows or precedes other suspicious behaviour in the same investigation.",
    "Automated triage cannot confidently rule out malicious intent."
  ],
  "false_positive_considerations": [
    "Legitimate administrative or maintenance activity matching this pattern.",
    "Approved security testing or red team exercise.",
    "Known benign software producing similar telemetry."
  ],
  "confluence": {
    "title": "T1528 - Steal Application Access Token Response Guidance",
    "labels": [
      "mitre",
      "attack",
      "d3fend",
      "secops",
      "basyrix"
    ],
    "sections": [
      "summary",
      "d3fend_mappings",
      "investigation_steps",
      "response_actions",
      "queries",
      "automation",
      "escalation_criteria",
      "false_positive_considerations"
    ]
  }
}