
背景と公開されている調査報告
Google Cloud Platform(GCP)の Cloud Build は、Google が提供する CI/CD(継続的インテグレーション/継続的デプロイ)サービスであり、アプリケーションのビルド、テスト、展開を自動化するために利用されています。Orca Security 社が、このサービスを利用した攻撃の対象領域について説明した記事を公開しました。同社が「Bad.Build」と名付けたサプライチェーン攻撃の手法などを取り上げています。指摘された具体的な問題の 1 つは、デフォルトのサービスアカウント(SA)を使った Cloud Build パイプラインが、GCP プロジェクト内の他のすべての権限割り当てを特定するために利用される恐れがあるというものでした。この問題は、Orca Security の報告後、Google により解消されています。しかし、悪意のある操作を実行するために Cloud Build パイプラインを利用する手法は、一般的な脅威ベクトルとして今も存在します。Cloud Build ジョブを送信して実行する権限を持つ攻撃者(つまり、cloudbuild.builds.create 権限を持つ攻撃者)は、Cloud Build SA に実行権限が割り当てられている任意の gcloud GCP CLI コマンドを実行できます。この手法を実行するには、GCP ユーザーまたは SA のログイン情報を取得するか(Mitre ATT&CK T1078.004
)、Cloud Build パイプラインを使用するように設定されているリポジトリにコードをプッシュまたはマージ(Mitre T1195.002
)します。Orca Security は、この脅威ベクトルを利用して、GCP Artifact Registry でホストされている標的アプリケーションに悪意のあるコードを追加し、サプライチェーン攻撃を実行するという手法を重点的に調査していました。同社の調査は実に包括的なものだったため、Talos はこの手法について広範な調査は実施しませんでしたが、今も同じ手法が使えることを確認しました。
独自調査
Orca Security の調査では、artifactregistry.* 権限を使うことで可能になるサプライチェーン攻撃について詳細に分析されていました。一方、以下で詳しく説明する Cisco Talos の調査では、storage.* 権限ファミリを使うことで可能となる悪意のある操作について重点的に分析しています。デフォルトの権限がリスクをもたらすというだけでなく、Cloud Build SA に追加された権限は、どのような権限であれ、この実行ベクトルにアクセスできる攻撃者によって利用される恐れがあります。この問題については、今後の調査対象となるでしょう。Orca Security の記事では、コードリポジトリのマージイベントによって Cloud Build ジョブがトリガーされる可能性があると指摘されていますが、同社が調査で実際に行った内容は、gcloud CLI ツールを使用して悪意のあるビルドジョブをトリガーするというものでした。一方 Talos の調査では、Cloud Build ジョブをトリガーするように設定した GitHub リポジトリへのコミットを使っています。その理由は、これが GCP アカウントのアイデンティティ プリンシパルにアクセスすることなく GCP アカウントを標的にできる初期アクセス手段の 1 つだと考えたからです。
Orca Security の調査報告とは異なり、Talos は、以下の調査で明らかになった攻撃経路が脆弱性や設計不良のサービスを示しているとは判断していません。Talos が悪意のある意図で利用した機能とデフォルト権限のすべてに、正当なビジネスユースケースが存在します。また、Google もセキュリティに関する確かな推奨事項とデフォルト設定を提供しています。クラウド管理者はむしろ、今回の調査結果を、こうした機能によってもたらされるリスクに関する教訓と捉えるべきであり、不正利用された機能が特定のアカウントに不要な場合は一部を制限するという選択肢もあります。セキュリティアナリストと調査員も今回の調査結果を指針として、特定された操作ログイベントをモニタリングしたり、不正利用を検出したり、インシデント発生後のインシデント対応ワークフローで分析したりするとよいでしょう。
防御のための推奨事項の概要
Talos が推奨するのは、異常モデルを使用した脅威検出を作成することです。これにより、特定の環境でデフォルトの Cloud Build SA が実行するのは一般的ではない操作が実行された場合に検出することができます。いつも提案していることですが、最小権限の原則を適切に適用することも重要です。Cloud Build に、特定の環境で必要となる権限だけを付与した低権限のサービスアカウントを割り当てれば、今回特定された攻撃対象領域を減らすことにつながります。最後に、リポジトリに適用されている、Cloud Build やその他の CI/CD サービスのジョブをトリガーする設定を見直すのもよいでしょう。プル要求(PR)によってトリガーされるビルドには手動承認を要求し、PR なしで GitHub リポジトリにコードを直接コミットすることを許可しないように設定できます。以上 3 つの推奨事項の詳細については後述します。
ラボ環境のセットアップ
Talos は、攻撃者の視点を取り入れてセキュリティ調査を行うために、Google Cloud Platform のラボ環境を持っています。このラボ環境で、Cloud Storage のバケットを作成しました。また、Cloud Build のアプリケーション プログラミング インターフェイス(API)を有効にしました。さらに、標的とする GitHub リポジトリも作成しました。このリポジトリは、調査目的のため非公開に設定しましたが、実際の攻撃者は、ほとんどの場合、公開設定したリポジトリを利用するでしょう。Cloud Build と GitHub を連携させるには Secrets Manager API も必要なので、これも有効にしました。以上の操作は、gcloud と GitHub の gh CLI ツールで以下のコマンドを使って実行できます。
gcloud storage buckets create BUCKET_NAME --location=LOCATION --project=PROJECT_ID gcloud services enable cloudbuild.googleapis.com --project=PROJECT_ID\ gcloud services enable secretmanager.googleapis.com --project=PROJECT_ID gh repo create REPO_NAME --private --description "Your repository description"
次に、実際のストレージバケットにデータが入力されるのをシミュレートします。短いシェルスクリプトを実行して、ランダムなデータを含む 100 個のテキストファイルを作成し、新しい Cloud Storage バケットに転送しました。
#!/bin/bash # Set your bucket name here BUCKET_NAME="data-destruction-research" # Create a directory for the files mkdir -p random_data_files # Generate 500 files with 10MB of random data for i in {1..100} do FILE_NAME="random_data_files/file_$i.txt" # Use /dev/urandom to generate random data and `head` to limit to 10MB head -c $((10*1024*1024)) </dev/urandom > "$FILE_NAME" echo "Generated $FILE_NAME" done # Upload the files to the GCP bucket for FILE in random_data_files/* do gsutil cp "$FILE" "gs://$BUCKET_NAME/" echo "Uploaded $FILE to gs://$BUCKET_NAME/" done
次に、GitHub リポジトリと Cloud Build を連携させるため、以下のコマンドを使って 2 つのリソース間の接続を作成します。その後、GitHub.com 側で認証とアクセス許可を行います。
gcloud builds connections create github CONNECTION_NAME --region=REGION
最後に、Cloud Build の「トリガー」を設定しました。このトリガーは、コードがメインブランチにプッシュされたり、プル要求(PR)が作成されたり、標的とする GitHub リポジトリで既存のプル要求にコードがコミットされたりしたときに、ビルドを開始するというものです。この設定には、以下の gcloud コマンドを使います。
gcloud builds triggers create github \–name=TRIGGER_NAME \
–repository=projects/PROJECT_ID/locations/us-west1/connections/data-destruction/repositories/REPO_NAME \
–branch-pattern=BRANCH_PATTERN # or –tag-pattern=TAG_PATTERN \
–build-config=BUILD_CONFIG_FILE \
–region=us-west1
防御上の注意
Google のドキュメントでは、PR の作成や PR へのコミットをトリガー条件として使用する場合の注意事項として、ビルドのトリガーには手動の承認を必要とすることを推奨しています。リポジトリを読むことができるユーザーなら誰でも PR を送信できるからです。この優れたアドバイスには、できる限り従うべきです。また、レビュー担当者は、Cloud Build によってもたらされる攻撃対象領域について認識しておく必要があります。ただ、今回の調査は潜在的な脅威ベクトルを明らかにするのが目的のため、Talos のラボ環境ではこのアドバイスには従わず、手動承認は設定しませんでした。また、メインブランチに PR がマージされることをきっかけとしてビルドがトリガーされることもあります。PR のマージ前に PR レビューの承認を入れるべきなのは、リポジトリの完全性を保護するためでもありますが、このリスクに対処するためでもあります。
正当なビジネスケースとして、PR が作成されたときに自動でビルドイベントを実行できるようにするというシナリオが、実際に存在する可能性もあります。だからこそ、適切な多層防御戦略の一環として、Cloud Build に割り当てられたあらゆるサービスアカウントが実行するイベントをモニタリングすべきです。Google は、GitHub リポジトリのオーナー、コラボレーターロールを持つユーザー、任意のコントリビューターのいずれかが「/gcbrun」という文字列を含むコメントを PR に入力しないと Cloud Build の実行をトリガーできないようにする機能も提供しています。これも強力なセキュリティ機能であり、可能であればこのオーナーまたはコラボレーターのコメントを要求するオプションを選択して設定することをお勧めします。ペネトレーションテストやレッドチーム演習で、GitHub PR を使って GCP を標的とする攻撃を試してみる場合は、悪意のある PR に「/gcbrun」とコメントを入力するとよいかもしれません。この権限が任意のコントリビューターに許可されていて、Cloud Build をトリガーできるようになっている可能性があります。
調査と推奨事項
データの破壊(Mitre ATT&CK T1485)
Talos は以前、GCP に焦点を当てたパープルチーム演習で、GCP の Cloud Storage 内でのデータ破壊による被害に対応したことがあります。今回の調査では、このときの調査を発展させ、GitHub から Cloud Build への実行パスを利用しました。最初に実行した具体的な操作は、Cloud Storage バケットの削除です。以下の gcloud コマンドを使います。
gcloud storage rm --recursive gs://BUCKET_NAME
Cloud Build パイプラインを使って操作を実行するために、Orca Security のシンプルなコンセプト実証(PoC)例の Cloud Build 構成ファイル(これは Google のドキュメントに記載されている例がベースになっていました)を、以下のように少し修正しました。
- name: 'gcr.io/cloud-builders/gcloud' args: ['storage', 'rm', '--recursive', 'gs://BUCKET_NAME']
この YAML ファイルを GitHub のブランチにコミットし、PR を作成します。以下が、そのために使うコマンドです。
git clone <repo URL> cd <repo name> cp ../totally-not-data-destruction.yaml cloudbuild.yaml git add cloudbuild.yaml git commit -m "Not going to do anything bad at all" git push gh pr create
Orca Security の調査では、Cloud Build のランタイムログ用に Google Cloud Storage(GCS)のバケットが必要でした。攻撃者は通常、フォレンジック アーティファクトを残したくないと考えるので、自分が管理している別の GCP アカウントの GCS バケットを指定するでしょう。つまり、アカウント内のすべてのストレージバケットを把握できていると仮定した場合、Cloud Build イベントで外部の GCS バケットが使用されているか調べれば、攻撃を検出できる可能性があります。しかし、GitHub などのリポジトリのトリガーを使って Cloud Build ジョブを実行する場合、ログを格納する GCS バケットを指定する必要はありません。
GCP は、「削除(復元可能)」を設定する機能を提供しています。これは、誤ってまたは悪意を持って削除された GCS バケットとオブジェクトを復元できるようにする機能であり、復元可能とする期間を設定することができます。強力なセキュリティ機能なので、できる限り有効化することをお勧めします。ただし、注意も必要です。バケットが削除されると同じ名前が再度使えるようになりますが、同名の新しいバケットの作成中に何らかの要求が行われた場合、削除された GCS バケットの復元は不可能になります。これは、Google の公式ドキュメントにも記載されています。つまり、バケットを削除した後、同じ名前の新しいバケットを直ちに作成すれば、GCS バケット内のデータを完全に破壊できるということです。
Cloud Build の構成ファイルを以下のように更新すれば、データを完全に破壊できます。
steps: - name: 'gcr.io/cloud-builders/gcloud' args: ['storage', 'rm', '--recursive', 'gs://BUCKET_NAME'] args: ['storage', 'buckets', 'create', 'gs://BUCKET_NAME', '--location=BUCKET_LOCATION']
防御上の注意
ログイベント
ここまでで説明してきたすべてのイベントは、Google の操作ログに記録が残ります。調査中に確認された操作ログイベントは以下のとおりです。
- google.devtools.cloudbuild.v1.CloudBuild.RunBuildTrigger
- 前述した GitHub のプル要求でトリガーする手法のような連携トリガーによって新しいビルドが作成されたことを記録するイベントです。これは、DFIR(デジタルフォレンジックとインシデント対応)調査員にとっては、調査の一環として非常に有用ですが、脅威検出の根拠にはならないでしょう。
- google.devtools.cloudbuild.v1.CloudBuild.CreateBuild
- 新しいビルドを手動で作成したことを記録するイベントであり、どのアイデンティティ プリンシパルがイベントをトリガーしたかがわかります。ビルドが手動でトリガーされ、なおかつビルドログの保存先として GCS バケットが指定されている場合、バケット名は「protoPayload.request.build.logsBucket=”gs://gcb_testing”」フィールドに指定されています。このフィールドが存在する場合、既知のインフラストラクチャの外部に存在する未知のバケットに対する脅威検出機能や脅威ハンティング用クエリが有用でしょう。また、たいていのクラウド監査ログイベントの場合と同様に、CreateBuild イベントが何度も失敗した後に成功したイベントがある場合、攻撃者が新しい機能の発見や権限の昇格を試みていた可能性があります。そうでなければ、このイベントは完全に正当なものなので、RunBuildTrigger と同じく、脅威の検出よりも主に DFIR 調査にとって有用な情報となります。
- storage.buckets.delete
- この methodName 値を持つイベントは、GCS バケットの削除を記録するものです。これは、データ破壊を含む DFIR の調査では自ずと注目されるイベントです。protoPayload.authenticationInfo.principalEmail の値がデフォルトの Cloud Build サービスアカウントとなっている場合、脅威ハンティングを行った方がよいかもしれません。Cloud Build が GCS バケットを使用して一時データを保存し、ビルド完了後に削除するのは正当な操作である場合もあるので、このイベントを脅威とみなして自動的に検出すべきというわけではありません。ただし、異常モデルを使用した検出の対象としては適していると言えるでしょう。
- storage.buckets.create
- 単体ではそれほど注目度が高いイベントではありませんが、前述したように、storage.buckets.delete イベントの直後に発生した場合は「削除(復元可能)」機能による保護をバイパスしようとする試みがあったことを示唆している可能性があります。これは自動的に検出するだけの価値があるかもしれません。脅威ハンティングや DFIR の調査クエリとして、間違いなく有用です。
データ暗号化による被害(Mitre ATT&CK T1486)
クラウド オブジェクト ストレージは、その性質上、ランサムウェアと無縁ではいられません。しかし、「ランサムクラウド」攻撃や他の類似した脅威ベクトルに利用される懸念はあるものの、クラウドストレージバケット内のオブジェクトを不可逆的に暗号化することは実際には非常に困難です。データを標的とするクラウドインパクト攻撃の場合、オブジェクトを暗号化するよりも、オブジェクトを漏洩させて削除したうえで、身代金と引き換えにオブジェクトを返す形を取る可能性の方がはるかに高いと言えます。Google Cloud Storage では、すべてのオブジェクトが暗号化されます。デフォルトでは Google が管理する暗号化鍵が使われますが、他にも 3 種類のオブジェクト暗号化手法がサポートされています。具体的には、Cloud Key Management Service(CKMS)を使用して鍵を管理するサーバーサイドの暗号化(カスタマーマネージド暗号化とも呼ばれる)、クラウドアカウントには一切保存されない顧客提供の鍵を使用するサーバーサイドの暗号化、クライアントサイドでのオブジェクト暗号化の 3 つです。攻撃者がカスタマーマネージド暗号化を使用した場合、オブジェクトの正規のオーナーは CKMS にアクセスしてオブジェクトを復号できます。顧客提供の暗号化鍵が使用された場合や、クライアントサイドで暗号化された場合は、顧客は元のバージョンのオブジェクトを上書きできるでしょう。ただし、バケットでオブジェクトのバージョン管理が有効化されている必要があります。この場合、管理者は単に以前のバージョンのオブジェクトに戻すだけで復旧できます。
攻撃者がオブジェクトのバージョン管理設定が有効化されていないバケットを特定できれば、前述した Cloud Build を使った攻撃手法により、自分たちが管理する顧客提供の鍵を使って既存オブジェクトを暗号化することも可能かもしれません。その場合は、以下の gcloud CLI コマンドが使用されます。
gcloud storage cp SOURCE_DATA gs://BUCKET_NAME/OBJECT_NAME --encryption-key=YOUR_ENCRYPTION_KEY
これは、前述した cloudbuild.yaml ファイルをバケット内の 10 個のオブジェクトのエントリで更新したうえで、別のビルドをトリガーするというやり方で実行しました。実際の攻撃では、保存されているオブジェクトを列挙したうえで、すべてのオブジェクトを暗号化する必要があります。
防御上の注意
新しい暗号化ファイルの作成は、storage.objects.create タイプのイベントで記録されました。しかし、残念ながら、イベントの本文には、顧客提供の暗号化鍵が暗号化に利用されたことを示す証拠はありませんでした。このイベントに関して、検出機能や調査員が調査すべき特別な異常は何も残っていなかったということです。とはいえ、繰り返しになりますが、「オブジェクトのバージョン管理」と「削除(復元可能)」を有効化することでこの攻撃手法をすべて回避できるので、両機能を有効にすることを強くお勧めします。
本稿は 2025 年 2 月 06 日にTalos Group
のブログに投稿された「Google Cloud Platform Data Destruction via Cloud Build
」の抄訳です。