ゴースト・イン・ザ・スクリプト:Google App Script プロジェクトになりすましてステルスパーシスタンスを行う

クリプトマイニングからステルスサービスアカウントまで、Google Apps Script の悪用のリスクと、不正使用を検出する方法について解説します。

Bleon Proko

Bleon Proko

Jakub Pavlik

Jakub Pavlik

インフラ攻撃における最も重要なステップの一つは、標的環境内で永続化を確立することです。永続化は、攻撃者が獲得したアクセスレベルに応じて、複数回確立する必要がある場合もあります。セキュリティシステムは、さまざまな永続化メカニズムを検出できるほど高度化しています。その結果、攻撃者は標的環境内にとどまり続けるための、新たで巧妙な方法を探し続けています。

Google Workspace Apps Script は、Gmail アカウントを持つあらゆるユーザーが業務アプリケーションを自動化し、それらのアプリケーション同士を連携させられる機能です。その内部では、Apps Script アプリがデプロイされると、そのアカウントが属する GCP 組織上に GCP プロジェクトが作成されます。これらのプロジェクトは、プロジェクト ID の形式を除けば、通常の GCP プロジェクトと大きくは変わりません。つまり、攻撃者はこれらのプロジェクトの一つを使ってリソースをホストし、標的環境内で永続化を確立できます。また、Apps Script プロジェクトと同じ命名形式の GCP プロジェクトを作成し、正規の Apps Script プロジェクトになりすまして検出を回避することもできます。

本ブログでは、Apps Script プロジェクトの仕組みと、攻撃者が Apps Script プロジェクトをどのように利用して標的環境内で永続化を行うのかを解説します。続いて、こうした手法をどのように検出・防止し、攻撃者による悪用を防ぐかを見ていきます。

Apps Script の仕組み

エンドポイント script.google.com に関連付けられた Google Workspace Apps Script は、Gmail アカウントを持つあらゆるユーザーが、Google Workspace と連携する業務アプリケーションを自動化できるローコードソリューションです。JavaScript を使用するスクリプトインターフェースを備えており、Google の各種サービスを連携させて、軽量な自動化を構築できます。

Google Apps Script editor showing “myFunction” in Code.gs of an untitled project.
Google Apps Script の例

Apps Script は非常に柔軟です。Apps Script を使うと、次のことが可能です。

  • Google ドキュメント、スプレッドシート、フォームにカスタムメニュー、ダイアログ、サイドバーを作成する
  • Google スプレッドシート向けのカスタム関数やマクロを開発する
  • Web アプリをスタンドアロンアプリケーションとして、または Google Sites に埋め込んで公開する
  • AdSense、Analytics、Calendar、Drive、Gmail、Maps などの Google サービスと連携する
  • 軽量なアドオンを構築し、Google Workspace Marketplace を通じて共有する
New deployment settings in Google Apps Script with Web app selected from configuration menu.
利用可能な Apps Script の種類

Apps Script が作成されると、接頭辞 sys- を持つプロジェクトが自動的に作成されます。これらのプロジェクトは、コンソール上の組織のプロジェクト一覧には表示されません。これらは組織プロジェクトとは見なされないため、この挙動は理にかなっています。

Google Cloud Console “Select a resource” dialog showing Exaforce organization and project list.
Apps Script プロジェクトが含まれていないプロジェクト一覧。

ただし、resourcemanager.projects.list を実行する権限を持つ ID であれば、ターミナルツール(gcloud)からこれらのプロジェクトを確認できます。

Terminal showing gcloud projects list output with project IDs and names.
gcloud projects list that does contain Apps Script projects

Apps Script プロジェクトが作成されると、GCP はデフォルトで組織内に system-gsuite/apps-script という名前の Resource Manager フォルダとサブフォルダを作成します。ここでも、コンソール上ではこれらのフォルダ内にプロジェクトが存在しないように見えます。

Google Cloud Console resources table showing Exaforce organization and projects.
プロジェクトが表示されない Apps Script サブフォルダのコンソール表示

