2つのキャンペーンが、ログインなしでMicrosoft Entra IDアカウントを列挙するために400万件以上の偽IDを作成していました。脅威そのものではなく、その挙動を検知する方法を解説します。
要点まとめ
- OAuthクライアントID詐称は、アプリケーション名や既知のアプリケーションIDに依存する検知手法を無効化します。フィールド自体が捏造・ローテーション・空欄になっているためです。
- AADSTS700016が未認識のクライアントIDと組み合わさって発生している場合、それは壊れたアプリ登録ではなく有効な認証情報を意味している可能性があります。ノイズではなくトリアージのシグナルとして扱うべきです。
- 効果的な検知ロジックは、単一のイベントではなく、クライアントIDのカーディナリティ、欠落したアプリケーション名、AADSTSエラーの発生順序を、一定の時間枠にわたって相関させます。
- リハーサル済みの対応手順書(リセット、失効、レビュー)は検知クエリそのものと同じくらい重要であり、これを省略した場合のコストはアラート疲れではなくアカウント乗っ取りという形で現れます。
見過ごされがちなシグナル
Microsoft Entra IDに対するサインイン失敗アラートのキューを思い浮かべてください。それぞれが異なるアプリケーションIDを指しており、いずれもテナントに登録されていません。単体で見れば、どのIDも量的なしきい値を超えることはありません。キュー全体を見ても、協調された攻撃には見えず、どのテナントにも蓄積されがちな、期限切れのアプリ登録や忘れられたテストスクリプトによる通常のゴミのように見えます。特定のアプリケーションで失敗が急増していないか探すアナリストなら、このキューを二度見することなくスクロールして通り過ぎてしまうでしょう。それこそが狙いです。2025年12月以降、少なくとも2つの脅威アクターが、攻撃ではなく設定上のノイズに見えるよう特別に設計された列挙キャンペーンを構築してきました。この2つの違いは、脅威インテリジェンスの問題というより、検知エンジニアリングの問題です。
3つのエラーコードに見る攻撃の仕組み
この手法は、1回のトークンリクエストにユーザー名・パスワード・クライアントIDをまとめて送信するOAuth 2.0 Resource Owner Password Credentials(ROPC)フローを悪用します。重要なシグナルを伝えるのは、Entra IDの3つのレスポンスコードです。
AADSTS50034はユーザー名が存在しないことを意味します。AADSTS50126はユーザー名は存在するもののパスワードが誤っていることを意味します。AADSTS700016は、ユーザー名とパスワードの両方が有効であるにもかかわらず、クライアントIDが認識されない場合に発生します。この最後のコードこそ、検知の仕組みを構築する価値があるものです。単体で見れば、管理者が消し忘れたアプリ登録の問題のように見えます。しかし、見慣れない、常にローテーションされているクライアントIDと組み合わさると、攻撃者がすでに有効な認証情報の組み合わせを確認済みであり、アカウント乗っ取りまであと一歩という段階にいることを意味する可能性があります。
ROPCは、セキュリティチームが想定している以上に多くの環境に残存しています。Microsoftのガイダンスがその利用回避を推奨する以前に構築されたレガシースクリプトやCIパイプライン、サードパーティ製の連携機能の中に生き残っているのです。これはまさに、ブラウザを開く必要が一切ない開発者にとって最も抵抗の少ない経路だからです。この既存導入基盤の広さこそが、この手法が活動できる余地を与えています。攻撃者が悪用しているのは稀な設定ミスではなく、その中に紛れ込めるほど今なお一般的なフローなのです。

