それは実行してくれと頼んできた
Sonatype は、AI が生成した名前で数百の使い捨てアカウントから公開された 846 個の悪意ある npm パッケージを追跡している。どれ一つとしてインストールスクリプトを持たない。README は require() でパッケージを読み込めと指示し、その import がローダーを実行する。名前・作者・宣言されたフックで悪いパッケージを見分ける防御は、いまや生成器と競走している。Bromure Agentic Coding は、生成器に偽造できない唯一の性質でブロックする。パッケージの古さである。
パッケージにインストールフックはない。だから npm には捕まえるものが
何もない。README は require() で読み込めと指示し、その import が
ローダーを実行する。そしてその指示に従う読み手は、ますますコーディング
エージェントになりつつある。
Sonatype Research Labs は 8月5日、 Flooding Dropper と呼ぶキャンペーンを公表した。悪意ある npm コンポーネントは 846 個を数え、 まだ増え続けている。 OpenSourceMalware の Paul McCarty は、そのペイロードを WEL1DROPPER として追跡している。より 広い報道は 8月7日 に出た。
この一件を、ここ一か月の npm サプライチェーン事件と分けている点が二つある。
一つ目は、パッケージが名前を得るやり方だ。攻撃者はそれを生成しており、
slopsquatting を生産ラインとして回している。bigops や bnpl といった
繰り返し現れる語幹が他の単語と組み替えられ、bigops-api や
dolyame-boxy-desktop-bnpl-card-gallery のような名前になる。多くが
35.x.y というバージョンパターンを共有する。公開は数百の使い捨て
アカウントに分散し、各アカウントが持つのはほんの数個だ。だから、BAN すべき
多作な作者というものが存在しない。
インストールスクリプトが無い
パッケージはライフサイクルフックを一切宣言しない。だから npm install は
何も実行しない。McCarty の記事はそれを率直に述べている。「これらの NPM
パッケージは preinstall も postinstall スクリプトも使わない。必要が
ないのだ」
パッケージはバンドルされた _helpers.js を同梱しており、これはモジュールが
import されたときに走る。そして README が読み手に import せよと指示する。
require() でパッケージを読み込め、とドキュメントは言い、そのドキュメント
は正しい。第一段はオペレーティングシステムとプロセッサアーキテクチャを
採取し、三つある Cloudflare Workers ホストのいずれかから HTTPS でネイティブ
の第二段を取得する。直接の取得が失敗した場合は、wel1.ru 配下の DNS TXT
レコードに置かれた base64 の断片からバイナリを再構成する経路に切り替える。
第二段はプラットフォームごとに異なる。Linux には UPX で圧縮された ELF が 届き、Sliver の C2 インプラントを設置する。macOS にはアンチデバッグの チェック、VMware の探索、ビーコンバイナリ、そして LaunchAgent による永続化 が届く。Windows には ETW と AMSI のパッチ、サンドボックス検知、メモリ内での リフレクティブ実行、レジストリの Run キーとタスクスケジューラによる永続化が 届く。McCarty と Sonatype はいずれも、このキャンペーンを 4月の依存関係混同 の波「Moika」— 環境変数の窃取を軸に 250 個ほどのパッケージを押し出した — の進化形として読んでいる。
攻撃者は引き金をパッケージマネージャから読み手へと移した。
自動化は拒否リストに勝つ
Sonatype 自身の要約が、一連の報道でもっとも鋭い一行だ。
これは防御についての言明として読むべきだ。悪いパッケージを見分けることで あなたを守る層は、それが名前によるものであれ、公開アカウントによるもので あれ、宣言されたフックによるものであれ、いまや生成器と競っている。それらの 層は、すでに見たものなら今も捕まえる。だが評価されるのは特定の文字列を 見分ける能力であり、特定の文字列の供給はスクリプトを一度回す程度のコストで 済む。
エージェントという角度がこれをさらに鋭くする。そもそも slopsquatting が 割に合うのは、言語モデルが「それらしく聞こえて実在しない」パッケージ名を 発明するからであり、その名前を登録した攻撃者はインストールのトラフィックを ただで集められる。そこに欠けたインストールフックを足せば、引き金が README に書かれた指示であるパッケージができあがる。コーディングエージェントこそ、 名前をハルシネーションし、その下にあるパッケージを見つけ、ドキュメントを 読み、ドキュメントの言う通りにする読み手だ。このキャンペーンは両端とも エージェント向けに形づくられている。
生成器に偽造できない性質を選べ。
古さは名前ではない
このキャンペーンのすべてのパッケージが共有する性質が一つある。どれだけ自動 化しても変えられない性質、すなわち「新しい」ということだ。モデルが先週の 火曜に発明した名前の下に、2019年からあるパッケージが存在することはあり得 ない。数百の使い捨てアカウントが数百の真新しいバージョンを公開するのは、 使い捨てアカウントには公開の元になる履歴が無いからだ。この商売は、誰かが 手を伸ばした時点でパッケージがそこにあることに依存する。だから攻撃者が それを作るのは、せいぜい数日前でしかない。
Bromure Agentic Coding はエージェントを Apple Silicon 上の使い捨て Linux VM の中で実行し、そのネットワークトラフィックの一バイト残らずをホスト上の プロキシ、エージェントが走る箱の外側へ通す。npm、 PyPI、Cargo、RubyGems、Maven、NuGet、Go モジュール、Packagist のパッケージ 取得はすべてそのプロキシを横切り、そこで最初に出会うポリシーが時計である。
エイジゲート、既定で有効
Bromure は最小の古さに満たないパッケージを拒否する。出荷時の設定は 2 日
だ。latest や semver の範囲といった浮動の参照は、しきい値より古い中で
最も新しいバージョンに解決される。だから通常のインストールは動き続け、
昨日のリリースに着地する。新しすぎるバージョンへの固定参照には 451 が
返り、何が起きたかを明示する Bromure のエラーが添えられる。800 個の
パッケージを止めるコストは 8 個と変わらない。Bromure はそのどれ一つとして
見分けていないからだ。
議論が始まる前に効く
このキャンペーンの巧妙な部分、つまりライフサイクルフックを捨てて実行を
require() へ移したことは、tarball がディスクに届いた後の話を述べて
いる。エイジゲートは取得の時点、ホストのプロキシの中、その全部より上流に
座っている。ペイロードがインストール時に走ったのか import 時に走ったのか
は、そもそも到着しなかったパッケージについての問いだ。VM 内の .npmrc と
pip.conf はこのポリシーを厳しくできるが、緩めることはできない。
評判は、時計の後ろに
socket.dev のキーがあれば、Bromure は取得を通す前に各パッケージを socket.dev のデータベースに照会し、侵害・マルウェア・タイポスクワットと フラグされたものをブロックする。OSV の照会は、あなたが選んだ深刻度以上の 既知脆弱性を抱えるパッケージをブロックする。Delpi はすべての npm リクエストをフィルタリングレジストリへ丸ごと振り替える。これらの層は物を 「見分ける」。それが有用である理由であり、二番手に置く理由でもある。
README はそれ相応に採点される
「これを require() で読み込め」は、見知らぬ他人が書いた指示であり、
エージェントが読むテキストとして届く。Bromure はエージェントが消費する
ツール出力を、ファイルの中身も、ウェブページも、取得したドキュメントも
含めて、ローカルのプロンプトインジェクションモデルで採点する。
エージェントが権威として扱う指示ファイル、CLAUDE.md や AGENTS.md と
その派生については、専用の分類器に加えて不可視 Unicode ペイロードの
スキャナで採点する。そのために Mac の外へ出るものは何もない。
ペイロードは違う機械のプロファイルを取る
パッケージがしきい値を越えて古くなっていた、あるいは許可リストの項目が例外 にしていたなどの理由で、取得が通ってしまったとしよう。WEL1DROPPER を Bromure のプロファイルの中で追いかけ、あなたがどうせ捨てるつもりだった機械 に対して、それが仕事をこなす様子を見てみればいい。
ローダーの最初の一手はホストの採取だ。Bromure のプロファイルにおいて、それ
が採取するホストは NAT 配下の Ubuntu VM であり、macOS からハイパーバイザ
一枚ぶん離れている。だから Linux の分岐を取り、そこに Sliver のインプラント
を植える。macOS の分岐は決して要求されない。アンチデバッグも、VMware の
探索も、beacon_mac.bin のダウンロードも、再起動を生き延びたはずの
LaunchAgent も、ロシアの決済ドメインへの関心も。そのすべては、ドロッパーが
一度も見なかった機械のためのコードだ。Erase home は、それが着地した層を
破棄し、ホームディレクトリを clone 直後の状態に戻す。
続いて infostealer の段は、その祖先である Moika が探しに行ったものを探しに
行く。環境変数だ。Bromure のプロファイルでは、環境変数が持っているのは
brm_… のプレースホルダである。本物の Anthropic・OpenAI・xAI のキーも、
GitHub と GitLab のトークンも、AWS の資格情報もレジストリの認証情報も、VM の
メモリにもドットファイルにも存在しない。Bromure はそれらをホスト側に留め、
プロキシで、ワイヤ上で、それぞれが属するただ一つの宛先ホストに対してのみ
リクエストへ差し込む。窃取は最後まで走りきり、たった一台の Mac でしか
解決しない文字列を送り出す。そしてその Mac は、コードを実行した機械ではない。
勧められている狩り、すでに走っている
McCarty は WEL1DROPPER の記事を防御側への助言で締めくくっている。wel1.ru
への DNS TXT クエリを狩れ、Cloudflare Workers のホスト宛のトラフィックが
ないかプロキシのログを見直せ、迷子の一時ファイルと LaunchAgent を探せ、と。
二つ目をもう一度読んでほしい。あなたのノート PC はプロキシのログを取って
いない。Bromure のプロファイルは構造上それを取っている。VM が出すすべての
リクエストはホストのプロキシを横切り、Security Log と Trace Inspector が
それぞれについてホスト、ステータス、レイテンシ、差し替えのレポートを記録
する。ビルドの最中に見慣れない *.workers.dev の宛先が三つ現れたなら、
それはあなたが読めるリストの三行だ。ビルドが走ったその機械の上に、何かを
デプロイしなくても、ある。
レジストリはこれらのパッケージを削除し続けるだろうし、テイクダウンは効く。 846 個は消えたか、消えつつある。しかしキャンペーンはすでに千を超えており、 作るのにコストのかからないアカウントから、発明するのにコストのかからない 名前で公開されている。次の波はスクリプト一回ぶんの距離にある。見分ける勝負 は、そもそも出るべき勝負ではない。
二日前のパッケージが、二時間前のパッケージに無い深い信頼を勝ち取るわけでは ない。ただ、この経済が回っている窓の外に出ているだけだ。そしてあなたは、 すでに有効になっている設定ひとつで、依存グラフをその窓の向こう側へ動かせる。
Bromure Agentic Coding をインストールし、取得があなたの 機械に出会う前に、時計に出会うようにしよう。