AuthCodeFix aka ConsentFix

年の瀬を前にConsentFixが現れました。正規の認証フローを悪用して認可コードを盗み出し、事実上Microsoft Entraの鍵を攻撃者に渡してしまう巧妙なOAuthベースの攻撃です。条件付きアクセスがあってもなぜ通ってしまうのか、ログにどんな痕跡が残るのか、そして守る側が実害の出る前にどう検知し止められるのかを解きほぐします。

AuthCodeFix aka ConsentFix

年末が近づくと恒例のように、新しい脆弱性か巧妙な攻撃手口が現れ、守る側はユーザーを守ろうと奔走することになります。その傍らで、他の攻撃者やレッドチームはそれをよく見て取り入れていきます。

今年はPushSecurityが「ConsentFix」と名付けた攻撃を見つけました。ClickFix攻撃の発展形で、Entraという王国の鍵をそのまま攻撃者に渡すことになるURIを、ユーザー自身に提供させるものです。実際に観測された手口は、ユーザーが手でコピー&ペーストすることを前提にしていました。その数日後、John Hammondがコピー&ペーストを不要にした改良版を実演する動画を公開しました。こちらではユーザーが認可コードを攻撃者にドラッグ&ドロップするだけで済みます。

この攻撃がなぜ成立し、デバイスのコンプライアンスをはじめとする条件付きアクセスの要件を一見すり抜けてしまうのか。その技術的な詳細をたどると、OAuth 2.0の認可コードフローに行き着きます。

OAuth 2.0の認可コードフロー

攻撃者は「Microsoft Azure CLI」クライアントと「Azure Resource Manager」リソースを対象にしたMicrosoft Entraのログインurlを作り、ユーザーが悪意あるサイトを訪れたときにそれを開きます。

認可コードフローに当てはめると、これはAzure CLIのようなネイティブのパブリックアプリがユーザーを認証するために通常最初に呼び出す手順にあたります。アプリケーションは実行中のマシン上で、ランダムな高位ポートにリスナーを立てます。このポートがいわゆるリプライURIとして使われます。

これは自分でも簡単に再現できます。たとえばTokenTacticsV2を使うか、URIを手で組み立ててください。

TokenTacticsV2

