プロバイダは鍵のある場所で動く
Aikido は、HashiCorp のレジストリに 2 つの Terraform プロバイダとして配布された北朝鮮製の RAT を発見した。terraform init はプロバイダをダウンロードし、terraform plan はそれをローカルプロセスとして実行し、プロバイダはあなたのクラウド認証情報を読む。プロバイダとはそういうものだからだ。ペイロードは特定の被害者の変数名でしか復号されないため、パッケージを取得したスキャナには不活性なコードしか見えなかった。到達した 18 台のうち 10 台は Mac だった。Bromure Agentic Coding はそのバイナリに VM と偽のシークレットキー、そして接続してよい宛先の一覧を与える。
業界がコーディングエージェントのために築いてきた制御は、どれもコマンドの手前に
ある。計画を承認し、差分を読み、push を絞る。ダウンロードされたプロバイダの
バイナリと ~/.aws のあいだに立つものは一つもない。
あなたはスタックにコンテナを追加しようとしている。エージェントに Docker
プロバイダを頼むと、HCL を 4 行書いてくれる。そして terraform init を実行する。
Terraform はレジストリからプロバイダを解決し、ダウンロードし、署名を検証し、
キャッシュする。次に terraform plan を実行すると、Terraform はそのバイナリを
あなたのマシン上のプロセスとして起動し、RPC で会話する。その過程で、あなた自身が
使っているのと同じ環境変数と同じ ~/.aws プロファイルを用いて、クラウド
アカウントへ認証する。
そのすべての段階が文書化された設計であり、そして今月はマルウェアの配送経路でも あった。
Aikido が見つけたもの
9 月 22 日、Aikido は報告した。 Go 製のリモートアクセス型トロイの木馬が、2 つの Terraform プロバイダと 2 つの Go モジュールを通じて配布されていた。The Hacker News は翌日これを報じ、 攻撃者が HashiCorp の運営するレジストリを使ってペイロードを配送した初めての 事例だとした。Checkmarx、JFrog、SafeDep も分析を加えている。
プロバイダは gocommunity-io/dockerd(ダウンロード 222 件)と
kreuzwenker/docker(1,449 件)。後者は、報告ベースで 5,600 万ダウンロードを持つ
本物のプロバイダ kreuzwerker/docker と 1 文字しか違わない。Go モジュールは
8 月 11 日公開の gocommunity.io/orderedbtree と、9 月 8 日公開の
gogets.dev/btreex である。
Aikido はこのキャンペーンを Graphalgo に結びつけている。ReversingLabs が 2 月に 文書化し、北朝鮮の運用者に帰属させたものだ。同じ署名鍵は 4 月以降 npm の ペイロードにも現れている。人々がこれらのパッケージにたどり着く経路は求人だ。 Web3 企業を装った相手が LinkedIn や Facebook で開発者に接触し、コーディング課題を 送ってくる。課題のリポジトリ自体はきれいだ。それが引き込む依存がマルウェアである。
プロバイダはプログラムであり、Terraform はそれに鍵を渡す
npm install はパッケージのセットアップスクリプトを走らせたあと身を引く。
terraform init はプログラムそのものを取得し、そのプログラムを実行することこそが
本番である。
Terraform プロバイダとは、Terraform Core が RPC で呼び出す実行可能バイナリ である。Terraform がそれを実行するのは、実行することがプロバイダの正体だからだ。 剥がすべきインストールフックも、監査すべきライフサイクルスクリプトも見つからない。 ここでの実行は、tarball を展開した副作用ではなく機能そのものだからである。
そのバイナリは、あなたのネットワークのうち本番までの経路がもっとも短い場所でも 動く。Purple Shield はそのマシンをこう記している。 「悪意あるプロバイダは、つまりあなたのインフラをデプロイする人のマシン上で、 その人が持つ権限のまま実行される」。そして「terraform apply を実行するノート パソコンには、たいていクラウドプロバイダの鍵、Git トークン、SSH 鍵、Terraform の state ファイルが手の届く範囲にある」。
npm の窃取型マルウェアは、認証情報を求めてホームディレクトリを漁らねばならない。 Terraform はそれをプロバイダに手渡す。
たった一人に宛てられていた
これらのプロバイダは一つの標的だけを攻撃し、インストールした他の全員を無視する。
どちらも /internal/provider/resource_docker_container_funcs.go に隠された入口を
持ち、Terraform 変数 containerName と networkID を連結した SHA256 が、
ハードコードされた一つの値と等しいときにだけ発火する。コードはそのハッシュを
AES 鍵として使い、パッケージ内のファイルパスを復号し、そこで見つけたものを
展開し、各ファイルを復号し、結果を Go パッケージとして切り離した go run . で
実行する。アーカイブは examples ディレクトリの中、import-resource.sqlite3 と
いう名前のファイルに隠されていた。
つまり攻撃者は、あなたが 2 つの変数に付けた名前を知っていなければならない。そして この攻撃者は知っていた。求人という餌こそが、それを学んだ経路だった。パッケージを 取得した研究者もスキャナも、そこに座ったまま何もしないコードを手にした。決して 復号されないものは検出できない。
第 2 段階はリモートアクセス型トロイの木馬である。まずシステム情報を報告する。
ハードウェアの属性、OS、ホスト名、そしてそのマシンで node が使えるかどうか。
それから 2 か所で指示を待つ。一つは Slack だ。portfolio-devs というワーク
スペース、frontend-devs というチャンネル、のちに qa-announcements という
チャンネルを持つ第 2 のワークスペース。Aikido はそこに 7 月 16 日まで遡る
1,240 件の暗号化メッセージを数えた。もう一つは Arbitrum Sepolia テストネット上の
スマートコントラクトで、ハードコードされたアドレスにあり、8 月 6 日まで遡る
1,402 件のトランザクションを抱える。マルウェアはブロックチェーンを 3 秒ごと、
Slack を 10 秒ごとに照会する。
どちらの経路も、評判の良い宛先への普通の HTTPS である。開発マシンは一日じゅう Slack と話しているし、そのうちの一台が公開 Ethereum RPC エンドポイントと話して いても、チームがオンチェーンで何かを出す週にはごく普通に見える。同じ集団は Checkmarx が先週記述した npm パッケージでも Sepolia のコントラクトを使っており、 公然と再利用している。公然とやっても通用するからだ。
Aikido は攻撃者自身の Slack から被害者リストを回収した。725 件のチェックイン メッセージにまたがる 18 個のユニークなホスト名。Windows が 3、Linux が 5、 macOS が 10。
Bromure のワークスペースで何が変わるか
このキャンペーンが狙ったのは認証情報だった。Bromure Agentic Coding では、VM の
~/.aws/config にあなたのシークレットアクセスキーは入っていない。そこが指すのは
credential-process ヘルパーで、virtio ソケット越しに、あなたの本物のアクセスキー
ID と偽の 40 文字のシークレットを組にして渡す。マニュアルには、これを設定
なしでそのまま拾うツールが並んでいる。すべての AWS SDK、aws CLI、boto3、そして
terraform。プロバイダは期待どおりに認証情報チェーンを読み、整った形のプロファイル
を得て、何の価値もないシークレットでリクエストに署名する。
その署名はホストが出口で直す。リサイナーは *.amazonaws.com 宛のトラフィックを
認識し、ゲストの絶望的な SigV4 署名を剥ぎ取り、リクエストがあなたの Mac を出る前に
本物のシークレットで署名し直す。あなたの terraform plan は動く。AWS は盗まれた
認証情報のコピーには InvalidSignatureException を返す。
次に、偽の鍵が外へ出ようとする。プロキシは外向きリクエストのヘッダとボディを すべて走査し、そのワークスペース自身のプレースホルダ値を探す。使うのは Aho-Corasick オートマトンで、全リクエストに掛けても十分に安い。鋳造された先では ない相手へ、たとえば Slack の webhook へ偽の鍵を送ってみれば、プロキシは HTTP 451 を返し、1 バイトも転送しない。VM は一時停止する。赤い Credential brokering の 行が Security Timeline に現れ、Bromure はそのワークスペースを侵害済みと記録する。 だから次回の起動時には、トークン・SSH 鍵・設定を保ったままディスクとホームを消す 選択肢が提示される。
C2 はなお、マシンから出ていかなければならない。egress は allow と deny の順序付き
ルールセットで、上から下へ照合され、最後は default allow か default deny で
終わる。インフラをデプロイするワークスペースに必要なのは
registry.terraform.io、releases.hashicorp.com、そしてあなたのクラウド API で、
portfolio-devs.slack.com と Arbitrum の RPC エンドポイントはその一覧に入らない。
ホストはルールを二度強制する。仮想スイッチが宛先 IP と DNS から読み取った
ホスト名で、TCP と UDP の両方にわたって照合し、プロキシが TLS のサーバ名で
もう一度照合する。スイッチはポート 80 とポート 443 をプロキシへ迂回させる。環境
変数は一切介在せず、ゲストが解除できるものも何もない。判定はすべて Security
Timeline の Firewall 行になる。
削除を承認するのはあなた
プロバイダとあなたのアカウントのあいだには、もう一つ制御がある。攻撃者が秘密より インフラそのものを欲しがったときに効くのは、これだ。
AWS の認証情報には書き込みポリシーが付いており、新しいワークスペースの既定は
Prompt before write である。プロキシは X-Amz-Target ヘッダまたは Action=
パラメータのアクション名で各呼び出しを分類する。Get*、List*、Describe* は
読み取りで、何も言わずに通る。一方 Delete*、Terminate*、Revoke* とその仲間は
破壊的とみなされる。書き込みは
Allow write on "<scope>" from workspace "<name>"? と題されたダイアログで止まる。
そこには要約ではなく操作そのものが文字どおり表示され、Allow for 15 minutes、
Allow once、Allow for the rest of the session、Don't allow が並ぶ。
拒否すれば、ゲストは AccessDeniedException を受け取り、エージェントはそれを
報告して先へ進む。
そのダイアログをクリックするのは、あなたの Mac にいるあなただ。VM の中のプロセスは そこへ手を伸ばせないし、Slack チャンネルの向こう側にいる誰かも同じである。
ツールチェーンを実行器として扱う
terraform init と terraform plan はダウンロードして実行するものだ。
プロバイダが内側で何を実行するのかを問うこと。実行するのだから。
生きた秘密を手の届く場所に置かない
プロバイダを満足させる認証情報チェーンと、何の価値もないシークレットを与える。 署名は境界の向こう側でホストが行い、その鍵が属する宛先に限定される。
まず宛先に名前を付ける
デプロイ用のワークスペースに必要なのは、短く安定した一覧だ。レジストリ、 リリースのホスト、あなたのクラウド API。一度書いて、一致しない通信を Deny に すれば、デッドドロップのコントラクトには照会する先がなくなる。
取り返しのつかない操作には尋ねさせる
読み取りは静かに走らせる。Terminate* の代償を、VM が届かない画面に出る
ダイアログ 1 回にする。
ここでは誰も脆弱性を見つけていない
HashiCorp のレジストリは求められたパッケージを配った。Terraform は渡された チェックサムを検証し、あらゆるプロバイダを実行するのと同じやり方でそのプロバイダを 実行した。サンドボックスは破られていないし、エージェントが汚染された指示を読んだ わけでもなく、モデルが誤作動したわけでもない。
合わせて 1,671 ダウンロードの 2 つのプロバイダが少なくとも 18 台のマシンに届き、 その多くは Mac で、そこに居座って 3 秒ごとにスマートコントラクトを照会していた。 連鎖全体を運んだのは、文書化された挙動である。唯一新しかった部分、つまり変数が 特定の名前でない限り暗号化されたままのペイロードは、何にも見つからないために 存在している。
だからこうしたことはセッションの前に決めておくことだ。最中には捕まえられない。 ペイロードを復号していないプロバイダは走査できないし、Slack 宛のトラフィックから 流出を選り分けることもできない。鍵が囮であり、宛先があなたの書いた一覧であり、 乗っ取られるマシンが捨てられるものである場所で、デプロイを実行すること。