Sunil Gentyala
2つのキャンペーン、1つの検知ギャップ
UNK_pyreq2323。Proofpointが追跡したこのキャンペーンは、AWSインフラから実行され、実在するExchange Online用アプリIDの末尾の桁を変異させて70万を超えるバリエーションを生成し、それぞれを最大12アカウント程度にしか使わずに使い捨てていました。このキャンペーンは約4,000のテナントにまたがる100万人超のユーザーを標的とし、被害を受けたアカウントの約28パーセントがロックアウトに至りました。
UNK_OutFlareAZ。主にCloudflareを経由して実行されたこのキャンペーンは、変異を一切行わず、リクエストごとに新規のランダムなUUIDを生成していました。370万件の詐称IDを200万を超えるアカウントに対して使用し、2026年3月15日には標的ユーザー数が約72万人でピークに達しました。
Proofpoint自身の研究者たちも、これが単一のアクターによるものなのか、それとも独立した2つのクラスターがフォーラムでの議論や試行錯誤を経て同じ手口にたどり着いたものなのか、まだ確認できていないと述べています。この点は、Help Net Securityもこの研究に関する報道で取り上げています。しかし検知という観点では、その曖昧さは重要ではありません。どちらのキャンペーンも、アプリケーションごとの量的しきい値という同じ制御を、同じギャップ、すなわち誰もカーディナリティを監視していない、相関の取れていない使い捨ての識別子フィールドを突くことで突破しているからです。
なぜこれがキューの問題ではなくCISOの問題なのか
100万ユーザー規模のキャンペーンにおける28パーセントというロックアウト率は、誤差の範囲では済みません。ヘルプデスクのコストであり、生産性への打撃であり、さらにロックアウト前にAADSTS700016が発生していたアカウントそれぞれについては、攻撃者がすでに検証済みで別の場所でも使用しうる生きた認証情報を意味します。メールボックスへのアクセス、OAuth同意の付与、連携先SaaSへの横展開などです。ここでの検知ギャップは表面的なものではありません。検証済み認証情報のイベントを、まだ1回の認証試行にとどまっている段階で捕捉できるか、それとも3週間後にメールボックスの転送ルールや不正な送金依頼から発覚するかの違いを生むのです。だからこそ、これは脅威インテリジェンスのダイジェストの脚注で済ませるのではなく、ルールを組む価値があるのです。
検知ロジックの構築
実用的なルールは、単一のイベントを個別にスコアリングするのではなく、リクエストの発生順序を再構築します。Entra IDのサインインログ、あるいは同等のSIEM取り込みデータに対して、中核となるロジックは次のようになります。
SigninLogs | where TimeGenerated > ago(1h) | where ResultType in ("50034", "50126", "700016") or isempty(AppDisplayName) | summarize DistinctClientIDs = dcount(AppId), ResultCodes = make_set(ResultType), Usernames = make_set(UserPrincipalName) by SourceIPAddress, UserAgent, bin(TimeGenerated, 15m) | where DistinctClientIDs > 5 | where ResultCodes has "700016"
最も重要な2つの変数は、DistinctClientIDsとAADSTS700016の存在です。DistinctClientIDsが重要なのは、単一の送信元が多数の未登録アプリIDを次々と試すことこそ、アプリケーションごとのしきい値では見逃してしまう手がかりだからです。そして同じ時間枠内にAADSTS700016が存在することは、そのイベントを単なる設定ノイズから、進行中である可能性のある認証情報検証へと格上げします。さらに、同一送信元からの試行にまたがるアルファベット順や辞書順の進行といったユーザー名パターンの検知を組み合わせることで、OutFlareAZ型のワードリストパターンを特に捕捉できます。
DistinctClientIDsのしきい値は、本番環境で信頼して使う前に、自社のテナントのベースラインに合わせて調整してください。少数のテスト用アプリ登録に対してCIを実行している開発チームも、同じ形の小規模版パターンを生成しうる上、名前付きアプリケーションのみを対象範囲とする条件付きアクセスポリシーでは、そもそも意図したアプリケーションスコープに一致することのない捏造クライアントIDを捕捉できません。
このルールは、誰も金曜午後にチェックしない独立ダッシュボードではなく、既存のSOARやチケッティングのワークフローに組み込んでください。パスワードスプレーや不可能な移動(impossible travel)のアラートと同じキューに発火する検知は、同じ緊急度でトリアージされます。一方、孤立したIDハイジーンレポートに埋もれる検知は、たとえ読まれるとしても数週間後になってしまいます。
シグナルとノイズの切り分け
アプリ名が空欄のイベントや700016のすべてが攻撃というわけではありません。削除されたアプリ登録、期限切れのマルチテナント同意、誤ったテナントへのエンドポイント呼び出し、誤ったクライアントIDを指すCIパイプラインなども、似たようなノイズを生み出します。2つのフィルターを適用することで、誤検知率を大幅に減らせます。正当な設定ミスは、ほぼ常に低いカーディナリティ、つまり数十件ではなく1件か2件のクライアントIDにとどまり、既知の社内インフラから繰り返し発生します。一方、攻撃パターンは高いカーディナリティを示し、見慣れないASNやホスティングプロバイダーのIPレンジから届きます。エスカレーションの前に既存のパスワードスプレーや不可能な移動のアラートと突き合わせることで、期限切れのサービスプリンシパルのためにアナリストを呼び出す事態を避けられます。また、このルールの最初の数週間における精度、つまり確定インシデント数と総発火数の比率を追跡し、それを確認してから本番グレードとして扱う価値があります。トリアージ基準はルール自体と併せて文書化しておきましょう。次のシフトのアナリストが、慎重を期してエスカレーションするのではなく、自信を持ってチケットをクローズできるよう、同じ判断根拠を共有する必要があるからです。
対応手順書
相関の取れたシグナルが発火した際の手順書は、インシデント発生時にその場で考えるのではなく、事前に短くまとめてリハーサルしておくべきです。該当アカウントに対してパスワードのリセットを強制し、アクティブなセッションとリフレッシュトークンを失効させ、AADSTS700016が発生した後の数日間のサインインおよびメールボックスアクセスのアクティビティをレビューしてください。確認済みの認証情報が別の場所で使われるのは、まさにその期間だからです。
ROPCを完全に廃止し、対話型アプリをブラウザベースの認証へ、サービスワークロードをマネージドIDまたは証明書ベースのサービスプリンシパルへ移行すれば、この手法が依存するフロー自体を取り除けます。その移行が完了するまでは、上記の検知ロジックこそが、単なる設定ノイズとしてクローズされるチケットと、捕捉されたアカウント乗っ取りの試みとを分ける唯一の砦となります。
検知エンジニアへの教訓
ここから得られる教訓は、OAuthに限った話ではありません。防御側がログには残しているものの相関させていないフィールド、アプリケーションID、ユーザーエージェント、ASN、あるいは今後登場する何であれ、それらはすべて、フィールドごとのしきい値が反応しなくなるまで攻撃者がトラフィックを分散させる際の標的候補となります。
誰もボリュームではなくカーディナリティを監視していないとき、そのギャップが大規模に現れた姿こそが400万件の偽アプリケーションです。そしてこれは、MITRE ATT&CKのValid Accounts: Cloud Accounts、T1078.004、すなわち技術的には有効な認証情報を使用する攻撃者という、根底にある同じ手口にほかなりません。この対策は、次にどのフィールドが悪用されるかに関わらず一般化できます。単一のフィールドが静的なしきい値を超えたかどうかではなく、複数のフィールドと時間枠にまたがる行動シーケンスを軸に検知を組み立てることです。