しかし CLI を使用すると、apps-script サブフォルダ内に Apps Script プロジェクトが存在していることが分かります。Apps Script プロジェクトは作成後、ここに格納されます。

Terminal filtering projects by folder ID using gcloud projects list --filter.
system-gsuite/apps-script サブフォルダ内の Apps Script プロジェクトを示す CLI 出力

GCP 組織における Apps Script なりすましの悪用

クリプトマイニング用インスタンス

Apps Script プロジェクトは、sys-<26 numbers> という ID 形式に従います。GCP では、アクセス権のある任意のフォルダまたはサブフォルダ内にプロジェクトを作成して格納でき、プロジェクト名も、ASCII 文字・数字・ハイフンのみを含み、6~30 文字であれば自由に設定できます。sys-<26 numbers> の組み合わせはちょうど 30 文字で、数字・文字・ハイフンを含んでいます。

Terminal output showing successful creation of new GCP projects using gcloud projects create.
Apps Script プロジェクトに見える GCP プロジェクトの作成

私たちが確認した違いの一つは、プロジェクトの保存場所によって見え方が変わることでした。プロジェクトが組織レベルに保存されている場合、sys-<26 numbers> 形式の ID を持っていても、コンソール上に表示されます(プロジェクト sys-00000000000000000000000000)。一方、apps-script フォルダ内に作成された場合、そのアプリはコンソール上でプロジェクトとして表示されません(プロジェクト sys-11111111111111111111111111)。

Resources table in Google Cloud Console showing organization, projects, and folders.
Console view of projects where sys-00000000000000000000000000 is shown due to being in the organization level, but sys-11111111111111111111111111 in the apps-script folder is not

それでも、resourcemanager.projects.list をターミナルで実行すると、これらのプロジェクトは一覧に表示されます。

Terminal output listing GCP projects with IDs, names, and project numbers.
gcloud CLI listing both projects

resourcemanager.projects.create 権限を持つ攻撃者は、Apps Script プロジェクトが通常のプロジェクトのようには表示されない点を悪用し、標的の組織内にプロジェクトを作成して、その中にリソースを格納できます。各プロジェクトには、作成者が任意の名前を付けることもできます。攻撃者は標的組織内の他のプロジェクトを確認し、そのプロジェクトにもっともらしい名前を付けることも可能です。

gcloud command creating a new project named ‘Exaforce Google Sheet Function’.
gcloud CLI used to create a hidden project

たとえば、攻撃者はこの隠しプロジェクトを利用して高性能なインスタンスを作成し、クリプトマイニング基盤として悪用できます。そのためには、次の手順が必要です。

  • プロジェクトの課金を有効にする
  • Compute API を有効にする
  • インスタンスを作成する
Terminal showing GCP billing link and VM instance creation with gcloud compute.
隠しプロジェクトで課金を有効化し、大規模インスタンスを作成する

これにより、攻撃者はクリプトマイニング用ハーベスタとして使用できる高性能インスタンスを掌握できます。

隠しプロジェクト内のサービスアカウントを利用して組織内で永続化する

永続化によって、攻撃者は標的のインフラに再侵入できます。理想的には、高権限 ID として戻ることを狙います。GCP 組織で永続化を行う方法には、ユーザーの作成、サービスアカウントの作成、永続的な認証情報の作成、高権限 ID が割り当てられたリソースの作成などがあります。ある永続化メカニズムをプロジェクト内に作成できるのであれば、Apps Script を装ったプロジェクト内にも作成できます。たとえば、サービスアカウントを作成し、そのキーを生成し、組織や他のプロジェクトに対して高権限ロールを付与して、後で使用できるよう保持しておくことができます。

gcloud IAM policy binding output showing roles assigned to Exaforce service accounts.
隠しプロジェクトでサービスアカウントを作成する

さらに厄介なのは、プロジェクト名が分かっている場合にしか、その ID が一覧に表示されないことです。さらに、誰もそのサービスアカウントにアクセスできないようにするポリシーをプロジェクトに設定することもできます。これは「絶対に破れない」防御ではありませんが、特にこれらのプロジェクトが Google によって作成・管理されているように見えるため、サービスアカウントをクリーンアップしようとする一部の試みを妨げる可能性があります。