ユーザーがEntra IDへのサインインに成功すると、リプライURI(たとえば http://localhost:3001 )へリダイレクトされます。通常であれば、ここでAzure CLIがこのURIへの呼び出しを受け取り、リダイレクトに含まれる重要かつ決定的な情報を手にします。

  • code
    これがauthorization_codeです。アプリケーションはこれを使ってベアラートークンを要求します。ベアラートークンはアクセストークン、IDトークン、場合によってはリフレッシュトークンから成ります。
    ドキュメントによれば、このコードの有効期間はおよそ10分で、その間に引き換える必要があります。
  • state
    これは省略可能なパラメーターで、アプリケーションは要求と応答で同じ値かどうかを検証すべきです。

攻撃の筋書きでもユーザーはリダイレクトされますが、localhostで動いているアプリケーションがないため、ブラウザーはエラーになります。

ブラウザーがエラーになる様子

それでもURIには機微な情報が残っており、攻撃者はユーザーにこれを渡させようとします。ユーザーが応じてしまうと、攻撃者はトークンを引き換え、アクセストークンとリフレッシュトークンを使って対象のリソース、この場合はAzure Resource Managerにアクセスできます。

次のスクリーンショットでは、ユーザーから渡されたURIを使ってベアラートークンを取得する様子が分かります。

ユーザーから渡されたURIを使ったベアラートークンの取得

検知の仕組みを試す場合は、最後の手順を別のネットワークにある別のシステムから実行してください。

検知に使えるアーティファクト

攻撃を再現してSigninLogsとAADNonInteractiveUserSignInLogsを確認すると、この1回のサインイン活動に対して2つのイベントが見つかります。1つ目は実際のユーザーのサインインを表し、2つ目は攻撃者側の基盤から発生したものです。

アクティビティログ

大きな違いは、1つ目が対話的なサインインイベントであるのに対し、2つ目が非対話的である点です。これは認証フローの2つの段階に対応します。まずユーザー、次にアプリケーション、この場合は攻撃者です。

Azure CLIの通常の挙動であれば、2つのサインインイベントは同じIPアドレスから発生します。しかしこの例ではIPアドレスが異なり、しかも別の国から来ています。もちろん後者は当てにできる指標ではありません。攻撃者が足跡を隠すために被害者と同じ国に居ることもありうるからです。

欠けているつながり

この2つのイベントを結びつける良い手がかりを探したとき、まず思いつくのはUnique Token Identifier(UTI)を見ることです。ところがMicrosoftは認可コードのUTIとベアラートークンのUTIに別の値を使うため、この方法は確かなつながりにはなりません。

Unique Token Identifier

一方でSessionIdは2つを結ぶ良い手がかりになります。ただし長く使われ続けるIDなので、正当なものを含めてこの組み合わせが複数含まれることもあります。

認可コードフローの制約という知識に加えて、ユーザーIDとアプリケーションIDも手がかりに使えば、時間が重要な検知の材料になります。

  • 両方のイベントのSessionIdが同じであること
  • 両方のイベントのApplicationIdが同じであること
  • 両方のイベントのUserIdが同じであること
  • 2つ目のイベントが1つ目より後であること
  • 2つ目のイベントが1つ目からおよそ10分以内に収まっていること。ちょうど10分にすべきではありません。Microsoftは「[...] they expire after about 10 minutes」と書いているからです
  • 対象にするのは直後の2つ目のイベントだけで、それ以降のものは含めないこと

豆知識
ResourceIdentityは良い手がかりではありません。リソースは認可コードに紐づいていないため攻撃者が変更できるからです。対象のアプリケーションIDは変更できません。

ノイズを減らす

ここまでの知識ですでに十分に使える検知ができましたが、それでも良性の検出が混ざっていました。最近の開発者は、ローカルのインスタンスのように見えるクラウドのリソースを使うため、ログには不規則なログインの並びが残ります。

決め手になるのは時間の要素です。この攻撃はURIをコピー&ペーストするかドラッグ&ドロップするというユーザーの操作を必要とするのに対し、良性の検出の発生源として特定したGitHub Codespaceの使い方は完全に自動化されていて、認可コードを数秒のうちに引き換えます。

そのため、この認証のやり取りを数秒で済ませているものを除外すれば、ほぼ良性として取り除けます。

もう1つのノイズの原因になりうるのが、インターネット通信の出口が変わることです。とくにSD-WAN、ZTNA、Secure Web Gatewayを使う構成で起こります。

影響を受けるファーストパーティアプリケーション

最初の報告では悪用されたアプリケーションとして「Microsoft Azure CLI」が挙げられていますが、どのテナントにも事前同意済みで存在し、localhostをリダイレクト先として認めるMicrosoftのファーストパーティアプリは他にもたくさんあります。しかも対象はそれだけではありません。攻撃者は、公開名前解決できないテスト用や開発用のリプライURLも悪用できます。

リソースに対する事前同意の権限が強い、とくに注意すべきアプリケーションを挙げます。

  • Microsoft Azure CLI (04b07795-8ddb-461a-bbee-02f9e1bf7b46)
  • Microsoft Azure PowerShell (1950a258-227b-4e31-a9cf-717495945fc2)
  • Visual Studio (04f0c124-f2bc-4f59-8241-bf6df9866bbd)
  • Visual Studio Code (aebc6443-996d-45c2-90f0-388ff96faa56)
  • MS Teams PowerShell Cmdlets (12128f48-ec9e-42f0-b203-ea49fb6af367)

こうしたアプリの全一覧は、同僚のFabian BaderによってEntraScopes.comに収録されています。

緩和策と保護

攻撃面と対象範囲を絞る

導入の手間: 低〜高(正当な利用者を洗い出す手間による)
緩和の効果: 中(攻撃の対象になりうる範囲を狭める)
適用範囲: 限定的

選択肢1:ユーザーの割り当てを必須にする

前提条件:

  • 対象となるファーストパーティアプリのサービスプリンシパルを、Microsoft Graph APIまたはPowerShellで追加する
  • サービスプリンシパルのオブジェクトに対して、Microsoft Graph APIまたはPowerShellでユーザー割り当ての要件を適用する
  • Access Packages、PIM-for-Groups(Just-in-Timeアクセス用)、またはその組み合わせによって、申請に応じてユーザーを割り当てる手順を整える。

// Example for Microsoft Graph PowerShell
Connect-MgGraph -Identity
$AppId = "04b07795-8ddb-461a-bbee-02f9e1bf7b46" // Microsoft Azure CLI
$sp = Get-MgServicePrincipal -Filter "appId eq '$AppId'"
Update-MgServicePrincipal -ServicePrincipalId $sp.Id -AppRoleAssignmentRequired:$false

利点:

  • Access Packagesや手動のグループ所属を通じてユーザーの割り当てを管理でき、この攻撃手法にさらされる範囲を絞れます。
  • 対象候補としてのグループ所属と組み合わせてJust-in-Timeアクセスを提供でき、CLIツールへのアクセスを一時的なものにすることで攻撃面をさらに狭められます。
  • 条件付きアクセスのポリシーを評価する前に適用されます。
  • 他の筋書きに対しても攻撃面を狭めます。

欠点:

  • 対象を絞れるのは特定のユーザーまでで、特定のデバイスの利用といった他の要件とは組み合わせられません
  • 正当なCLIツールの利用者をすべて洗い出す必要があります
  • これまでのサインインを確認しながら、副作用と組織への影響を丁寧に見極める必要があります。

選択肢2:条件付きアクセスのポリシーでアクセスを遮断する

前提条件:

  • 「Microsoft Graph Command Line Tools」と「Windows Azure Service Management API」を対象に、正当な利用者を除外したうえでCLIツールへのアクセスを遮断する条件付きアクセスのポリシーを作成する
  • 除外はグループ所属で管理する。手動でも、エンタイトルメント管理(Access Packagesなど)でもよい。

利点:

  • 正当でない利用者や権限のない利用者へのトークン発行を防ぎます。
  • デバイスやネットワークといった追加の条件に基づいて、きめ細かく対象を絞れます。

欠点:

  • 正当なCLIツールの利用者をすべて洗い出して除外する必要があります。
  • これまでのサインインを確認し、ポリシーをレポート専用モードで評価しながら、副作用と組織への影響を丁寧に見極める必要があります。

認可コードフローによるトークン発行を遮断する

選択肢: トークン保護を必須にする
導入の手間: 高
緩和の効果: 高
適用範囲: 非常に限定的

前提条件:

  • Microsoft Entra ID P1のライセンス
  • Windowsプラットフォーム上のEntra ID登録デバイス、ハイブリッド参加デバイス、またはEntra ID参加デバイス
  • Azure CLI、Azure PowerShell、Microsoft Graph PowerShellでWeb Account Manager(WAM)を有効にする(最近のバージョンでは既定)
  • 条件付きアクセスを次のように構成する。
    • クラウドアプリの対象を次のアプリに設定する。
      • Office 365 Exchange Online
      • Office 365 SharePoint Online
      • Microsoft Teams Services
    • モバイルアプリとデスクトップクライアント のクライアントアプリにトークン保護を必須とする。
    • ポリシーの対象とする デバイスプラットフォーム として Windows を選ぶ

利点:

Microsoft Entraのトークン保護はproof-of-possession(PoP)を要求します。これは、クライアントがWindowsのWeb Account Manager(WAM)のような信頼されたトークンブローカーと直接やり取りする場合にしか強制できません。ブラウザーはこの安全な経路を確立できないため、ブラウザーで開始された認可コードフローはトークン保護のポリシーによって遮断されます。

ポリシーがブローカー経由のPoPを要求するトークン保護を強制している場合、ブラウザーに返された認可コードは引き換えられません。コードをトークンに交換する際に必要となる、ブローカーが署名した証明をブラウザーが作れないからです

この場合、対象のアプリケーションをトークン保護で守れる限り、AuthCodeFixによる攻撃は完全に緩和されます。

下のスクリーンショットのとおり、フィッシングによって被害者に開始させた認可コードフローの引き換えを、トークン保護がしっかり止めています。

認可コードフローの引き換えをトークン保護が止めている様子

欠点:

  • 公式にサポートされているリソースは次のものだけです。
    • Office 365 Exchange Online
    • Office 365 SharePoint Online
    • Microsoft Teams Services

      Microsoft Graph APIは前述のリソースを通じて間接的に含まれ、Microsoft Graph PowerShellはサポート対象のクライアントとして挙げられています。この筋書きでも攻撃が緩和されることは、当社の検証で確認できました。「Windows Azure Service Management API」はサポート対象のリソースとして挙げられていません。2つのCLIクライアント(Azure CLIとAzure PowerShell)はどちらもWAMに対応しており、これはトークン保護を使うためのクライアント側の要件です。MicrosoftはAzureの管理シナリオ向けにトークン保護の機能を広げることをブログ記事で表明しています。
  • Microsoft Graph PowerShellにいくつか不具合があり、WAMの連携を一時的に無効にせざるをえない場合があります
  • これまでのサインインを確認し、ポリシーをレポート専用モードで評価しながら、副作用と組織への影響を丁寧に見極める必要があります。クラウドアプリを対象にすると、Microsoft 365の日常業務のアクセスにも影響します。
  • 対応するプラットフォームとEntra ID連携デバイスでしか使えないため、適用範囲は限られます。

準拠ネットワークの確認または信頼済みネットワークで以降のトークン発行を遮断する

導入の手間: 中
緩和の効果: 中
適用範囲: 広い

選択肢:Global Secure Accessで準拠ネットワークの外からのアクセスを遮断する

前提条件:

  • Entra ID P1のライセンス
  • Windows、macOS、Android、iOSの各プラットフォーム上のEntra ID登録デバイス、ハイブリッド参加デバイス、またはEntra ID参加デバイス
  • 対象となるすべてのクライアントにGlobal Secure Accessクライアントを導入し、M365 Traffic Profile向けにEntra Internet Accessを有効にすること
  • ネットワークの準拠確認を強制する条件付きアクセスのポリシーを、すべてのクラウドアプリに適用すること

利点:

信頼済みネットワークの確認を強制することで、以降のトークン発行を遮断します。この緩和策により、攻撃者は認可コードフローで得たリフレッシュトークンを使って新しいトークンを取得できなくなります。ただし、最初の認可コードの引き換えや最初のアクセストークンの発行そのものは防げません。そのトークンはもともと被害者が要求したものなので、準拠ネットワークの外でも有効なままです。

準拠ネットワークの条件とともにGSAを強制すると、他のトークンリプレイの筋書きも遮断でき、検知やハンティングにとても役立つログも増えます。

欠点:

  • Global Secure Accessクライアントを導入したユーザーとデバイスにしか適用できません
  • Entra ID連携デバイスでしか使えないため、適用範囲は限られます
  • 条件付きアクセスで準拠ネットワークを強制する際は、鶏と卵の問題を避けるためにIntuneなどいくつかの除外が必要になります。展開前に入念な検証が要ります

ハンティングクエリ

トークン窃取の緩和に必要な前提が整ったら、つまりGSAクライアントを展開し(NetworkAccessTrafficログの取り込みを含む)、WAM認証を活かせるようになったら、脅威ハンティングと裏づけの手立てが増えます。

GSAのログとWAM認証を使ったハンティング、または検知結果の確度の確認

このハンティングクエリは、Global Secure Access(GSA)のNetworkAccessTrafficログを使います。このログには、Microsoft Entraのトークンエンドポイントとの通信を開始したプロセスが含まれます。これにより、トークン要求がブラウザーから直接出たものかどうか、またGSAのネットワークの外で追加のトークン要求が行われたかどうかを判断できます。

このクエリは前提条件が満たされている場合にのみ動作し、信頼できる結果を返します。そうでなければ誤検知が多くなります。

なぜこれが効くのか。WindowsデバイスでWeb Account Manager(WAM)を使ってCLIやPowerShellのモジュールからサインインする場合、ブラウザーを介した認可コードは関与しません。このサインインの挙動は最近のバージョンでは既定です。したがって、開始したプロセスがブラウザーの実行ファイル(たとえばmsedge.exe)であれば、それは疑わしい活動の強い手がかりになります。macOSでは、Platform SSOを使っているとCompany Portalアプリ(com.microsoft.CompanyPortalMac.ssoextension)がプロセスを開始します。

トークンのバインドとPoP。WAM認証では通常、Proof-of-Possession(PoP)を強制することでトークンがデバイスに紐づきます。攻撃者はPoPなしにはさらに紐づいたトークンを発行できないため、紐づいていないリフレッシュトークンもまた強い手がかりです。

制約。ここで挙げた手がかりはいずれも、アクセス元のデバイスがMicrosoft Entra IDに登録または参加している場合にしか得られません。

確度スコアの考え方。このクエリは複数の手がかりを組み合わせて確度スコアを算出します。

  • トークン要求を開始したブラウザーのプロセスがあるか。
  • 紐づいていないトークンへの降格を検知したか。
  • サインインの間にネットワークプロバイダーが変わったか(準拠から非準拠への変化を含む)。

これらの手がかりは、活動を探すためにクエリの中で使うこともできますし、先の検知に基づいてインシデントが起きた際の確度スコアを導くのにも使えます。

ハンティングクエリで使う手がかり

条件に応じて、次のようにスコアが表示されます。

非常に高い確度は、NetworkAccessTrafficのログがトークン要求の開始元として見慣れたブラウザーのプロセスを示し、かつ紐づいていないトークンへの降格が検知された場合に表示されます。

高い確度は、別のネットワークプロバイダー(ASN)からのサインインであり、かつ紐づいていないトークンを伴う非準拠ネットワークだった場合に表示されます。

中程度の確度は、ネットワークプロバイダーの変化と準拠ネットワークだけが確認され、あわせて使われたトークンの種類が変わっている場合に表示されます。

ハンティングクエリの最新版はGitHubにあります。

発行されたトークンによる活動を追う

調査はサインインのイベントだけにとどめず、攻撃者が発行させたトークンを使って行われた活動まで広げて考えるべきです。同僚のThomas NaunheimがMicrosoftCloudActivityというKQL関数を公開しており、この広げた調査に役立ちます。さらに、対象のSessionIdを、先のハンティングで見つけた疑わしいUniqueIdと突き合わせれば、より深く分析できます。

KQL関数

この例では、攻撃者は攻撃中に得たリフレッシュトークンを使ってMicrosoft Graph API向けのアクセストークンを発行させました。そのトークンを使って被害者が所有するアプリケーションにクライアントシークレットを追加し、居座りと横展開を図っています。このクエリは、トークン保護の状態や、その操作がGlobal Secure Accessのネットワークの外で行われたかどうかを含め、Graph APIの操作の詳細を示します。

Graph APIの操作のスクリーンショット

さらに読む

関連記事