
읽기 전용 Entra ID 앱 자격 증명 평가: Graph 권한, Azure RBAC 및 접근 가능한 클라우드 데이터를 열거한 다음, 발견 사항을 권한 상승 및 측면 이동 경로에 매핑합니다.
/ / ______ ___ ___ / /_ / / / /__ _ / / /_____ ____ \ / -) / -) -)/ / \ \ / __/ _ `// / '/ -) / //_/_/_/_/ _/ // _/_,////_\__/_/
╔╦╦╬╬╬╬╬╬╦╦╗
╔╬╬╬╝╝┘ ╚╝╝╬╬╬┐
╬╬╝╚╩╬╗╔ ╚╬╬╬
╬╝ ╚╬╬╗╗ ╔ ╚╬╗ ╬╬ ╔╗ ╚╬╬╬╬╬╬╦ ╬╬ we found your secret... ╔╬┤ ╬╬╬ ╬╬╬╬╬╬╬╬╝╝╝╬╬╗ ...now let's see what it ╬╬┤ ╚╩┘ ╚╬╬╬╬╬╩ ╠╬╬ can REALLY do. ( o_o)>=|= ╬╬┤ ╠╬╬ ╬╬ ╦╗ ╗╗ ╬╬ [ client_id + secret -> total recall ] └╬┐ ╚╬╗╗ ╔╬╬╝ ╔╬┘ └╬╗ ╚╩╩╬╬╬╩╩╝╝ ╔╬╬ ╚╬╬╬╗ ┌╗╬╬╝┘ ╚╩╬╬╬╦╦╦╦╦╦╬╬╬╝╝ ╚╚╝╝╝╝ // pst... that app registration talks too much. \
**What can this Entra ID client ID + secret actually do?**
You found an Entra ID (Azure AD) application credential — a client ID and secret —
on an authorized engagement, and the tenant it belongs to is in scope.
`secret_stalker` takes those two values and tells you, from a cold start:
1. **Is it valid, and when does the secret expire?** — and if not valid, *why*
(bad secret, expired secret, app not in the tenant…). For a valid secret it
reads the app registration's `passwordCredentials` and reports the expiration
date + days remaining (needs directory read; see note below).
2. **What Microsoft Graph rights does it carry?** — application permissions read
straight from the issued token, plus **Entra directory roles** it holds (even
detected passively from the token's `wids` claim) and **objects it owns**
(apps/SPs you can add credentials to).
3. **What control does it have over Azure?** — RBAC role assignments at
management-group and subscription scope.
4. **Can it reach real data?** — optional Key Vault (secrets / keys / certificates),
Storage (blob / file / queue / table), and Cosmos DB data-plane reachability checks.
5. **What's the impact?** — dangerous permissions, roles, ownership, and reachable
data mapped to known privesc / lateral-movement primitives, rated by severity,
with concrete **attack-path** narratives.
It authenticates with either a **client secret** or a **certificate** (`--cert`),
and works against **commercial and sovereign clouds** (`--cloud`).
It is **passive by default** and **never modifies anything** — read-only
enumeration only.
> ⚠️ **Authorized testing only.** Run it solely against tenants that are
> explicitly in scope for an engagement you are authorized to perform.
---
## Install```bash
pip install -r requirements.txt # just runs it from source
# — or —
pip install . # installs the `secret_stalker` command
pip install '.[cert]' # + certificate (--cert) auth support
pip install '.[dev]' # + pytest for the test suite
유일한 런타임 종속성은 requests입니다. 토큰은 로컬에서 (base64 +
JSON)으로 디코딩됩니다 — 서명 검증도, 암호화 라이브러리도, Microsoft SDK도 필요 없습니다. 유일한
예외는 인증서 인증(--cert)으로, JWT 클라이언트 어서션에 서명하려면 선택적 cryptography
패키지가 필요합니다. Python 3.7+가 필요합니다.
pip install . 후에는 python -m secret_stalker … 대신
secret_stalker …로 호출할 수 있습니다.
자격 증명으로 무엇을 할 수 있는지 알아보는 가장 빠른 방법:```bash
python -m secret_stalker
--tenant contoso.onmicrosoft.com
--client-id 11111111-2222-3333-4444-555555555555
--secret ''
`--tenant`는 테넌트 GUID 또는 도메인을 허용합니다 — 도메인은 공개 OpenID 구성 엔드포인트를 통해
해당 테넌트 ID로 자동 확인됩니다.
### 셸 기록에 비밀이 남지 않도록 하기
플래그 대신 환경 변수로 자격 증명을 전달하세요:```bash
export SS_TENANT=contoso.onmicrosoft.com
export SS_CLIENT_ID=11111111-2222-3333-4444-555555555555
export SS_SECRET='<client-secret>'
python -m secret_stalker
--tenant / --client-id / --secret 중 어느 것이든 SS_TENANT / SS_CLIENT_ID / SS_SECRET에서 가져올 수 있습니다. 플래그가 환경 변수보다 우선합니다.
이는 셸 히스토리에만 해당되는 이야기가 아닙니다. argv 값은 프로세스의 수명 동안 모든 로컬 사용자가 읽을 수 있습니다(ps, /proc/<pid>/cmdline). --secret 또는 --cert-password가 플래그로 전달되면 도구는 stderr에 한 줄짜리 알림을 출력합니다 — 이 값은 --json 또는 --export 출력에는 절대 나타나지 않습니다.
앱 등록은 종종 비밀 대신 인증서를 사용합니다. --cert (개인 키 및 인증서를 포함하는 PEM 또는 .pfx/.p12)를 전달하면 도구는 서명된 JWT 클라이언트 어설션으로 인증합니다:```bash
python -m secret_stalker --tenant contoso.onmicrosoft.com
--client-id --cert ./app.pem # or app.pfx
python -m secret_stalker ... --cert app.pfx --cert-password ''
인증서 인증에는 선택적 `cryptography` 패키지가 필요합니다 (`pip install '.[cert]'`).
이 도구는 인증서 자체의 만료일을 보고합니다(앱의 `keyCredentials`에서 지문으로 일치).
이는 클라이언트 비밀의 경우와 동일합니다. `--cert`/`--cert-password`는
`SS_CERT` / `SS_CERT_PASSWORD`에서도 읽습니다.
### 소버린 및 정부 클라우드
기본적으로 secret_stalker는 **상용(commercial)** 클라우드를 대상으로 합니다. 소버린 테넌트의 경우,
`--cloud`(또는 `SS_CLOUD`)를 전달하여 Entra 인증 기관과 Graph / ARM / Key
Vault 엔드포인트가 일치하도록 하세요. 그렇지 않으면 유효한 자격 증명이 액세스 권한이 없는 것처럼 보입니다:```bash
# US Government (GCC High)
python -m secret_stalker --cloud usgov --tenant contoso.onmicrosoft.us ...
# US DoD (L5)
python -m secret_stalker --cloud usdod ...
# Azure operated by 21Vianet (China)
python -m secret_stalker --cloud china --tenant contoso.partner.onmschina.cn ...
--cloud | Entra authority | Microsoft Graph | ARM | Key Vault |
|---|---|---|---|---|
public (기본값) | login.microsoftonline.com | graph.microsoft.com | management.azure.com | vault.azure.net |
usgov (GCC High) | login.microsoftonline.us | graph.microsoft.us | management.usgovcloudapi.net | vault.usgovcloudapi.net |
usdod (DoD) | login.microsoftonline.us | dod-graph.microsoft.us | management.usgovcloudapi.net | vault.usgovcloudapi.net |
china (21Vianet) | login.chinacloudapi.cn | microsoftgraph.chinacloudapi.cn | management.chinacloudapi.cn | vault.azure.cn |
gov, dod, commercial, gcc-high, 21vianet 같은 별칭도 허용됩니다.
(Storage 데이터 플레인 대상(audience)인 storage.azure.com은 모든 클라우드에서 동일합니다.)
이렇게 하면 인증을 수행하고 테넌트의 Graph appRole 맵을
~/.secret_stalker/app_roles_cache.json에 캐시한 후 종료됩니다.
자격 증명이 서비스 주체를 읽을 수 없다면 건너뛰세요. 번들로 제공되는 맵은
잘 알려진 권한을 계속 포함합니다.
Credential status : VALID Tenant : aaaaaaaa-... Client (app) id : 1111... App display name : Recon App SP object id : cccc... Secret : valid — expires 2027-03-01 (in 207 days)
OK graph OK arm NO storage — no storage token
...
[CRITICAL] (GRAPH) Application.ReadWrite.All Can add credentials to any app/SP and impersonate it — tenant-wide pivot. [CRITICAL] (ARM) Owner Full control including granting access to others. [CRITICAL] (DATA) keyvault:secrets Can read Key Vault secret values — connection strings, passwords, tokens. [MEDIUM] (GRAPH) Mail.Read Read all mailboxes — data exposure.