name: organizations/ORG_ID/denyPolicies/deny-service-account-all
displayName: "Restrict all SA usage"
rules:
- denyRule:    
    deniedPrincipals:
    - principalSet://goog/public:all    
    deniedPermissions:    
    - iam.serviceAccounts.*

なぜ Apps Script プロジェクトになりすますのか

Apps Script プロジェクトは、その実体としては通常の GCP プロジェクトです。通常のプロジェクトと異なるのは、デフォルトでは Google が管理する 1 つの ID を除き、どの ID にも適切なアクセス権が付与されていない点です。サービスアカウント appsdev-apps-dev-script-auth@system.gserviceaccount.com は Apps Script プロジェクトを作成する共通 ID であり、Google のみが管理しています。デフォルトで Apps Script プロジェクトとそのリソースを管理できるのは、この ID だけです。

Terminal output showing gcloud IAM policy for project with service account owner role.
デフォルトでは Google 管理のアカウントのみが正規の Apps Script プロジェクトにアクセスできることを示す例

適切な権限を持つ攻撃者は、プロジェクトの IAM ポリシーを変更し、このプロジェクト上で任意のサービスの任意のリソースをホストできるよう、自身に権限を付与できます。

gcloud command adding admin@exaforce.cloud  as owner and verifying IAM policy update.
正規の Apps Script プロジェクトを変更するための攻撃者アクセスを許可するようプロジェクトポリシーを更新する

たとえば、攻撃者は次のようなことを行えます。

  • サービスアカウントを作成し、標的組織内で永続化できるよう、その ID に組織レベルの高権限ロールを付与する
  • プロジェクトを請求先アカウントにリンクし、クリプトマイニング用の大規模リソースを作成する

Apps Script プロジェクトの悪用を検出する

プロジェクトの課金情報から、プロジェクトのなりすましを見つける

GCP では、リソースごとに異なる課金形態があり、中には制限なく無料で使えるものもあります。IAM ID はその代表例です。その他の無料リソースの例として、IAM Resource Manager Organizations、Artifact Registry、VPC の基本ネットワーキングなどがあります。

特定のサービスとそのリソースを利用するには、組織のオーナーがプロジェクトを請求先アカウントにリンクする必要があります。プロジェクトを請求先アカウントにリンクするとは、平たく言えば、そのプロジェクトに支払い方法を設定することです。請求先アカウントは支払い方法をプロジェクトに関連付け、毎月、リソース使用量に応じてプロジェクトのオーナーに請求が行われます。

大規模な Compute インスタンスを作成できるということは、そのプロジェクトに請求先アカウントがリンクされている必要があるということです。攻撃者によって作成され、大規模リソースのホストとして使用されている Apps Script プロジェクトを検出する一つの方法は、そのプロジェクトが請求先アカウントにリンクされているかを確認することです。以下の例では、大規模な Compute インスタンスを含む攻撃者作成プロジェクト sys-22222222222222222222222222 には、請求先アカウントがリンクされています(billingAccountName および billingEnabled フィールドで確認できます)。一方、正規の Apps Script プロジェクト sys-14600875379148140018929136 には、請求先アカウントへのリンクは不要です。

Terminal shows billing account linked for one project but missing for another, indicating disabled billing.
請求先アカウントに紐付けられた隠しプロジェクトと、紐付けられていない正規の Apps Script プロジェクトの比較

課金情報を変更してプロジェクトを紛れ込ませる

この検出を回避する方法の一つは、プロジェクトと請求先アカウントのリンクを解除することです。攻撃者がインスタンスやその他の有料サービスを作成したい場合、そのプロジェクトは請求先アカウントに紐付いている必要があります。そのため、無料枠で提供されていないリソースやサービスは、請求先アカウントがリンクされたプロジェクト内にデプロイする必要があります。

プロジェクトと請求先アカウントのリンクを解除すると、そのリソースは削除されます。

