mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-07-28 22:51:09 -07:00
Translated ['', 'src/pentesting-cloud/azure-security/az-basic-informatio
This commit is contained in:
+201
-82
@@ -1,98 +1,171 @@
|
||||
# Az - Tokens & Public Applications
|
||||
# Az - 令牌与公开应用
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Basic Information
|
||||
## 基本信息
|
||||
|
||||
Entra ID 是微软的基于云的身份和访问管理 (IAM) 平台,作为 Microsoft 365 和 Azure Resource Manager 等服务的基础身份验证与授权系统。Azure AD 实现了 OAuth 2.0 授权框架和 OpenID Connect (OIDC) 身份验证协议来管理对资源的访问。
|
||||
Entra ID 是 Microsoft 的基于云的身份和访问管理 (IAM) 平台,作为 Microsoft 365 和 Azure Resource Manager 等服务的基础认证和授权系统。Azure AD 实现了 OAuth 2.0 授权框架和 OpenID Connect (OIDC) 认证协议以管理对资源的访问。
|
||||
|
||||
### OAuth
|
||||
|
||||
**OAuth 2.0 的关键参与者:**
|
||||
|
||||
1. **Resource Server (RS):** 保护资源所有者拥有的资源。
|
||||
2. **Resource Owner (RO):** 通常是拥有受保护资源的终端用户。
|
||||
3. **Client Application (CA):** 代表资源所有者请求访问资源的应用程序。
|
||||
4. **Authorization Server (AS):** 在对 client application 进行身份验证和授权后向其颁发 access tokens。
|
||||
1. **Resource Server (RS):** 保护资源拥有者所拥有的资源。
|
||||
2. **Resource Owner (RO):** 通常是拥有受保护资源的终端用户。
|
||||
3. **Client Application (CA):** 代表资源拥有者寻求访问资源的应用程序。
|
||||
4. **Authorization Server (AS):** 在对客户端应用进行身份验证和授权后向其颁发访问令牌。
|
||||
|
||||
**Scopes 与 Consent:**
|
||||
**作用域与同意:**
|
||||
|
||||
- **Scopes:** 在 resource server 上定义的细粒度权限,指定访问级别。
|
||||
- **Consent:** 资源所有者授予 client application 访问具有特定 scopes 的资源的过程。
|
||||
- **Scopes:** 在资源服务器上定义的细粒度权限,用于指定访问级别。
|
||||
- **Consent:** 资源拥有者授予客户端应用以特定作用域访问资源权限的过程。
|
||||
|
||||
**Microsoft 365 集成:**
|
||||
|
||||
- Microsoft 365 使用 Azure AD 进行 IAM,并由多个 “first-party” OAuth 应用组成。
|
||||
- Microsoft 365 利用 Azure AD 作为 IAM,并由多个“第一方” OAuth 应用组成。
|
||||
- 这些应用深度集成,通常具有相互依赖的服务关系。
|
||||
- 为简化用户体验并保持功能性,Microsoft 会对这些 first-party 应用授予 “implied consent” 或 “pre-consent”。
|
||||
- **Implied Consent:** 某些应用会被自动 **granted access to specific scopes without explicit user or administrator approva**l。
|
||||
- 这些预先同意的 scopes 通常对用户和管理员可见性较低,在标准管理界面中不易察觉。
|
||||
- 为了简化用户体验并保持功能性,Microsoft 向这些第一方应用授予“implied consent”或“pre-consent”。
|
||||
- **Implied Consent:** Certain applications are automatically **granted access to specific scopes without explicit user or administrator approva**l.
|
||||
- 这些已预同意的作用域通常对用户和管理员都隐藏,使它们在标准管理界面中较不明显。
|
||||
|
||||
**Client Application 类型:**
|
||||
**客户端应用类型:**
|
||||
|
||||
1. **Confidential Clients:**
|
||||
- 拥有自己的凭据(例如密码或证书)。
|
||||
- 能够**安全地向 authorization server 进行自我身份验证**。
|
||||
- 拥有自己的凭证(例如密码或证书)。
|
||||
- 可以 **安全地进行自我认证** 到授权服务器。
|
||||
2. **Public Clients:**
|
||||
- 没有唯一凭据。
|
||||
- 无法安全地向 authorization server 进行身份验证。
|
||||
- **安全含义:** 攻击者在请求 tokens 时可以冒充 public client application,因为 authorization server 无法验证该应用的合法性。
|
||||
- 没有唯一凭证。
|
||||
- 无法安全地向授权服务器进行身份验证。
|
||||
- **Security Implication:** 攻击者在请求令牌时可以冒充 public client application,因为授权服务器无法验证应用程序的合法性。
|
||||
|
||||
### ROPC / Password Grant
|
||||
|
||||
The OAuth2 **Resource Owner Password Credentials** (**ROPC**) 流程使用直接的 `POST` 到 `https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token`,包含 `grant_type=password`、一个 **username**、**password**、一个 `client_id`,以及所请求的 **scope**。在 Entra ID 中,这主要对 **public clients** 有趣,因为攻击者可以重用 Microsoft 第一方的 client IDs 或任何其他被允许的 public client,而无需 secret。
|
||||
```bash
|
||||
curl -X POST "https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
--data-urlencode "client_id=f05ff7c9-f75a-4acd-a3b5-f4b6a870245d" \
|
||||
--data-urlencode "client_info=1" \
|
||||
--data-urlencode "grant_type=password" \
|
||||
--data-urlencode "username=user@corp.com" \
|
||||
--data-urlencode "password=Password123!" \
|
||||
--data-urlencode "scope=https://graph.microsoft.com/.default"
|
||||
```
|
||||
If the credentials are valid and the flow is allowed, Entra can return **access tokens** and sometimes **refresh tokens** that are immediately usable against Microsoft Graph or the target resource.
|
||||
|
||||
### Entra ID 登录日志绕过类别
|
||||
|
||||
一些历史上的 Entra ID 漏洞允许 **password validation** 或甚至 **full token issuance**,但不会生成预期的 **Entra ID sign-in log** 条目。这些问题已被修复,但这些技术仍然有助于理解 auth 管道如何以某些方式失败,导致 **downstream token use visible** 而 **upstream sign-in telemetry is absent**。
|
||||
|
||||
#### 1. 使用 foreign-tenant endpoint 进行 stealth password validation
|
||||
|
||||
如果请求被发送到另一个 **different tenant GUID** 的 token endpoint,Entra 仍可能在流程因该用户在该外部 tenant 中不存在而失败之前,验证提交的密码是否与所提供的 username 匹配。历史上这允许:
|
||||
|
||||
- **Password spraying / credential validation**,但在受害 tenant 中没有相应的登录日志
|
||||
- 响应差异可以揭示密码步骤是否成功
|
||||
- 不会签发 token,但生成的遥测(telemetry)比正常失败登录要少
|
||||
|
||||
#### 2. 强制发生 password 验证之后的失败
|
||||
|
||||
如果在 credential validation 之后使用的某个参数无效,比如无效的 `client_id`,则整个事务可能会失败,即使密码已经正确。历史上这会产生一个 **失败的** 登录视图,同时隐藏密码猜测实际成功的事实。
|
||||
|
||||
需要记住的模式是:
|
||||
|
||||
- **密码检查成功**
|
||||
- 之后的某个验证步骤失败
|
||||
- 日志代表最终事务状态,但不反映成功的 password-validation 步骤
|
||||
|
||||
#### 3. 使用过大但合法的值触发日志写入失败
|
||||
|
||||
最危险的一类是当请求在语法上仍然有效、authentication 成功、**tokens are returned**,但某些 **logged field** 足够大以破坏日志管道。已报告的示例包括:
|
||||
|
||||
- 重复有效 scopes 数千次,例如 `openid openid openid ...`
|
||||
- 提供过长但仍被接受的 **User-Agent** header
|
||||
|
||||
这表明一类通用问题,其中:
|
||||
|
||||
1. Entra 验证凭证和请求语法
|
||||
2. token 成功签发
|
||||
3. 日志尝试持久化一个原始的、由用户控制的字段
|
||||
4. 由于长度或架构假设,日志写入失败
|
||||
5. 用户获得了有效的 token,但没有相应的 sign-in 记录
|
||||
|
||||
Example of the repeated-scope pattern:
|
||||
```bash
|
||||
curl -X POST "https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
--data-urlencode "client_id=f05ff7c9-f75a-4acd-a3b5-f4b6a870245d" \
|
||||
--data-urlencode "client_info=1" \
|
||||
--data-urlencode "grant_type=password" \
|
||||
--data-urlencode "username=user@corp.com" \
|
||||
--data-urlencode "password=Password123!" \
|
||||
--data-urlencode "scope=$(for num in {1..10000}; do echo -n 'openid '; done)"
|
||||
```
|
||||
#### Hunting / 防御 说明
|
||||
|
||||
不要假设每次有效的 token 使用都会有对应的 Entra sign-in 事件。调查可疑的 Graph 活动时,将以下信息关联起来:
|
||||
|
||||
- **Non-interactive sign-in logs**
|
||||
- **Graph Activity Logs**
|
||||
- **IP 地址**、**user/object ID**、**session/correlation identifiers** 和 **时间窗口**
|
||||
|
||||
一种实用的验证方法是将怀疑的“不可见成功”夹在两次正常的失败登录之间,然后验证在摄取延迟后是否缺失了预期的序列 `Failed -> Successful -> Failed` 中的中间事件。如果存在下游的 Graph 活动但没有对应的 sign-in 日志,则将其视为潜在的 **sign-in logging gap** 或 **token replay** 情况。
|
||||
|
||||
## Authentication Tokens
|
||||
|
||||
在 OIDC 中使用三种类型的 tokens:
|
||||
在 OIDC 中使用的有 **三种类型的 tokens**:
|
||||
|
||||
- [**Access Tokens**](https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens)**:** client 向 resource server 提交此 token 以**访问资源**。它只能用于特定的 user、client 和 resource 组合,并且在到期前**无法被撤销**——默认为 1 小时。
|
||||
- **ID Tokens:** client 从 authorization server 收到的 **token**。它包含有关用户的基本信息。它**绑定到特定的 user 和 client 组合**。
|
||||
- **Refresh Tokens:** 与 access token 一起提供给 client。用于**获取新的 access 和 ID tokens**。它绑定到特定的 user 和 client 组合并且可以被撤销。默认过期为**90 天**(对非活动的 refresh tokens),而**活动 token 没有过期**(可以通过 refresh token 获取新的 refresh tokens)。
|
||||
- refresh token 应该绑定到一个 **`aud`**、一些 **scopes**,以及一个 **tenant**,并且它应该只能为该 aud、这些 scopes(且不能更多)和 tenant 生成 access tokens。然而,对于 **FOCI applications tokens** 情况并非如此。
|
||||
- refresh token 是加密的,只有 Microsoft 能够解密它。
|
||||
- 获取新的 refresh token 不会撤销先前的 refresh token。
|
||||
- [**Access Tokens**](https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens)**:** 客户端向资源服务器出示该 token 以 **访问资源**。它只能用于特定的 user、client 和 resource 组合,并且在到期前 **不能被撤销** —— 默认过期时间为 1 小时。
|
||||
- **ID Tokens**:客户端从 authorization server 接收此 token。它包含有关用户的基本信息。它 **绑定到特定的 user 与 client 组合**。
|
||||
- **Refresh Tokens**:与 access token 一起提供给客户端。用于 **获取新的 access 和 ID tokens**。它绑定到特定的 user 与 client 组合,并且可以被撤销。默认过期为对非活动 refresh tokens 的 **90 天**,对活动 tokens 则 **无过期**(通过 refresh token 可以获取新的 refresh tokens)。
|
||||
- refresh token 应该绑定到一个 **`aud`**、若干 **scopes** 和一个 **tenant**,并且应只能够为该 aud、这些 scopes(且不超出)和该 tenant 生成 access tokens。然而,对于 **FOCI applications tokens** 并非如此。
|
||||
- refresh token 是加密的,只有 Microsoft 能解密它。
|
||||
- 获取新的 refresh token 不会撤销之前的 refresh token。
|
||||
|
||||
> [!WARNING]
|
||||
> 关于 **conditional access** 的信息是**存储**在 **JWT** 内的。因此,如果你从一个 **allowed IP address** 请求 **token**,该 **IP** 会被**存储**在 token 中,然后你可以从 **non-allowed IP** 使用该 token 来访问资源。
|
||||
> **conditional access** 的信息是 **存储** 在 **JWT** 内。因此,如果你从一个被允许的 IP 地址 请求该 token,该 **IP** 将被 **存储** 在 token 中,然后你可以从一个 **非被允许 IP** 使用该 token 访问资源。
|
||||
|
||||
### Access Tokens "aud"
|
||||
|
||||
"aud" 字段指示用于执行登录的 **resource server**(即应用)。
|
||||
"aud" 字段所指示的是用于执行登录的 **resource server**(即应用)。
|
||||
|
||||
命令 `az account get-access-token --resource-type [...]` 支持以下类型,每种类型会在生成的 access token 中添加特定的 "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>
|
||||
<summary>aud 示例</summary>
|
||||
|
||||
- **aad-graph (Azure Active Directory Graph API)**: 用于访问已弃用的 legacy 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 资源。包括创建、更新和删除诸如虚拟机、存储帐户等资源的操作。
|
||||
- **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 Graph API —— Microsoft 365 服务数据的统一端点。它允许访问来自 Azure AD、Office 365、Enterprise Mobility 和 Security services 的数据和见解。
|
||||
- **ms-graph (Microsoft Graph API)**:用于访问 Microsoft Graph API,这是 Microsoft 365 服务数据的统一端点。它允许你访问来自 Azure AD、Office 365、Enterprise Mobility 及安全服务等的 数据与洞见。
|
||||
- `https://graph.microsoft.com`
|
||||
|
||||
- **oss-rdbms (Azure Open Source Relational Databases)**: 用于访问 Azure 针对开源关系数据库引擎(如 MySQL、PostgreSQL 和 MariaDB)的数据库服务。
|
||||
- **oss-rdbms (Azure Open Source Relational Databases)**:用于访问面向开源关系数据库引擎(如 MySQL、PostgreSQL 和 MariaDB)的 Azure 数据库服务。
|
||||
- `https://ossrdbms-aad.database.windows.net`
|
||||
|
||||
</details>
|
||||
|
||||
### Access Tokens Scopes "scp"
|
||||
|
||||
access token 的 scope 存储在 access token JWT 内的 scp 键中。这些 scopes 定义了 access token 可以访问的内容。
|
||||
access token 的 scope 存储在 access token JWT 的 scp 键中。这些 scopes 定义了 access token 的权限范围。
|
||||
|
||||
如果某个 JWT 被允许访问特定 API,但**没有执行所请求操作的 scope**,那么它**无法使用该 JWT 执行该操作**。
|
||||
如果某个 JWT 被允许访问特定 API,但**没有执行所请求操作所需的 scope**,则该 JWT **无法执行该操作**。
|
||||
|
||||
### Get refresh & access token example
|
||||
```python
|
||||
@@ -144,33 +217,31 @@ scopes=["https://graph.microsoft.com/.default"],
|
||||
)
|
||||
pprint(new_azure_cli_bearer_tokens_for_graph_api)
|
||||
```
|
||||
### 其他访问令牌字段
|
||||
### 其他 access token 字段
|
||||
|
||||
- **appid**: 用于生成令牌的应用程序 ID
|
||||
- **appidacr**: Application Authentication Context Class Reference,指示客户端如何被认证;对于 public client 值为 0,若使用 client secret 则值为 1
|
||||
- **acr**: Authentication Context Class Reference 声明为 "0" 表示终端用户认证未满足 ISO/IEC 29115 的要求。
|
||||
- **amr**: Authentication method 指示令牌的认证方式。值为 “pwd” 表示使用了密码。
|
||||
- **groups**: 指示主体所属的组。
|
||||
- **iss**: 发行者,标识生成令牌的安全令牌服务 (STS)。例如 https://sts.windows.net/fdd066e1-ee37-49bc-b08f-d0e152119b04/(uuid 为租户 ID)
|
||||
- **oid**: 主体的对象 ID
|
||||
- **tid**: 租户 ID
|
||||
- **iat, nbf, exp**: iat 为签发时间(何时签发),nbf 为生效时间(在此时间之前不可使用,通常与 iat 相同),exp 为过期时间。
|
||||
- **appid**:用于生成 token 的 Application ID
|
||||
- **appidacr**:Application Authentication Context Class Reference,表示客户端如何被认证;对于 public client 值为 0,如果使用了 client secret 值为 1
|
||||
- **acr**:Authentication Context Class Reference 声明在终端用户认证未满足 ISO/IEC 29115 要求时为 "0"
|
||||
- **amr**:Authentication method,指示 token 的认证方式。值为 “pwd” 表示使用了密码
|
||||
- **groups**:指示主体所属的 groups
|
||||
- **iss**:issuer 标识生成该 token 的 security token service (STS)。例如 https://sts.windows.net/fdd066e1-ee37-49bc-b08f-d0e152119b04/(uuid 为 tenant ID)
|
||||
- **oid**:主体的 object ID
|
||||
- **tid**:Tenant ID
|
||||
- **iat, nbf, exp**:Issued at(签发时间),Not before(此时间之前不可使用,通常与 iat 相同),Expiration time(过期时间)
|
||||
|
||||
## FOCI Tokens Privilege Escalation
|
||||
|
||||
## FOCI Tokens 权限提升
|
||||
之前提到 refresh tokens 应该绑定到生成时的 **scopes**、生成时的 **application** 和 **tenant**。如果这些边界中的任意一项被突破,就可能发生权限提升,因为可以生成针对用户有权限访问的其他资源和 tenant 的 access tokens,并且获得比最初意图更多的 scopes。
|
||||
|
||||
之前提到 refresh tokens 应该绑定到生成它们时的 **scopes**、生成它们的 **application** 和 **tenant**。如果这些边界中的任意一项被打破,就可能发生权限提升,因为可以生成针对用户可访问的其他资源和租户且拥有比原本更多 scopes 的 access tokens。
|
||||
此外,**this is possible with all refresh tokens** in the [Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/) (Microsoft Entra accounts, Microsoft personal accounts, and social accounts like Facebook and Google) because as the [**docs**](https://learn.microsoft.com/en-us/entra/identity-platform/refresh-tokens) mention: "Refresh tokens are bound to a combination of user and client, but **aren't tied to a resource or tenant**. A client can use a refresh token to acquire access tokens **across any combination of resource and tenant** where it has permission to do so. Refresh tokens are encrypted and only the Microsoft identity platform can read them."
|
||||
|
||||
此外,**在 [Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/) 中所有的 refresh tokens 都有这种可能性**(Microsoft Entra accounts、Microsoft personal accounts,以及像 Facebook 和 Google 这样的 social accounts),正如 [**docs**](https://learn.microsoft.com/en-us/entra/identity-platform/refresh-tokens) 所述:
|
||||
"刷新令牌绑定到用户和客户端的组合,但 **不绑定到资源或租户**。客户端可以使用刷新令牌来获取在其有权限的任何资源与租户组合上的访问令牌。刷新令牌是加密的,只有 Microsoft identity platform 可以读取它们。"
|
||||
另外,请注意 FOCI applications 是 public applications,因此**不需要 secret**来进行服务器认证。
|
||||
|
||||
另外,注意 FOCI applications 是 public applications,因此 **无需 secret** 即可向服务器进行身份验证。
|
||||
在 [**original research**](https://github.com/secureworks/family-of-client-ids-research/tree/main) 中报告的已知 FOCI clients 可以在 [**found here**](https://github.com/secureworks/family-of-client-ids-research/blob/main/known-foci-clients.csv) 找到。
|
||||
|
||||
已知的 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) 查看。
|
||||
### Get different scope
|
||||
|
||||
### 获取不同作用域
|
||||
|
||||
根据之前的示例代码,下面的代码请求了一个针对不同作用域(scope)的新令牌:
|
||||
沿用前面的示例代码,这段代码请求了一个用于不同 scope 的新 token:
|
||||
```python
|
||||
# Code from https://github.com/secureworks/family-of-client-ids-research
|
||||
azure_cli_bearer_tokens_for_outlook_api = (
|
||||
@@ -207,22 +278,39 @@ pprint(microsoft_office_bearer_tokens_for_graph_api)
|
||||
|
||||
A BroCI refresh tokens is a brokered token exchange pattern where an existing refresh token is used with extra broker parameters to request tokens as another trusted first-party app.
|
||||
|
||||
这些 BroCI 刷新令牌是一种经 broker 中介的令牌交换模式:使用已有的刷新令牌并附加额外的 broker 参数,以代表另一个受信任的第一方应用请求令牌。
|
||||
|
||||
These refresh tokens must be minted in that broker context (a regular refresh token usually cannot be used as a BroCI refresh token).
|
||||
|
||||
这些刷新令牌必须在该 broker 上下文中签发(普通刷新令牌通常不能用作 BroCI 刷新令牌)。
|
||||
|
||||
|
||||
### Goal and purpose
|
||||
|
||||
BroCI 的目标是重用来自支持 broker 的应用链的有效用户会话,并为另一个受信任的 app/resource 配对请求令牌。因此可以从原始令牌实现“escalate privileges”。
|
||||
The goal of BroCI is to reuse a valid user session from a broker-capable app chain and request tokens for another trusted app/resource pair. Therefore, allowing to "escalate privileges" from the original token.
|
||||
|
||||
从攻击性角度看,这很重要,因为:
|
||||
BroCI 的目标是重用来自支持 broker 的应用链的有效用户会话,并为另一个受信任的应用/资源对请求令牌。因此可以从原始令牌实现“权限提升”。
|
||||
|
||||
- 它可以解锁标准 refresh 交换无法访问的已预先同意的第一方应用路径。
|
||||
- 它可以以具有广泛委派权限的应用身份返回针对高价值 API(例如 Microsoft Graph)的 access tokens。
|
||||
- 它将认证后基于令牌的枢转机会扩展到超过经典的 FOCI client switching 场景。
|
||||
From an offensive perspective, this matters because:
|
||||
|
||||
在 NAA/BroCI 刷新令牌中发生变化的不是可见的令牌格式,而是 Microsoft 在 brokered 刷新操作中验证的 **issuance context** 和与 broker 相关的元数据。
|
||||
- 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.
|
||||
|
||||
从进攻性视角来看,这很重要,因为:
|
||||
|
||||
- 它可以解锁那些标准刷新交换无法访问的预先同意的第一方应用路径。
|
||||
- 它可为高价值 API(例如 Microsoft Graph)返回访问令牌,且这些令牌以具有广泛委托权限的应用身份颁发。
|
||||
- 它将后认证的令牌枢轴机会扩展到超出经典 FOCI 客户端切换的场景。
|
||||
|
||||
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 刷新令牌变化的不是可见的令牌格式,而是颁发上下文和 Microsoft 在 brokered 刷新操作期间验证的与 broker 相关的元数据。
|
||||
|
||||
NAA/BroCI token exchanges are **not** the same as a regular OAuth refresh exchange.
|
||||
|
||||
NAA/BroCI 的令牌交换与常规 OAuth 刷新交换**不同**。
|
||||
|
||||
- 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.
|
||||
@@ -230,8 +318,17 @@ NAA/BroCI token exchanges are **not** the same as a regular OAuth refresh exchan
|
||||
- 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.
|
||||
|
||||
- 常规刷新令牌(例如通过 device code flow 获取)通常可用于标准的 `grant_type=refresh_token` 操作。
|
||||
- BroCI 请求包含额外的 broker 上下文(`brk_client_id`、broker 的 `redirect_uri` 和 `origin`)。
|
||||
- Microsoft 会验证所提供的刷新令牌是否在匹配的 broker 上下文中签发。
|
||||
- 因此,许多“普通”刷新令牌在 BroCI 请求中会失败,并出现诸如 `AADSTS900054`(“Specified Broker Client ID does not match ID in provided grant”)之类的错误。
|
||||
- 通常无法通过代码将普通刷新令牌“转换”为 BroCI 有效的令牌。
|
||||
- 你需要一个已由兼容 broker 流程签发的刷新令牌。
|
||||
|
||||
Check the web **<https://entrascopes.com/>** to find BroCI configured apps an the trust relationships they have.
|
||||
|
||||
请查看网站 **<https://entrascopes.com/>** 以查找已配置 BroCI 的应用及其信任关系。
|
||||
|
||||
|
||||
### Mental model
|
||||
|
||||
@@ -241,6 +338,13 @@ Think of BroCI as:
|
||||
|
||||
If any part of that broker chain does not match, the exchange fails.
|
||||
|
||||
思考 BroCI 可视为:
|
||||
|
||||
`user session -> brokered refresh token issuance -> brokered refresh call (brk_client_id + redirect_uri + origin) -> access token for target trusted app/resource`
|
||||
|
||||
如果该 broker 链的任何部分不匹配,交换将失败。
|
||||
|
||||
|
||||
### Where to find a BroCI-valid refresh token
|
||||
|
||||
One practical way is browser portal traffic collection:
|
||||
@@ -253,13 +357,27 @@ One practical way is browser portal traffic collection:
|
||||
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).
|
||||
|
||||
一种实际的方法是收集浏览器门户流量:
|
||||
|
||||
1. 登录 `https://entra.microsoft.com`(或 Azure 门户)。
|
||||
2. 打开 DevTools -> Network。
|
||||
3. 筛选:
|
||||
- `oauth2/v2.0/token`
|
||||
- `management.core.windows.net`
|
||||
4. 识别出 brokered 令牌响应并复制 `refresh_token`。
|
||||
5. 在为目标应用请求令牌时,使用该刷新令牌并提供匹配的 BroCI 参数(`brk_client_id`、`redirect_uri`、`origin`)(例如 ADIbizaUX / Microsoft_Azure_PIMCommon 场景)。
|
||||
|
||||
|
||||
### 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.
|
||||
|
||||
- `AADSTS900054`: 刷新令牌上下文与提供的 broker 元组(`brk_client_id` / `redirect_uri` / `origin`)不匹配,或该令牌并非来自 brokered 门户流程。
|
||||
- `AADSTS7000218`: 所选客户端流程期望机密凭证(`client_secret`/assertion),通常在尝试对非公开客户端使用 device code 时看到。
|
||||
|
||||
<details>
|
||||
<summary>Python BroCI 刷新辅助工具 (broci_auth.py)</summary>
|
||||
<summary>Python BroCI 刷新助手 (broci_auth.py)</summary>
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
@@ -532,26 +650,26 @@ raise SystemExit(main())
|
||||
```
|
||||
</details>
|
||||
|
||||
## 在哪里可以找到令牌
|
||||
## 在哪里可以找到 tokens
|
||||
|
||||
从攻击者的角度来看,当受害者的 PC 被入侵时,知道在哪里可以找到 access 和 refresh tokens 非常有价值:
|
||||
从攻击者的角度来看,知道在哪些地方可以找到 access and refresh tokens 非常有价值,尤其是在受害者的 PC 被攻破时,例如:
|
||||
|
||||
- Inside **`<HOME>/.Azure`**
|
||||
- **`azureProfile.json`** 包含过去登录的用户信息
|
||||
- **`clouds.config contains`** 包含有关订阅的信息
|
||||
- 在 **`<HOME>/.Azure`** 内
|
||||
- **`azureProfile.json`** 包含过去已登录用户的信息
|
||||
- **`clouds.config contains`** 包含关于订阅的信息
|
||||
- **`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`** and msal_token_cache.bin 用于 Windows,并使用 DPAPI 加密
|
||||
- **`service_principal_entries.bin`** 和 msal_token_cache.bin 在 Windows 上使用,并使用 DPAPI 加密
|
||||
- **`msal_http_cache.bin`** 是 HTTP 请求的缓存
|
||||
- Load it: `with open("msal_http_cache.bin", 'rb') as f: pickle.load(f)`
|
||||
- **`AzureRmContext.json`** 包含使用 Az PowerShell 的先前登录信息(但不包含凭据)
|
||||
- Inside **`C:\Users\<username>\AppData\Local\Microsoft\IdentityCache\*`** 有多个 `.bin` 文件,包含 **access tokens**、ID tokens 和账号信息,并由用户的 DPAPI 加密。
|
||||
- 在 **`C:\Users\<username>\AppData\Local\Microsoft\TokenBroken\Cache\`** 内的 `.tbres` 文件中可能会找到更多 **access tokens**,这些文件包含用 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。
|
||||
- 可以通过检查 `$HOME/.local/share/.IdentityService/` 是否存在来判断 Az PowerShell 是否在 Linux 或 macOS 上被使用(尽管其中的文件是空的且无用)
|
||||
- 如果用户通过浏览器 **logged inside Azure**,根据这篇 [**post**](https://www.infosecnoodle.com/p/obtaining-microsoft-entra-refresh?r=357m16&utm_campaign=post&utm_medium=web) 可以启动带有 **redirect to localhost** 的认证流程,让浏览器自动授权登录,并接收 refresh token。注意,只有少数 FOCI applications 允许 redirect to localhost(例如 az cli 或 powershell 模块),因此这些应用必须被允许。
|
||||
- 博客中解释的另一个选项是使用工具 [**BOF-entra-authcode-flow**](https://github.com/sudonoodle/BOF-entra-authcode-flow),它可以使用任何应用,因为它会 **获取 OAuth code,然后从最终认证页面的标题中获取 refresh token**,使用的 redirect URI 为 `https://login.microsoftonline.com/common/oauth2/nativeclient`.
|
||||
- 加载它:`with open("msal_http_cache.bin", 'rb') as f: pickle.load(f)`
|
||||
- **`AzureRmContext.json`** 包含使用 Az PowerShell 的先前登录信息(但不含凭据)
|
||||
- 在 **`C:\Users\<username>\AppData\Local\Microsoft\IdentityCache\*`** 中有多个 `.bin` 文件,包含用用户 DPAPI 加密的 **access tokens**、ID tokens 和账户信息。
|
||||
- 在 **`C:\Users\<username>\AppData\Local\Microsoft\TokenBroken\Cache\`** 内的 `.tbres` 文件中,可能会发现更多 **access tokens**,这些文件包含用 DPAPI 加密并以 base64 编码的 access tokens。
|
||||
- 在 Linux 和 macOS 上,如果使用了 Az PowerShell,可以运行 `pwsh -Command "Save-AzContext -Path /tmp/az-context.json"` 来获取 **access tokens, refresh tokens and id tokens**
|
||||
- 在 Windows 上这仅会生成 id tokens。
|
||||
- 可以通过检查 `$HOME/.local/share/.IdentityService/` 是否存在来判断 Az PowerShell 是否在 Linux 和 macOS 上被使用(尽管其中的文件为空且无用)
|
||||
- 如果用户通过浏览器已在 Azure 中登录,根据这篇 [**post**](https://www.infosecnoodle.com/p/obtaining-microsoft-entra-refresh?r=357m16&utm_campaign=post&utm_medium=web),可以启动带有 **redirect to localhost** 的认证流程,使浏览器自动授权登录,并接收 refresh token。注意,只有少数 FOCI 应用允许重定向到 localhost(例如 az cli 或 powershell 模块),因此必须允许这些应用。
|
||||
- 博文中解释的另一个选项是使用工具 [**BOF-entra-authcode-flow**](https://github.com/sudonoodle/BOF-entra-authcode-flow),该工具可使用任意应用,因为它会 **获取 OAuth code,然后从最终认证页面的标题中获取 refresh token**,使用的 redirect URI 为 `https://login.microsoftonline.com/common/oauth2/nativeclient`。
|
||||
|
||||
## 参考资料
|
||||
|
||||
@@ -559,5 +677,6 @@ raise SystemExit(main())
|
||||
- [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/)
|
||||
- [https://trustedsec.com/blog/full-disclosure-a-third-and-fourth-azure-sign-in-log-bypass-found](https://trustedsec.com/blog/full-disclosure-a-third-and-fourth-azure-sign-in-log-bypass-found)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Reference in New Issue
Block a user