mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-07-29 07:00:29 -07:00
Translated ['', 'src/pentesting-cloud/azure-security/az-basic-informatio
This commit is contained in:
+168
-95
@@ -1,10 +1,10 @@
|
||||
# Az - Tokens & Public Applications
|
||||
# Az - 토큰 & 공개 애플리케이션
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Basic Information
|
||||
## 기본 정보
|
||||
|
||||
Entra ID는 Microsoft의 클라우드 기반 신원 및 접근 관리(ID and Access Management, 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
|
||||
|
||||
@@ -12,87 +12,160 @@ Entra ID는 Microsoft의 클라우드 기반 신원 및 접근 관리(ID and Acc
|
||||
|
||||
1. **Resource Server (RS):** 리소스 소유자가 소유한 리소스를 보호합니다.
|
||||
2. **Resource Owner (RO):** 일반적으로 보호된 리소스의 소유자인 최종 사용자입니다.
|
||||
3. **Client Application (CA):** 리소스 소유자를 대신하여 리소스에 접근하려는 애플리케이션입니다.
|
||||
4. **Authorization Server (AS):** 클라이언트 애플리케이션을 인증하고 권한을 부여한 후 접근 토큰을 발급합니다.
|
||||
3. **Client Application (CA):** 리소스 소유자를 대신해 리소스 접근을 요청하는 애플리케이션입니다.
|
||||
4. **Authorization Server (AS):** 클라이언트 애플리케이션을 인증·승인한 후 접근 토큰을 발급합니다.
|
||||
|
||||
**Scopes 및 Consent:**
|
||||
**스코프 및 동의:**
|
||||
|
||||
- **Scopes:** 리소스 서버에 정의된 세분화된 권한으로 접근 수준을 지정합니다.
|
||||
- **Consent:** 리소스 소유자가 특정 scopes로 클라이언트 애플리케이션의 리소스 접근을 허용하는 과정입니다.
|
||||
- **Consent:** 리소스 소유자가 특정 스코프를 가진 리소스 접근 권한을 클라이언트 애플리케이션에 부여하는 과정입니다.
|
||||
|
||||
**Microsoft 365 통합:**
|
||||
|
||||
- Microsoft 365는 IAM을 위해 Azure AD를 사용하며 여러 "first-party" OAuth 애플리케이션으로 구성됩니다.
|
||||
- 이러한 애플리케이션은 깊게 통합되어 있고 종종 상호 의존적인 서비스 관계를 가집니다.
|
||||
- 사용자 경험을 단순화하고 기능을 유지하기 위해 Microsoft는 이러한 first-party 애플리케이션에 대해 "implied consent" 또는 "pre-consent"를 부여합니다.
|
||||
- **Implied Consent:** 일부 애플리케이션은 **명시적인 사용자 또는 관리자 승인 없이 특정 scopes에 대한 액세스가 자동으로 부여됩니다**.
|
||||
- 이러한 사전 동의된 scopes는 일반적으로 사용자와 관리자 모두에게 숨겨져 있어 표준 관리 인터페이스에서 잘 보이지 않습니다.
|
||||
- 이러한 애플리케이션들은 깊게 통합되어 있으며 종종 상호 의존적인 서비스 관계를 가집니다.
|
||||
- 사용자 경험을 단순화하고 기능을 유지하기 위해 Microsoft는 이러한 first-party 애플리케이션에 "implied consent" 또는 "pre-consent"를 부여합니다.
|
||||
- **Implied Consent:** 특정 애플리케이션은 사용자 또는 관리자에 대한 명시적 승인 없이 특정 스코프에 대한 접근 권한을 자동으로 **부여받습니다.**
|
||||
- 이러한 사전 동의된 스코프는 일반적으로 사용자와 관리자 모두에게 숨겨져 있으며 표준 관리 인터페이스에서는 덜 눈에 띕니다.
|
||||
|
||||
**클라이언트 애플리케이션 유형:**
|
||||
|
||||
1. **Confidential Clients:**
|
||||
- 자체 자격증명(예: 비밀번호 또는 인증서)을 보유합니다.
|
||||
- Authorization Server에 대해 **자체를 안전하게 인증할 수 있습니다**.
|
||||
- 권한 서버에 **안전하게 자신을 인증할 수 있습니다.**
|
||||
2. **Public Clients:**
|
||||
- 고유 자격증명을 보유하지 않습니다.
|
||||
- Authorization Server에 대해 안전하게 인증할 수 없습니다.
|
||||
- **보안적 의미:** Authorization Server가 애플리케이션의 정당성을 확인할 수 있는 메커니즘이 없으므로 공격자가 토큰 요청 시 public client 애플리케이션을 가장할 수 있습니다.
|
||||
- 고유한 자격증명이 없습니다.
|
||||
- 권한 서버에 안전하게 인증할 수 없습니다.
|
||||
- **보안 영향:** 권한 서버가 애플리케이션의 정당성을 검증할 메커니즘이 없기 때문에 공격자는 토큰을 요청할 때 public client 애플리케이션을 가장할 수 있습니다.
|
||||
|
||||
### ROPC / Password Grant
|
||||
|
||||
The OAuth2 **Resource Owner Password Credentials** (**ROPC**) flow uses a direct `POST` to `https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token` with `grant_type=password`, a **username**, **password**, a **client_id**, and the requested **scope**. In Entra ID this is mainly interesting for **public clients** because the attacker can reuse Microsoft first-party client IDs or any other allowed public client without needing a 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 Sign-In Log Bypass Classes
|
||||
|
||||
일부 과거의 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 for stealth password validation
|
||||
|
||||
요청이 다른 **tenant GUID**의 token endpoint로 전송되면, Entra는 제출된 비밀번호가 제공된 username에 대해 올바른지 여부를 여전히 검증할 수 있습니다. 그 후 흐름은 해당 외부 tenant에 사용자가 없기 때문에 실패할 수 있습니다. 역사적으로 이것은 다음을 허용했습니다:
|
||||
|
||||
- **Password spraying / credential validation**이 피해자 tenant에 해당하는 sign-in log 없이 수행되는 경우
|
||||
- 비밀번호 단계가 성공했는지 여부를 드러내는 응답 차이
|
||||
- 토큰 발급은 없지만 일반적인 실패 로그인보다 적은 텔레메트리
|
||||
|
||||
#### 2. Force a post-password failure
|
||||
|
||||
credential 검증 **후에** 사용되는 파라미터(예: 유효하지 않은 `client_id`)가 잘못된 경우, 비밀번호가 이미 올바르더라도 전체 트랜잭션이 실패할 수 있습니다. 역사적으로 이러한 경우는 비밀번호 추측이 성공했음을 숨기면서 **failed** 로그인으로 표시되었습니다.
|
||||
|
||||
기억할 패턴은 다음과 같습니다:
|
||||
|
||||
- **Password check succeeds**
|
||||
- 이후의 검증 단계가 실패
|
||||
- 로그는 최종 트랜잭션 상태를 나타내며 성공한 password-validation 단계를 반영하지 않습니다
|
||||
|
||||
#### 3. Trigger logging failure with oversized-but-valid values
|
||||
|
||||
가장 위험한 유형은 요청이 구문적으로 유효하고 인증이 성공하여 **tokens are returned**되지만, 일부 **logged field**가 로그 파이프라인을 중단시킬 만큼 너무 큰 경우입니다. 보고된 예로는 다음이 포함됩니다:
|
||||
|
||||
- `openid openid openid ...`처럼 유효한 scope를 수천 번 반복
|
||||
- 지나치게 길지만 여전히 수용되는 **User-Agent** 헤더 전송
|
||||
|
||||
이는 일반적으로 다음과 같은 문제군을 시사합니다:
|
||||
|
||||
1. Entra가 자격 증명과 요청 구문을 검증한다
|
||||
2. The token is issued successfully
|
||||
3. 로깅이 원시 사용자 제어 필드를 영구 저장하려 시도한다
|
||||
4. 길이 또는 스키마 가정 때문에 로깅 쓰기가 실패한다
|
||||
5. 사용자는 해당 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 / defensive note
|
||||
|
||||
모든 유효한 토큰 사용이 반드시 Entra sign-in 이벤트와 일치한다고 가정하지 마세요. 의심스러운 Graph 활동을 조사할 때는 다음을 상호 연관 지어 확인하세요:
|
||||
|
||||
- **Non-interactive sign-in logs**
|
||||
- **Graph Activity Logs**
|
||||
- **IP address**, **user/object ID**, **session/correlation identifiers**, 그리고 **시간 창**
|
||||
|
||||
한 가지 실용적인 검증 방법은 의심되는 보이지 않는 성공 이벤트를 두 개의 정상적인 실패 로그인 사이에 끼워 넣고, 수집 지연 후에 예상되는 시퀀스 `Failed -> Successful -> Failed`의 중간 이벤트가 없는지를 확인하는 것입니다. 하위 Graph 활동은 존재하지만 sign-in 로그가 없다면 이를 잠재적인 **sign-in logging gap** 또는 **token replay** 상태로 간주하세요.
|
||||
|
||||
## Authentication Tokens
|
||||
|
||||
OIDC에서 사용되는 **세 가지 토큰 유형**이 있습니다:
|
||||
OIDC에서 사용되는 **세 가지 유형의 토큰**이 있습니다:
|
||||
|
||||
- [**Access Tokens**](https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens)**:** 클라이언트는 이 토큰을 리소스 서버에 제시하여 **리소스에 접근**합니다. 특정 사용자, 클라이언트 및 리소스의 조합에 대해서만 사용 가능하며 만료될 때까지 **취소(revoke)**할 수 없습니다 — 기본 만료 시간은 1시간입니다.
|
||||
- **ID Tokens**: 클라이언트가 Authorization Server로부터 받는 토큰으로, 사용자에 대한 기본 정보를 포함합니다. 특정 사용자 및 클라이언트의 조합에 **바인딩(bound)** 됩니다.
|
||||
- **Refresh Tokens**: 접근 토큰과 함께 클라이언트에 제공되며, **새로운 access 및 ID 토큰을 얻기 위해** 사용됩니다. 특정 사용자와 클라이언트의 조합에 바인딩되며 취소될 수 있습니다. 비활성 refresh token의 기본 만료 기간은 **90일**이며, 활성 토큰의 경우 **만료가 없음**(refresh token에서 새로운 refresh token을 얻는 것이 가능)입니다.
|
||||
- refresh token은 특정 `aud`, 일부 **scopes**, 그리고 **tenant**에 연동되어야 하며 해당 aud와 scopes(및 그 이상이 아닌)와 tenant에 대해서만 access token을 생성할 수 있어야 합니다. 그러나 **FOCI applications tokens**의 경우에는 그렇지 않습니다.
|
||||
- [**Access Tokens**](https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens)**:** 클라이언트가 리소스 서버에 **리소스에 접근**하기 위해 제시하는 토큰입니다. 특정 사용자, 클라이언트, 리소스의 조합에 대해서만 사용될 수 있으며 만료될 때까지 **철회(revoke)할 수 없습니다** — 기본값은 1시간입니다.
|
||||
- **ID Tokens**: 클라이언트가 권한 부여 서버로부터 수신하는 **토큰**입니다. 사용자에 대한 기본 정보를 포함합니다. 특정 사용자와 클라이언트의 조합에 **바인딩(bound)** 됩니다.
|
||||
- **Refresh Tokens**: access token과 함께 클라이언트에 제공됩니다. **새로운 access 및 ID 토큰을 얻기 위해** 사용됩니다. 특정 사용자와 클라이언트의 조합에 바인딩되며 철회될 수 있습니다. 기본 만료는 비활성 refresh token의 경우 **90일**, 활성 토큰의 경우 **만료 없음**입니다 (refresh token으로부터 새로운 refresh token을 얻을 수 있기에).
|
||||
- refresh token은 특정 **`aud`**, 일부 **scopes**, 그리고 **tenant**에 묶여 있어야 하며, 그 `aud`와 scopes(그리고 그 이상은 아님) 및 tenant에 대한 access token만 생성할 수 있어야 합니다. 그러나 **FOCI applications tokens**의 경우에는 그렇지 않습니다.
|
||||
- refresh token은 암호화되어 있으며 Microsoft만 복호화할 수 있습니다.
|
||||
- 새로운 refresh token을 얻는다고 해서 이전 refresh token이 취소되지는 않습니다.
|
||||
- 새로운 refresh token을 받는다고 해서 이전 refresh token이 폐기되지는 않습니다.
|
||||
|
||||
> [!WARNING]
|
||||
> **conditional access**에 대한 정보는 **JWT** 내부에 **저장**됩니다. 따라서 **허용된 IP 주소에서 토큰을 요청**하면 해당 **IP**가 토큰에 **저장**되고, 이후 해당 토큰을 사용해 **허용되지 않은 IP에서도 리소스에 접근**할 수 있습니다.
|
||||
> **conditional access**에 대한 정보는 **JWT** 내부에 **저장**됩니다. 따라서 **허용된 IP 주소에서 토큰을 요청**하면 해당 **IP**가 토큰에 **저장**되고, 이후 그 토큰을 **비허용 IP에서 사용하여 리소스에 접근**할 수 있게 됩니다.
|
||||
|
||||
### Access Tokens "aud"
|
||||
|
||||
"aud" 필드에 표시된 항목은 로그인 수행에 사용된 **리소스 서버**(애플리케이션)입니다.
|
||||
"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들일 뿐이며 더 많은 API가 존재합니다.
|
||||
> 다음은 `az account get-access-token`에서 지원하는 API 예시일 뿐이며, 더 많은 항목이 존재합니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>aud examples</summary>
|
||||
<summary>aud 예시</summary>
|
||||
|
||||
- **aad-graph (Azure Active Directory Graph API)**: 레거시 Azure AD Graph API(더 이상 권장되지 않음)에 접근하는 데 사용되며, 애플리케이션이 Azure Active Directory의 디렉터리 데이터를 읽고 쓸 수 있게 합니다.
|
||||
- **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 365 서비스 데이터에 대한 통합 엔드포인트인 Microsoft Graph API에 접근하는 데 사용됩니다. Azure AD, Office 365, Enterprise Mobility 및 보안 서비스와 같은 서비스의 데이터와 인사이트에 접근할 수 있습니다.
|
||||
- **ms-graph (Microsoft Graph API)**: Microsoft 365 서비스 데이터의 통합 엔드포인트인 Microsoft Graph API에 접근하는 데 사용됩니다. Azure AD, Office 365, Enterprise Mobility 및 보안 서비스 등의 데이터 및 인사이트에 접근할 수 있습니다.
|
||||
- `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"
|
||||
|
||||
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,32 +217,31 @@ scopes=["https://graph.microsoft.com/.default"],
|
||||
)
|
||||
pprint(new_azure_cli_bearer_tokens_for_graph_api)
|
||||
```
|
||||
### 기타 access token 필드
|
||||
### 기타 액세스 토큰 필드
|
||||
|
||||
- **appid**: 토큰을 생성하는 데 사용된 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는 토큰이 어떻게 인증되었는지를 나타냅니다. 값이 “pwd”이면 비밀번호가 사용되었음을 의미합니다.
|
||||
- **groups**: principal이 소속된 그룹을 나타냅니다.
|
||||
- **iss**: 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
|
||||
- **appid**: 토큰을 생성하는 데 사용된 애플리케이션 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**: iss는 토큰을 생성한 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**: 발급 시간(iat), Not before(이 시간 이전에는 사용할 수 없음, 보통 iat와 동일), 만료 시간(exp).
|
||||
|
||||
- **iat, nbf, exp**: 발급 시각(iat), Not before(nbf: 이 시간 이전에는 사용 불가, 보통 iat와 동일), 만료 시간(exp).
|
||||
|
||||
## FOCI Tokens Privilege Escalation
|
||||
|
||||
앞서 언급했듯이 refresh tokens는 생성될 때 사용된 **scopes**, 생성된 **application**, 그리고 생성된 **tenant**에 묶여야 합니다. 이러한 경계 중 어느 하나라도 무너진다면 사용자가 접근할 수 있는 다른 리소스와 테넌트에 대해 원래 의도보다 더 많은 scope를 가진 access token을 생성할 수 있게 되어 privilege escalation이 발생할 수 있습니다.
|
||||
앞서 언급했듯이 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/) (Microsoft Entra accounts, Microsoft personal accounts, and social accounts like Facebook and Google)에 있는 모든 refresh tokens에서 **이것이 가능**합니다. 이는 [**docs**](https://learn.microsoft.com/en-us/entra/identity-platform/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 applications는 public applications이므로 서버에 인증하기 위해 **no secret is needed**합니다.
|
||||
또한 FOCI 애플리케이션은 public applications이므로 서버에 인증하기 위해 **no secret is needed** 합니다.
|
||||
|
||||
그렇다면 [**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)에서 확인할 수 있습니다.
|
||||
|
||||
### 다른 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 = (
|
||||
@@ -186,7 +258,7 @@ scopes=[
|
||||
)
|
||||
pprint(azure_cli_bearer_tokens_for_outlook_api)
|
||||
```
|
||||
### 다른 클라이언트 및 스코프 가져오기
|
||||
### 다른 클라이언트와 스코프 가져오기
|
||||
```python
|
||||
# Code from https://github.com/secureworks/family-of-client-ids-research
|
||||
microsoft_office_client = msal.PublicClientApplication("d3590ed6-52b3-4102-aeff-aad2292ab01c")
|
||||
@@ -204,58 +276,58 @@ pprint(microsoft_office_bearer_tokens_for_graph_api)
|
||||
```
|
||||
## NAA / BroCI (Nested App Authentication / Broker Client Injection)
|
||||
|
||||
A BroCI refresh tokens는 기존 refresh token을 추가 브로커 파라미터와 함께 사용하여 다른 신뢰된 first-party 앱으로서 토큰을 요청하는 브로커드 토큰 교환 패턴이다.
|
||||
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.
|
||||
|
||||
이러한 refresh tokens는 해당 브로커 컨텍스트에서 발급되어야 하며(일반적인 refresh token은 보통 BroCI refresh token으로 사용할 수 없다).
|
||||
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-capable 앱 체인에서 유효한 사용자 세션을 재사용하고 다른 신뢰된 앱/리소스 쌍에 대한 토큰을 요청하는 것이다. 따라서 원래 토큰에서 권한을 상승시키는 것이 가능해진다.
|
||||
BroCI의 목적은 broker-capable 앱 체인에서 유효한 사용자 세션을 재사용하여 다른 신뢰된 app/resource 쌍에 대한 토큰을 요청하는 것입니다. 즉, 원래 토큰에서 권한을 "상승"시킬 수 있게 합니다.
|
||||
|
||||
공격 관점에서, 이는 중요하다. 왜냐하면:
|
||||
공격 관점에서 중요한 이유는 다음과 같습니다:
|
||||
|
||||
- 표준 refresh 교환으로 접근할 수 없는 사전 동의된 first-party 앱 경로를 열 수 있다.
|
||||
- 넓은 delegated 권한을 가진 앱 아이덴티티로 하여금 고가치 API(예: Microsoft Graph)에 대한 access tokens를 반환할 수 있다.
|
||||
- 고전적인 FOCI client switching을 넘어선 인증 후(post-authentication) 토큰 피벗 기회를 확장한다.
|
||||
- 표준 refresh 교환으로 접근할 수 없는 pre-consented first-party app 경로를 열 수 있습니다.
|
||||
- broad delegated permissions를 가진 앱 ID 하에서 high-value API(예: Microsoft Graph)에 대한 access tokens를 반환할 수 있습니다.
|
||||
- 고전적인 FOCI client 전환을 넘어선 인증 후 토큰 피벗 기회를 확장합니다.
|
||||
|
||||
NAA/BroCI refresh token에서 변경되는 것은 토큰의 가시적 형식이 아니라, 브로커된 리프레시 동작 동안 Microsoft가 검증하는 **issuance context**와 브로커 관련 메타데이터이다.
|
||||
NAA/BroCI refresh token에서 바뀌는 것은 눈에 보이는 토큰 형식이 아니라, Microsoft가 brokered refresh 작업 중에 검증하는 **issuance context**와 브로커 관련 메타데이터입니다.
|
||||
|
||||
NAA/BroCI 토큰 교환은 일반적인 OAuth refresh 교환과 **같지 않다**.
|
||||
NAA/BroCI 토큰 교환은 일반 OAuth refresh 교환과 **다릅니다**.
|
||||
|
||||
- 일반적인 refresh token(예: device code flow를 통해 얻은)은 보통 표준 `grant_type=refresh_token` 작업에 대해 유효하다.
|
||||
- BroCI 요청은 추가 브로커 컨텍스트(`brk_client_id`, 브로커 `redirect_uri`, 및 `origin`)를 포함한다.
|
||||
- Microsoft는 제시된 refresh token이 일치하는 브로커드 컨텍스트에서 발급되었는지 검증한다.
|
||||
- 따라서 많은 "정상" refresh token은 BroCI 요청에서 `AADSTS900054`("Specified Broker Client ID does not match ID in provided grant")와 같은 오류로 실패한다.
|
||||
- 일반적으로 정상적인 refresh token을 코드로 BroCI 유효한 토큰으로 "변환"할 수 없다.
|
||||
- 호환 가능한 brokered flow에서 이미 발급된 refresh token이 필요하다.
|
||||
- 일반적인 refresh token(예: device code flow로 획득한)은 보통 표준 `grant_type=refresh_token` 연산에 유효합니다.
|
||||
- BroCI 요청은 추가 브로커 컨텍스트(`brk_client_id`, broker `redirect_uri`, 및 `origin`)를 포함합니다.
|
||||
- Microsoft는 제시된 refresh token이 일치하는 brokered context에서 발급되었는지 검증합니다.
|
||||
- 따라서 많은 "일반" refresh token은 `AADSTS900054`("Specified Broker Client ID does not match ID in provided grant") 같은 오류로 BroCI 요청에서 실패합니다.
|
||||
- 일반적으로 정상 refresh token을 코드상에서 BroCI 유효 토큰으로 "변환"할 수 없습니다.
|
||||
- 호환되는 brokered flow로 이미 발급된 refresh token이 필요합니다.
|
||||
|
||||
Check the web **<https://entrascopes.com/>** to find BroCI configured apps an the trust relationships they have.
|
||||
브로커 구성된 앱과 그들이 가진 신뢰 관계를 찾으려면 웹 <https://entrascopes.com/> 을 확인하세요.
|
||||
|
||||
|
||||
### Mental model
|
||||
|
||||
BroCI를 다음과 같이 생각하라:
|
||||
BroCI를 다음과 같이 생각하세요:
|
||||
|
||||
`user session -> brokered refresh token issuance -> brokered refresh call (brk_client_id + redirect_uri + origin) -> access token for target trusted app/resource`
|
||||
|
||||
그 브로커 체인의 어느 부분이라도 일치하지 않으면, 교환은 실패한다.
|
||||
브로커 체인의 어느 부분이라도 일치하지 않으면 교환은 실패합니다.
|
||||
|
||||
### Where to find a BroCI-valid refresh token
|
||||
|
||||
실용적인 방법 하나는 브라우저 포털 트래픽 수집이다:
|
||||
실용적인 방법 중 하나는 브라우저 포털 트래픽 수집입니다:
|
||||
|
||||
1. Sign in to `https://entra.microsoft.com` (or Azure portal).
|
||||
2. Open DevTools -> Network.
|
||||
3. Filter for:
|
||||
1. `https://entra.microsoft.com` (또는 Azure portal)에 로그인합니다.
|
||||
2. DevTools -> Network를 엽니다.
|
||||
3. 다음으로 필터링합니다:
|
||||
- `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).
|
||||
4. brokered token 응답을 식별하고 `refresh_token`을 복사합니다.
|
||||
5. 대상 앱(예: ADIbizaUX / Microsoft_Azure_PIMCommon 시나리오)에 대한 토큰을 요청할 때 일치하는 BroCI 매개변수(`brk_client_id`, `redirect_uri`, `origin`)와 함께 해당 refresh token을 사용합니다.
|
||||
|
||||
### Common errors
|
||||
|
||||
- `AADSTS900054`: refresh token 컨텍스트가 제공된 브로커 튜플 (`brk_client_id` / `redirect_uri` / `origin`)과 일치하지 않거나 토큰이 브로커드 포털 흐름에서 온 것이 아니다.
|
||||
- `AADSTS7000218`: 선택된 클라이언트 흐름이 confidential credential (`client_secret`/assertion)을 기대한다. 이는 종종 device code를 non-public client와 함께 시도할 때 관찰된다.
|
||||
- `AADSTS900054`: refresh token 컨텍스트가 제공된 브로커 튜플(`brk_client_id` / `redirect_uri` / `origin`)과 일치하지 않거나 토큰이 brokered portal flow에서 나온 것이 아닙니다.
|
||||
- `AADSTS7000218`: 선택된 client flow는 confidential credential(`client_secret`/assertion)을 기대하며, 보통 non-public client로 device code를 시도할 때 발생합니다.
|
||||
|
||||
<details>
|
||||
<summary>Python BroCI refresh helper (broci_auth.py)</summary>
|
||||
@@ -533,30 +605,31 @@ raise SystemExit(main())
|
||||
|
||||
## 토큰을 찾을 수 있는 위치
|
||||
|
||||
공격자 관점에서, 예를 들어 피해자의 PC가 침해되었을 때 access and refresh tokens를 어디서 찾을 수 있는지 아는 것은 매우 흥미롭다:
|
||||
공격자 관점에서, 예를 들어 피해자의 PC가 침해되었을 때 access and refresh tokens를 어디에서 찾을 수 있는지 아는 것은 매우 흥미롭습니다:
|
||||
|
||||
- **`<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`** 및 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로 암호화되어 있다.
|
||||
- `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 and id tokens**를 얻을 수 있다.
|
||||
- Windows에서는 이는 id tokens만 생성한다.
|
||||
- Linux 및 macOS에서 Az PowerShell 사용 여부는 `$HOME/.local/share/.IdentityService/`의 존재로 확인할 수 있다(단 포함된 파일들은 비어 있고 쓸모없음).
|
||||
- 사용자가 **logged inside Azure with the browser** 상태라면, 이 [**post**](https://www.infosecnoodle.com/p/obtaining-microsoft-entra-refresh?r=357m16&utm_campaign=post&utm_medium=web)에 따르면 인증 흐름을 **redirect to localhost**로 시작하고 브라우저가 자동으로 로그인 승인을 하도록 만들어 refresh token을 받을 수 있다. 단, az cli나 powershell module과 같이 localhost 리디렉트를 허용하는 FOCI 애플리케이션은 극소수이므로 해당 애플리케이션들이 허용되어 있어야 한다.
|
||||
- 블로그에서 설명한 다른 옵션은 도구 [**BOF-entra-authcode-flow**](https://github.com/sudonoodle/BOF-entra-authcode-flow)를 사용하는 것이다. 이 도구는 어떤 애플리케이션이든 사용할 수 있는데, 최종 인증 페이지의 제목에서 OAuth 코드를 얻어 그 코드로 refresh token을 가져오기 때문이다. 리디렉트 URI는 `https://login.microsoftonline.com/common/oauth2/nativeclient`.
|
||||
- **`azureProfile.json`**에는 이전에 로그인한 사용자에 대한 정보가 들어 있습니다
|
||||
- **`clouds.config`**에는 구독에 대한 정보가 들어 있습니다
|
||||
- **`service_principal_entries.json`**에는 애플리케이션 자격증명(tenant id, clients 및 secret)이 들어 있습니다. Linux & macOS에서만 해당
|
||||
- **`msal_token_cache.json`**에는 access tokens 및 refresh tokens가 들어 있습니다. Linux & macOS에서만 해당
|
||||
- **`service_principal_entries.bin`** 및 **`msal_token_cache.bin`**은 Windows에서 사용되며 DPAPI로 암호화되어 있습니다
|
||||
- **`msal_http_cache.bin`**은 HTTP 요청 캐시입니다
|
||||
- Load it: `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` 파일에서도 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 및 macSO에서 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 to localhost**로 인증 흐름을 시작해 브라우저가 자동으로 로그인을 허용하도록 만들고 resh token을 받을 수 있습니다. localhost로 리디렉션을 허용하는 FOCI 애플리케이션은 몇 개뿐(예: az cli 또는 the powershell module)이므로 이러한 애플리케이션이 허용되어야 합니다
|
||||
- 블로그에서 설명한 또 다른 방법은 도구 [**BOF-entra-authcode-flow**](https://github.com/sudonoodle/BOF-entra-authcode-flow)를 사용하는 것입니다. 이 도구는 모든 애플리케이션을 사용할 수 있는데, 리디렉트 URI `https://login.microsoftonline.com/common/oauth2/nativeclient`를 사용하여 최종 인증 페이지의 제목에서 OAuth 코드를 얻고 그 코드를 통해 refresh token을 획득하기 때문입니다
|
||||
|
||||
## 참고자료
|
||||
## References
|
||||
|
||||
- [https://github.com/secureworks/family-of-client-ids-research](https://github.com/secureworks/family-of-client-ids-research)
|
||||
- [https://github.com/Huachao/azure-content/blob/master/articles/active-directory/active-directory-token-and-claims.md](https://github.com/Huachao/azure-content/blob/master/articles/active-directory/active-directory-token-and-claims.md)
|
||||
- [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