請求先アカウントのリンク解除によりインスタンスが削除される

IAM サービスアカウントは有料リソースの範囲には含まれないため、請求先アカウントにリンクされていないプロジェクトでも、永続化に利用できるサービスアカウントを保持できます。つまり、この方法では前述の検出をすり抜けられる可能性があります。

Output showing disabled billing and two listed service accounts in the GCP project.
サービスアカウントを維持したまま請求先アカウントのリンクを解除する

有効化されている API サービスから Apps Script プロジェクトのなりすましを検出する

API 経由でプロジェクト上の特定のサービスを利用するには(gcloud もこれを使用します)、そのサービス API を有効にする必要があります。デフォルトでは、新規プロジェクトでは複数のサービスが有効になっており、serviceusage.services.enable または serviceusage.services.disable を使用して、さらに多くのサービスを有効化または無効化できます。

一方、Apps Script プロジェクトでは、デフォルトでは有効なサービスはありませんが、必要に応じて有効化できます。そのため、そのプロジェクトが本当に Apps Script プロジェクトなのかを検出する一つの方法は、有効化されているサービスを確認することです。

Comparison of enabled Google Cloud services lists for two different projects.
本物と偽の Apps Script プロジェクトにおける有効化サービスの一覧

API エンドポイントを無効化して紛れ込ませる

この検出手法の問題点の一つは、Apps Script プロジェクトでも、実行するタスクによっては一部のサービスを有効にする必要があることです。攻撃者は、使用していないサービスをすべて無効にし、プロジェクトをより正規のものらしく見せることができます。

Terminal loop disabling multiple Google Cloud services successfully for a project.
未使用のサービスを無効化し、正規の Apps Script プロジェクトに見せかける

たとえば、プロジェクト内で Compute インスタンスを作成するには、computeoslogin の各サービスを有効にする必要があります。リソースが存在している状態でこれらを無効化すると、実行は失敗し、そのプロジェクトが現在利用中であることが分かります。それ以外のサービスは無効化しておき、標的環境でタスクを実行するたびに、攻撃者が一時的に再有効化することもできます。

Error message when disabling a compute service that still has active resources.
サービスが無効化されている場合の実行エラー

もう一つの問題は、サービス API がプロジェクトで有効化されていなくても動作するサービス API があることです。そのため、IAM や一部の基本的なストレージコマンドに加え、Service Usage API(プロジェクト上でサービス API を有効化・無効化するための API)や Resource Manager も利用可能です。

  • IAM
  • Cloud Resource Manager
  • Service Usage API
  • Cloud Storage(基本機能は常時利用可能。課金を有効化すると、より詳細な制御、課金、メトリクスが利用可能)

つまり、有効なサービスが一つも表示されないプロジェクトでも、攻撃者が永続化メカニズムとして利用できるサービスアカウントをホストできるということです。

ログから Apps Script プロジェクトのなりすましを検出する

もう一つの検出方法は、apps-script フォルダのログを確認することです。以下の例では、攻撃者が作成したプロジェクト(sys-11111111111111111111111111 および sys-22222222222222222222222222)では、principalEmail フィールドに作成者のユーザーメールアドレスが設定されています。一方、Google が作成したプロジェクトでは、appsdev-apps-dev-script-auth@system.gserviceaccount.com が設定されています。注意が必要なのは、これらのイベントは組織レベルのイベントではなく、フォルダ配下のイベントにしか現れない点です。

Log entries showing multiple CreateProject actions by different service accounts in GCP.
偽の Apps Script プロジェクトと正規の Apps Script プロジェクトで principalEmail が異なることを示すログ

組織ポリシーを使用してプロジェクトなりすましを制限する

Apps Script プロジェクトが作成されると、その ID 形式は sys-<26 random digits> になります。Apps Script を装ったプロジェクトの作成を防ぐ方法の一つは、次のポリシーを使用して、sys-<26 random digits> 形式の ID を持つプロジェクトの作成・更新を拒否することです。

name: >-

organizations/012345678912/customConstraints/custom.denyAppsScriptProjectImpersonation
resource_types: cloudresourcemanager.googleapis.com/Project
method_types:
  - CREATE
  - UPDATE
