すべての投稿に戻る
公開日 · 著者 Renaud Deraison

それは実行してくれと頼んできた

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 を生産ラインとして回している。bigopsbnpl といった 繰り返し現れる語幹が他の単語と組み替えられ、bigops-apidolyame-boxy-desktop-bnpl-card-gallery のような名前になる。多くが 35.x.y というバージョンパターンを共有する。公開は数百の使い捨て アカウントに分散し、各アカウントが持つのはほんの数個だ。だから、BAN すべき 多作な作者というものが存在しない。

インストールスクリプトが無い

パッケージはライフサイクルフックを一切宣言しない。だから npm install は 何も実行しない。McCarty の記事はそれを率直に述べている。「これらの NPM パッケージは preinstallpostinstall スクリプトも使わない。必要が ないのだ」

パッケージはバンドルされた _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 個ほどのパッケージを押し出した — の進化形として読んでいる。

攻撃者は引き金をパッケージマネージャから読み手へと移した。

見分けられなくする三つの道名前は生成されるbigops-api, dolyame-boxy-desktop-bnpl-card-gallery、ほか約844件拒否リストは先に名前が要る生成器の在庫はまだある発行者は使い捨て数百アカウント、各数個のパッケージ作者を BAN しても止まるのは800件のうち4件インストールスクリプトが無いpreinstall も postinstall も無しREADME 曰く: require() せよ_helpers.js は import で走る引き金は読み手import がローダーを実行するOS と arch を採取し、ネイティブ段を取得Cloudflare Workers、または wel1.ru の DNS TXT
このキャンペーンは見分けられることを封じるように作られている。攻撃者は名前を選ぶのではなく生成し、発行者を数百の使い捨てアカウントで回し、実行の引き金をライフサイクルフックから README の指示へと移した。スキャナが見つけるべきインストールスクリプトは残らず、拒否リストが掴める安定した名前も作者も残らない。

自動化は拒否リストに勝つ

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 内の .npmrcpip.conf はこのポリシーを厳しくできるが、緩めることはできない。

評判は、時計の後ろに

socket.dev のキーがあれば、Bromure は取得を通す前に各パッケージを socket.dev のデータベースに照会し、侵害・マルウェア・タイポスクワットと フラグされたものをブロックする。OSV の照会は、あなたが選んだ深刻度以上の 既知脆弱性を抱えるパッケージをブロックする。Delpi はすべての npm リクエストをフィルタリングレジストリへ丸ごと振り替える。これらの層は物を 「見分ける」。それが有用である理由であり、二番手に置く理由でもある。

README はそれ相応に採点される

「これを require() で読み込め」は、見知らぬ他人が書いた指示であり、 エージェントが読むテキストとして届く。Bromure はエージェントが消費する ツール出力を、ファイルの中身も、ウェブページも、取得したドキュメントも 含めて、ローカルのプロンプトインジェクションモデルで採点する。 エージェントが権威として扱う指示ファイル、CLAUDE.mdAGENTS.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 は、コードを実行した機械ではない。

開発者の Mac の上でホストを採取するmacOS, arm64: mac の分岐へbeacon_mac.bin、LaunchAgent、再起動耐性環境変数を読むAPI キー、git の認証情報、クラウドの資格情報すべて本物、すべて使えるそして居座る奪われる機械は、あなたが使う機械Bromure のプロファイルの中で取得はまず時計に当たる今日公開、しきい値は2日: 451名前の照合も署名も要らないそして万一届いたとしてもVM の中の Linux、mac の分岐は未使用環境にあるのは brm_ のプレースホルダそして居座れないErase home が着地した層を戻す
ドロッパーは、見つけたホストに応じてペイロードを選ぶ。Bromure のプロファイルの中では、そのホストは使い捨ての Linux VM だ。だから LaunchAgent による永続化も、VMware の探索も、ビーコンバイナリも伴う macOS の分岐は決して要求されず、infostealer の段が読み取るのは、本当の値が一度も VM に入ったことのないプレースホルダの認証情報である。

勧められている狩り、すでに走っている

McCarty は WEL1DROPPER の記事を防御側への助言で締めくくっている。wel1.ru への DNS TXT クエリを狩れ、Cloudflare Workers のホスト宛のトラフィックが ないかプロキシのログを見直せ、迷子の一時ファイルと LaunchAgent を探せ、と。

二つ目をもう一度読んでほしい。あなたのノート PC はプロキシのログを取って いない。Bromure のプロファイルは構造上それを取っている。VM が出すすべての リクエストはホストのプロキシを横切り、Security Log と Trace Inspector が それぞれについてホスト、ステータス、レイテンシ、差し替えのレポートを記録 する。ビルドの最中に見慣れない *.workers.dev の宛先が三つ現れたなら、 それはあなたが読めるリストの三行だ。ビルドが走ったその機械の上に、何かを デプロイしなくても、ある。

レジストリはこれらのパッケージを削除し続けるだろうし、テイクダウンは効く。 846 個は消えたか、消えつつある。しかしキャンペーンはすでに千を超えており、 作るのにコストのかからないアカウントから、発明するのにコストのかからない 名前で公開されている。次の波はスクリプト一回ぶんの距離にある。見分ける勝負 は、そもそも出るべき勝負ではない。

二日前のパッケージが、二時間前のパッケージに無い深い信頼を勝ち取るわけでは ない。ただ、この経済が回っている窓の外に出ているだけだ。そしてあなたは、 すでに有効になっている設定ひとつで、依存グラフをその窓の向こう側へ動かせる。

Bromure Agentic Coding をインストールし、取得があなたの 機械に出会う前に、時計に出会うようにしよう。