# Az - トークンとパブリックアプリケーション

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

## 基本情報

Entra ID は Microsoft のクラウドベースの identity and access management (IAM) プラットフォームで、Microsoft 365 や Azure Resource Manager のようなサービスの認証と認可の基盤として機能します。Azure AD は OAuth 2.0 認可フレームワークと OpenID Connect (OIDC) 認証プロトコルを実装して、リソースへのアクセスを管理します。

### OAuth

OAuth 2.0 の主な参加者:

1. **Resource Server (RS):** リソースオーナーが所有するリソースを保護します。
2. **Resource Owner (RO):** 通常、保護されたリソースを所有するエンドユーザーです。
3. **Client Application (CA):** リソースオーナーに代わってリソースへのアクセスを求めるアプリケーション。
4. **Authorization Server (AS):** クライアントアプリを認証・認可した後、アクセス トークンを発行します。

**スコープと同意:**

- **Scopes:** リソースサーバー上で定義される細かな権限で、アクセスレベルを指定します。
- **Consent:** リソースオーナーが特定のスコープでクライアントアプリにリソースへのアクセス権を付与するプロセス。

**Microsoft 365 の統合:**

- Microsoft 365 は IAM に Azure AD を利用しており、複数の「first-party」OAuth アプリケーションで構成されています。
- これらのアプリケーションは深く統合されており、サービス間で相互依存関係を持つことが多いです。
- ユーザー体験を簡素化し機能性を維持するため、Microsoft はこれらの first-party アプリに対して「implied consent」または「pre-consent」を付与します。
- **Implied Consent:** 特定のアプリケーションは**ユーザーや管理者の明示的な承認なしに特定のスコープへのアクセスが自動的に付与されます**。
- これらの事前承認されたスコープは通常、ユーザーや管理者の双方から隠されており、標準の管理インターフェースでは見えにくくなっています。

**クライアントアプリケーションの種類:**

1. **Confidential Clients:**
- 独自の資格情報（例: パスワードや証明書）を持ちます。
- Authorization Server に対して**安全に自身を認証できる**。
2. **Public Clients:**
- 固有の資格情報を持ちません。
- Authorization Server に対して安全に認証することができません。
- **Security Implication:** Authorization Server がアプリの正当性を検証する仕組みがないため、トークン取得時に攻撃者が public client アプリを偽装できる可能性があります。

## 認証トークン

OIDC で使用される**3種類のトークン**があります:

- [**Access Tokens**](https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens)**:** クライアントはこのトークンをリソースサーバーに提示して**リソースにアクセス**します。これは特定のユーザー、クライアント、リソースの組み合わせでのみ使用可能で、期限切れまで**取り消すことはできません（デフォルトで1時間）**。
- **ID Tokens**: クライアントはこの**トークンを authorization server から受け取ります**。ユーザーに関する基本情報を含みます。特定のユーザーとクライアントの組み合わせに**紐づいています**。
- **Refresh Tokens**: アクセストークンとともにクライアントに提供されます。新しいアクセスおよび ID トークンを取得するために使用されます。特定のユーザーとクライアントの組み合わせに紐づき、取り消すことができます。非アクティブな refresh token のデフォルト有効期限は **90 days** で、アクティブなトークンには**有効期限がありません**（リフレッシュトークンから新しいリフレッシュトークンを取得することが可能なため）。
- リフレッシュトークンは **`aud`**、いくつかの **scopes**、および **tenant** に紐づくべきであり、その aud、scopes（それ以上ではない）およびテナントのためのアクセストークンのみを生成できるべきです。しかし、これは **FOCI applications tokens** では当てはまりません。
- リフレッシュトークンは暗号化されており、復号できるのは Microsoft のみです。
- 新しいリフレッシュトークンを取得しても、以前のリフレッシュトークンは取り消されません。

> [!WARNING]
> **条件付きアクセス** に関する情報は **JWT** の内部に**格納**されます。したがって、**許可された IP アドレスからトークンを要求すると**、その **IP** はトークンに**格納**され、後で**許可されていない IP からそのトークンを使ってリソースにアクセスできる**ようになります。

