Netwrixセキュリティリサーチでは、Entra ID評価で顧客を支援しており、既にオンプレミスでADの階層化を実施していることを把握していました。また、Azureに仮想化されたドメインコントローラーがあることも知っていましたが、どのWindowsサーバーが実際のDCか判別が難しかったのです。Azure Run CommandがSYSTEM権限でコマンドを実行できることを突き止め、それを使って偵察しDCを特定しました。
この投稿に表示されているすべての内容(スクリーンショット、機械名、ユーザー名)は、技術を示すために後で再構築したラボ環境のものです。顧客データは含まれていません。
参考までに、Microsoftの公式ドキュメントでは、Run CommandをVMエージェントを通じてWindows VM内でPowerShellスクリプトを実行する方法として説明しており、主にアクセスやネットワークの問題を修正するなどの一般的な管理作業に使われます。ただし、どの種類のVMかは確認しません。ドメインコントローラーでも他のVMと同様に問題なく動作します。
Claudeと一緒に、複数のWindowsマシンに一度にコマンドを実行できるPowerShellスクリプトを作成することにしました。Run CommandコンソールでVMを一つずつクリックする代わりに使えます。必要なのは、Virtual Machine Contributor以上に付与されるMicrosoft.Compute/virtualMachines/runCommand/actionの権限だけです。
コツは、通常のStart-Jobの代わりに各Invoke-AzVMRunCommand呼び出しをThreadJob内で実行することです。Start-JobはVMごとに新しいPowerShellプロセスを起動し、毎回Azモジュールを再読み込みするため、数台を超えると急速に遅くなります。ThreadJobは同じプロセス内のスレッドで動作するため、一度に多数を起動してもほとんど追加コストがかかりません。見つけたすべてのVMをキューに入れ、同時に実行する数を固定(デフォルト20)して管理します。1つが終了したらすぐに空きを補充し、バッチ全体の完了を待ちません。
ポータルを通じて1台ずつwhoamiを実行する様子は次の通りです:
こちらは、PowerShellスクリプトを使ってサブスクリプション内のすべてのVMに対して一度に実行される同じコマンドです:
これが動作したら、次のステップはDCを見つけることでした。チェック自体は簡単です:Run Commandを使って稼働中のすべてのWindows VMでGet-Service NTDSを実行します。NTDSはAD DSデータベースを動かすサービスなので、存在して稼働していれば、そのマシンはドメインコントローラーです。
1台のVMは明らかに稼働していましたが、Run Commandは応答せずにエラーが発生しました。他のすべてのVMは正常に応答していました。名前だけでも強いヒントでした。Run Commandの失敗は、適切にロックダウンされたドメインコントローラーで、より厳しいセキュリティツールが妨げになっている状況にまさに合致します。そのエラーをそのまま受け入れて進んでいたら、サブスクリプション内にドメインコントローラーがゼロと報告することになっていたでしょう。
エラーを信用する代わりに、直接ADに問い合わせました。Run Commandがまだアクセスできるドメイン参加済みのVMは、LDAP経由でADを照会するために使われます。Active Directoryモジュールは不要で、ドメイン参加済みのどのマシンでも動作する組み込みのPowerShellタイプアクセラレータ[adsisearcher]だけです。
このチェックはSERVER_TRUST_ACCOUNTというuserAccountControlビットで、値は8192です。ADはコンピューターがDCに昇格したときにのみこれを設定します。さらに2つのチェックを追加しました:プライマリグループ516(「Domain Controllers」グループ)と、構成パーティション内の対応するNTDS設定オブジェクトです。
ただし一つ注意点があります。ホスト名が一致するだけでは、このVMであることやAzure内にあることの証明にはなりません。名前は変わったり切り詰められたり、時には再利用されたりします。そこでまずIPを確認します。ドメイン内からDCのホスト名をIPに解決し、そのIPをAzureがそのVMに報告するプライベートIPと比較します。そのIPはマシンが本当にAzure上で動作している場合にのみ存在するため、ここでの一致は非常に確実です。DNSが解決しない場合はホスト名の比較に切り替えますが、名前はIPほど安定しないため、こちらは弱い証拠と見なします。
以前にエラーが発生したマシンがドメインコントローラーであることを確実に確認できた後、本当の問題は誰がSYSTEMとして任意のコマンドを実行できるかでした。この種のアクセスはTier-0だからです。Domain Adminを持つ人ではありません。顧客はすでに階層構造でそれを確認済みです。特定のVMに対するAzureの権限を持つ人であり、Run CommandはADの内容を気にせず、Azure RBACのみを重視します。
これがその様子です。複数回表示されるユーザーが数名います(役割ごとに1行)、グループと2つのサービスプリンシパルも含まれ、すべてのチェーンにOwnerまたはContributorがあり、そのほとんどはVMに直接割り当てられたのではなくサブスクリプションから継承されています。彼ら全員がドメインコントローラーに対してRun Commandを呼び出し、SYSTEMとしてコードを実行できます。これらはオンプレミスのDomain Adminsに実際に誰がいるかとは関係ありません。
この時点で、スクリプトはこのデータからBloodHoundスタイルのグラフを生成し、どのセキュリティプリンシパルがAzureの仮想化ドメインコントローラーへの直接または間接の経路を持つかを可視化するかどうかを尋ねます。私たちのラボでは、DCへの経路を持つプリンシパルは12件で、その多くはVMに直接割り当てられたのではなくサブスクリプションから継承されています。任意のノードをクリックすると、サイドバーにその内容と到達経路が詳細に表示され、グループメンバーシップも含まれるため、「3メンバー」と表示されるグループノードは単なる数ではありません。実際に誰が含まれているかを見ることができます。
おすすめ
Azureのドメインコントローラーに、VMリソース上でTier-0-OnPrem-ADのようなタグを付けてください。明白に思えますが、私たちが見た多くの環境では行われておらず、タグでフィルターするだけでなく識別に実際の作業が必要でした。一度タグ付けすれば、そのタグのついたものに対して新しい所有者や貢献者の割り当てを拒否したり、変更時に通知したりするポリシーを作成できます。
タグ付けは今後何が起こるかを示すだけで、既にある問題を修正するものではありません。私たちが調査したすべての環境では、これらのVMに対して時間とともに権限が蓄積されており、その多くはリソースグループやサブスクリプションから継承されたもので、直接割り当てられたものではありません。だからこそ見落としやすいのです。現在アクセス権を持っている人を確認し、各ユーザーおよびサービスプリンシパルに対して、ドメインコントローラーであるマシンに対してまだオーナーまたはコントリビューターの権限が必要かどうかを尋ねてください。
設定が完了すれば、フィルターは階層列を追加するか、フィルターバーにTier-0-OnPrem-ADと入力するだけで、すべてのDCが表示されます。スクリプトは不要です。かつては全列挙パイプラインが必要だったことが、今ではチームの誰でも2クリックでフィルターできます。
参考文献
- Microsoft Learn: AzureのWindows VMでRun Commandsアクションを使ってスクリプトを実行する
https://learn.microsoft.com/en-us/azure/virtual-machines/windows/run-command - Microsoft Learn: 仮想マシン向け Azure インスタンス メタデータ サービス
https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service
共有する
もっと詳しく
著者について
Huy Kha
セキュリティリサーチ部門ディレクター
HuyはNetwrixのセキュリティリサーチ部門ディレクターであり、セキュリティリサーチチームを率いています。また、顧客のレジリエンス向上を支援するために、セキュリティ製品ポートフォリオ全体の改善を推進しています。さらに、彼はWindows & Devices分野のMicrosoft MVPでもあります。インシデントレスポンス、セキュリティオペレーション、システム最適化の経験を背景に、複雑な課題を明確で効率的なプロセスへと変える、実用的で再現可能なアプローチに注力しています。