condition: 'resource.projectId.matches(''sys-[0-9]{26}'')'
action_type: DENY
display_name: Deny Apps Script Project Impersonation
description: ''

この組織ポリシーは、sys-<26 random digits> 形式の ID を持つあらゆるプロジェクトの作成を拒否します。これには、script.google.com エンドポイントを通じて Google が正規に作成するプロジェクトも含まれます。

Error showing project creation denied due to organization policy constraints in GCP.
隠しプロジェクトの作成をブロックする組織ポリシー

この組織ポリシーは、sys-<26 random digits> 形式の ID を持つあらゆるプロジェクトの作成を拒否します。これには、script.google.com エンドポイントを通じて Google が正規に作成するプロジェクトも含まれます。

ブロックされた Apps Script プロジェクトの作成

エクサフォースによる Apps Script プロジェクト悪用の検出

エクサフォースは、なりすましや悪用を含む Apps Script プロジェクトの不正利用を検出し、阻止するための多層的なカバレッジを提供します。エクサフォースのアプローチは、高度な異常検出を活用して、環境内のエンティティのベースライン行動を理解し、異常な振る舞いを特定するものです。この調査の結果として、お客様を保護するための新たな検出機能をいくつか追加しました。

  • エクサフォースは、通常とは異なる API サービス、ラベル、またはポリシーバインディングを使用する Google Apps Script プロジェクトを検出し、オンボーディング時にフラグを立てます。
  • エクサフォースは、攻撃者が Apps Script プロジェクトに見えるプロジェクトを作成しようとする、プロジェクトなりすましの試みを検出します。

以下は、Apps Script プロジェクトへのなりすましを試みるシナリオの例です。

Exaforce dashboard showing detection of attempt to impersonate GCP Apps Script project naming pattern.
隠し Apps Script なりすましプロジェクトの検出例

検出の概要には、エクサボット(AI エージェント)によるアラートの自動トリアージの要約と結論が表示されます。対象となる principal など、重要な情報を把握できます。

Session timeline showing GCP project and billing API activity from a single Boston IP address.
なりすましに関連するすべてのイベントを含むセッション

エクサフォースでは、この principal に関連するイベントをセッションとしてまとめるため、このプロジェクトが作成された文脈や、この principal が実行した他のアクティビティを把握しやすくなり、影響分析を包括的に行えます。

Graph linking user bproko@exaforce.com  and IP 86.62.29.143 to multiple CreateProject and EnableService calls.
Apps Script なりすましプロジェクトの作成時に発生したイベントのビジュアルグラフ

影響を受けた関連イベントとリソースは、調査しやすいようにグラフとして視覚的にマッピングされます。

予防的コントロール

エクサフォースの検出により、この潜在的な問題を可視化できます。加えて、組織内で Apps Script を利用していない場合は、前述の、sys-[0-9]{26} に一致する ID を持つプロジェクトの作成をブロックする組織ポリシーを適用することも推奨します。これにより、攻撃対象領域を縮小し、組織のセキュリティ態勢を大幅に強化できます。

ステルス性の高い永続化と防御

Apps Script プロジェクトは、監視されないまま放置されると、ステルス性の高い永続化メカニズムとして機能する可能性があります。攻撃者はこれらになりすまして、クリプトマイニング、高権限のサービスアカウント、その他の悪意あるリソースを環境内に隠すことができます。

防御側は、Apps Script プロジェクトの内部的な仕組みと、潜在的な悪用をどのように検出・ブロックするかを理解する必要があります。組織ポリシーと、エクサフォースが提供するような強力な検出機能を組み合わせることで、この永続化ベクトルに対する包括的なカバレッジを実現できます。

関連記事

理想のSOCチーム。
24時間365日、お客様とともに稼働します。

お客様の環境を一元的かつリアルタイムに把握する4つのエクサボットが、検出、トリアージ、調査、対応をカバーします。プラットフォームを自社で運用することも、エクサフォースに運用を任せることもできます。