### Access Tokens "aud"

"aud" フィールドに示される値は、ログインを実行するために使用される**resource server**（アプリケーション）です。

コマンド `az account get-access-token --resource-type [...]` は次のタイプをサポートしており、それぞれが結果のアクセス トークンに特定の "aud" を追加します:

> [!CAUTION]
> 次に示すのは `az account get-access-token` がサポートする API の例に過ぎず、他にもあります。

<details>

<summary>aud examples</summary>

- **aad-graph (Azure Active Directory Graph API)**: レガシーな Azure AD Graph API（非推奨）にアクセスするために使用され、アプリケーションが Azure Active Directory (Azure AD) のディレクトリデータを読み書きできるようにします。
- `https://graph.windows.net/`

* **arm (Azure Resource Manager)**: Azure Resource Manager API を通じて Azure リソースを管理するために使用されます。これには仮想マシン、ストレージアカウントなどのリソースの作成、更新、削除といった操作が含まれます。
- `https://management.core.windows.net/ or https://management.azure.com/`

- **batch (Azure Batch Services)**: 大規模並列処理や高性能コンピューティングアプリケーションをクラウド上で効率的に実行するための Azure Batch にアクセスするために使用されます。
- `https://batch.core.windows.net/`

* **data-lake (Azure Data Lake Storage)**: Azure Data Lake Storage Gen1 と対話するために使用される、スケーラブルなデータストレージおよび分析サービスです。
- `https://datalake.azure.net/`

- **media (Azure Media Services)**: ビデオやオーディオコンテンツのクラウドベースのメディア処理および配信サービスである Azure Media Services にアクセスするために使用されます。
- `https://rest.media.azure.net`

* **ms-graph (Microsoft Graph API)**: Microsoft 365 サービスのデータに対する統一エンドポイントである Microsoft Graph API にアクセスするために使用されます。これにより、Azure AD、Office 365、Enterprise Mobility、Security services などのサービスからデータやインサイトにアクセスできます。
- `https://graph.microsoft.com`

- **oss-rdbms (Azure Open Source Relational Databases)**: MySQL、PostgreSQL、MariaDB のようなオープンソースのリレーショナルデータベースエンジン向けの Azure Database サービスにアクセスするために使用されます。
- `https://ossrdbms-aad.database.windows.net`

</details>

### Access Tokens Scopes "scp"

アクセス トークンのスコープは、アクセス トークンの JWT 内の scp キーに格納されます。これらのスコープが、アクセス トークンが何にアクセスできるかを定義します。

JWT が特定の API にアクセスすることが許可されていても、要求されたアクションを実行するための**スコープを持っていない**場合、その 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(
"00b41c95-dab0-4487-9791-b9d2c32c80f2" # ID for Office 365 Management
)
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)
```
### その他の access token フィールド

- **appid**: トークンを生成するために使用された Application ID
- **appidacr**: The Application Authentication Context Class Reference はクライアントがどのように認証されたかを示す。public client の場合値は 0、client secret が使われた場合は 1
- **acr**: The Authentication Context Class Reference クレームは、エンドユーザーの認証が ISO/IEC 29115 の要件を満たしていない場合 "0" になる。
- **amr**: The Authentication method はトークンがどのように認証されたかを示す。値が “pwd” の場合はパスワードが使用されたことを示す。
- **groups**: プリンシパルがメンバーであるグループを示す。
- **iss**: トークンを生成したセキュリティトークンサービス (STS) を識別する。例: https://sts.windows.net/fdd066e1-ee37-49bc-b08f-d0e152119b04/ （uuid は tenant ID）
- **oid**: プリンシパルの object ID
- **tid**: Tenant ID
- **iat, nbf, exp**: Issued at（発行時刻）、Not before（この時刻以前は使用できない、通常 iat と同じ値）、Expiration time（有効期限）。

## FOCI Tokens Privilege Escalation

以前に述べたように、refresh tokens は生成された **scopes**、生成先の **application**、および生成先の **tenant** に紐付けられるべきである。これらの境界のいずれかが破られると、ユーザーがアクセスできる他のリソースやテナント向けの access tokens を、元々意図されていたより多くの scopes で生成できるようになり、escalate privileges が可能になる。

さらに、[Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/)（Microsoft Entra アカウント、Microsoft personal アカウント、および Facebook や Google のような social accounts を含む）では、[**docs**](https://learn.microsoft.com/en-us/entra/identity-platform/refresh-tokens) に記載されているとおり、**this is possible with all refresh tokens**: "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."

また、FOCI アプリケーションは public applications であるため、サーバーに対して認証するのに **no secret is needed** である点にも注意すること。

既知の FOCI クライアントは [**original research**](https://github.com/secureworks/family-of-client-ids-research/tree/main) に報告されており、[**found here**](https://github.com/secureworks/family-of-client-ids-research/blob/main/known-foci-clients.csv) で確認できる。

### 異なる scope を取得

前のサンプルコードの続きで、このコードでは異なる 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)
```
### 異なるクライアントとスコープを取得する
```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)
```
## tokens を見つける場所

攻撃者の観点では、被害者のPCが侵害された場合にどこで access and refresh tokens を見つけられるかを知ることは非常に重要です:

- **`<HOME>/.Azure`** の中
- **`azureProfile.json`** は過去にログインしたユーザーの情報を含む
- **`clouds.config contains`** はサブスクリプションに関する情報
- **`service_principal_entries.json`** はアプリケーションの認証情報（tenant id、clients、secret）を含む。Linux & macOS のみ
- **`msal_token_cache.json`** は access tokens と refresh tokens を含む。Linux & macOS のみ
- **`service_principal_entries.bin`** と msal_token_cache.bin は Windows で使用され、DPAPI で暗号化されている
- **`msal_http_cache.bin`** は HTTP リクエストのキャッシュ
- 読み込む: `with open("msal_http_cache.bin", 'rb') as f: pickle.load(f)`
- **`AzureRmContext.json`** は Az PowerShell を使った過去のログイン情報を含む（ただし資格情報は含まない）
- **`C:\Users\<username>\AppData\Local\Microsoft\IdentityCache\*`** の中には複数の `.bin` ファイルがあり、access tokens、ID tokens、およびアカウント情報がユーザーの DPAPI で暗号化されている
- さらに **access tokens** は **`C:\Users\<username>\AppData\Local\Microsoft\TokenBroken\Cache\`** 内の `.tbres` ファイルに、DPAPI で暗号化された base64 として含まれていることがある
- Linux と macOS では、Az PowerShell（使用されていれば）で `pwsh -Command "Save-AzContext -Path /tmp/az-context.json"` を実行すると access tokens, refresh tokens and id tokens を取得できる
- Windows ではこれは id tokens のみを生成する
- Linux と macOS で Az PowerShell が使用されたかどうかは `$HOME/.local/share/.IdentityService/` の存在を確認することでわかる（ただし含まれるファイルは空で役に立たない）
- ユーザーがブラウザで Azure にログインしている場合、こちらの [**post**](https://www.infosecnoodle.com/p/obtaining-microsoft-entra-refresh?r=357m16&utm_campaign=post&utm_medium=web) によれば、authentication flow を **redirect to localhost** で開始し、ブラウザに自動でログインを承認させて refresh token を受け取ることが可能である。localhost への redirect を許可する FOCI applications は限られている（例: az cli や powershell module）ため、これらのアプリケーションが許可されている必要がある
- ブログで説明されている別の方法として、任意のアプリケーションを使えるツール [**BOF-entra-authcode-flow**](https://github.com/sudonoodle/BOF-entra-authcode-flow) を使用する手法がある。これはリダイレクト URI `https://login.microsoftonline.com/common/oauth2/nativeclient` を用い、最終認証ページのタイトルから OAuth code を取得してから refresh token を取得する

## 参考資料

- [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}}
