Joyfillのnpmベータ版と公開リポジトリへの不正なコードの混入
- カテゴリ
- マルウェア
Joyfillのアドバイザリによると、npmパッケージ@joyfill/layoutsと@joyfill/componentsのベータ版が、読み込み時に動く遠隔操作型トロイの木馬(RAT)を含んで2026年7月28日に公開された[1]。Joyfillは9月12日の事後報告で、ホスト型のプラットフォームで不正なコードが動いた形跡も、顧客データにアクセスされた形跡も見つからなかったと説明した[1]。
経緯
侵入から公開までの経路
Joyfillの事後報告では、攻撃者は貢献者1人のGitHubの資格情報を使い、同社のリポジトリに自動化ツールを走らせた[1]。ツールは既存のコミットを、作者・メール・メッセージ・時刻を写した偽の複製に置き換えて不正なコードを加え、元のコミットの上にforce-pushした[1]。偽のコミットはどれも.gitignoreを編集し、.gitignoreにconfig.batを加え、.envを外していた[1]。npmのトークンの窃取、npmアカウントへのフィッシング、CIの侵害ではなかったとJoyfillは説明した[1]。
ベータ版は保守者の端末でビルド・公開されていた[1]。新しいベータ機能を試していた同社の技術者1人が、マルウェアを含むと知らずに偽のブランチの1つを取り込み、自分の端末から2つのベータ版を公開した[1]。ローカルのブランチがレビュー済みのものと一致するかを確かめる仕組みは無く、リリースにはコミットとの来歴(provenance)も無かった[1]。
事後報告より前に、StepSecurityは、不正なコードは公開されたtarballの中だけにあり、プロジェクトのソースに対応する変更は無いと報告した[6]。The Hacker Newsの記事では、Socketは2つの不正な版が同じnpmのIDからNode.js 18.20.0とnpm 10.5.0で公開されたと説明した[9]。同紙は、ソースへの差し込みが開発者の端末・ソースのリポジトリ・CI環境・公開用の資格情報のどの侵害によるかは分かっていないとも報じた[9]。
不正なコードの動作
Joyfillの8月2日の改訂版(Revision 2)によると、回収したペイロードは攻撃者の基盤との対話的な遠隔操作の通信路を開き、任意のJavaScriptとシェルのコマンドを実行する[1]。ホストと環境の情報、クリップボードを集め、端末のファイルを送り出す[1]。ブラウザの保存資格情報、ウォレットとパスワード管理の拡張機能のデータ、Gitの資格情報、GitHub CLIの設定、エディタの保存データも集める[1]。常駐の手段は、npmのCLIを含む開発ツールの書き換えである[1]。
StepSecurityの報告では、不正なコードは5段階からなり、公開ブロックチェーンのTronとBNB Smart Chain(BSC)、予備のAptosのトランザクションから次の段階を取り出す[6]。遠隔操作の通信路にはSocket.IOを使う[6]。
常駐のための自己再読み込みのコードは、VS Code・Cursor・Antigravityが使う@vscode/deviceid、Discordのデスクトップ版、GitHub Desktopのmain.jsなどにも差し込まれる[6]。Pythonの情報窃取ツールはOSのキーチェーンも集め、暗号化した書庫にしてC2に送る[6]。StepSecurityは、真の標的は開発者の端末だとした[6]。
The Hacker Newsによると、最終段のNode.jsのRATは、ホスト名がgithub-runner・buildbot・buildkitsandbox・microsoft-standard-WSL2の機械では動かない[9]。Pythonの情報窃取ツールはOmniStealerの改変版と評価されていると、同紙は報じた[9]。
作戦との関連
Joyfillは事後報告で、コードがPolinRiderと一致すると説明した[1]。PolinRiderは、研究者が公開の場で北朝鮮(DPRK)に関連する運用者に帰属させ、Contagious Interviewのクラスターと関連づけている作戦だとした[1]。8月2日の改訂版には、Socketがこのコードを「DEV#POPPER / Contagious Interview」のマルウェアの系統に結びつけているとの記載があった[1]。
Socketは、ViteVenomと今回のnpmパッケージを、北朝鮮の脅威アクターによる同じ継続中の作戦につながるものとThe Hacker Newsに説明した[9]。SafeDepは、Tronの主なアドレスとBSCの中継のウォレットがastro.config.mjsを使った先の攻撃と同じで、同じ運用者だと確かめられると報告した[7]。OpenSourceMalwareは7月31日の記事で、PolinRiderによる27の正規パッケージの侵害の一覧にJoyfillの2版を入れ、発見者をSocketとした[8]。
時系列
| 日付 | 主体 | 内容 | 資料 |
|---|---|---|---|
| 2026年 | Joyfill | 技術者が偽のブランチを取り込んだ(時刻はUTC。以下同じ) | [1] |
| 2026年 | Joyfill | @joyfill/layouts@0.1.2-2773.beta.0を公開した | [1] |
| 2026年 | Joyfill | @joyfill/components@4.0.0-rc24-2773-beta.4を公開した | [1] |
| 2026年 | - | 無関係な整理のコミットがブランチから不正なコードを取り除いた | [1] |
| 2026年 | Socket、StepSecurity | 分析を公開した | [1] |
| 2026年 | Joyfill | 影響を受けた2版をnpmから削除し〔注2〕、アドバイザリを公開した | [1] |
| 2026年 | Joyfill | 不正なコードのあった公開リポジトリ3件をアクセスできない状態にした | [1] |
| 2026年 | Joyfill | 改訂版(Revision 2)を公開した | [1] |
| 2026年 | Joyfill | 事後報告(Revision 3)を公開し、アドバイザリを閉じた | [1] |
影響範囲
Joyfillは事後報告で、影響を受けた版を6つから2つに改めた〔注1〕[1]。8月2日の改訂版は、初期の第三者の分析に合わせて2773のビルド6つをすべて挙げていた[1]。その後、ビルドのキャッシュから回収した削除済みの版を含め、全版の元のtarballを調べた[1]。0.1.2-2773.beta.1・beta.2と4.0.0-rc24-2773-beta.5・beta.6には、不正なコードが無かった[1]。
OSVの記録(MAL-2026-11161・MAL-2026-11160)では、影響版は@joyfill/layoutsが0.1.2-2773.beta.0、@joyfill/componentsが4.0.0-rc24-2773-beta.4である[4、5]。@joyfill/layoutsの記録には、分類としてCWE-506(Embedded Malicious Code)が付いている[4]。
Joyfillによると、npmでの影響の対象は、7月28日に影響版のどちらかを導入して読み込んだ者である[1]。テストの実行、ビルド、開発サーバーの起動だけでも読み込みが起き、npm install --ignore-scriptsでは防げなかった[1]。
SafeDepの分析では、componentsの影響版はlayoutsの影響版に依存し、不正なコードはlayoutsの側だけにあった[7]。導入用のスクリプトは無かった[7]。不正な版はregistry.npmmirror.comなどの第三者のミラーにキャッシュが残るとも、SafeDepは報告した[7]。
OpenSourceMalwareは、ベータ版の導入は過去に1版あたり2〜4回と少ないと書いた[8]。
GitHubでの影響の対象は、表1のリポジトリを5月25日〜8月1日にclone・pull・fetchし、ビルドか実行をした者である[1]。Joyfillは、cloneの中にコードがあっても、そのファイルを実行・読み込みしていなければ端末は影響を受けていないとした[1]。
対応と現状
Joyfillの8月2日の改訂版では、過去12か月にnpmに公開した800以上の版と、公開・管理する依存パッケージを手作業で調べ、既知の不正なコードは見つからなかった[1]。問題の無い版として@joyfill/layouts 0.1.1と@joyfill/components 4.0.0-rc24を挙げた[1]。4.0.0-rc24自体がプレリリースのため、^4.0.0-rc24や~4.0.0-rc24の範囲指定は2773のビルドに一致しうるとして、正確な版に固定するよう求めた[1]。Joyfillのリリースノートによると、4.0.0-rc24は7月9日に公開された[10]。
同じ改訂版でJoyfillは、影響版を導入・読み込んだ端末を侵害されたものとして扱い、再イメージ化するよう勧めた[1]。その端末から届く資格情報(npmトークン、クラウドの鍵、CIの秘密、SSH鍵、GitHubの個人アクセストークン、データベースの資格情報、.envの内容)の更新も求めた[1]。
Joyfillの事後報告では、リポジトリの保護と公開の手順を次のように改める[1]。
- 組織全体のルールセットで全ブランチのforce-pushと履歴の書き換えを禁じ、バージョンタグを作れる者を制限した[1]。
- リリースはGitHub Actionsからnpmのtrusted publishing(OIDC)でだけ用意し、その前にビルド済みパッケージのマルウェア検査を走らせる[1]。
- 保守者が二要素認証でnpmの管理画面から承認するまで、どの版も導入できないようにする[1]。
- 開発端末では新しく公開されたパッケージに待機期間を設け、ローカルのコード・導入済みパッケージ・開発ツールのプラグインをマルウェア検査する[1]。
npmレジストリの記録では、@joyfill/layoutsの0.1.2-2773.beta.0は現存の版に無く、beta.1とbeta.2は非推奨の表示付きで残っている[2]。@joyfill/componentsの4.0.0-rc24-2773-beta.4・beta.5・beta.6は、どれも現存の版に無い[3]。
未公表の事項
- 貢献者のGitHubの資格情報が侵害された経緯
- 影響版を導入・読み込んだ利用者の数
- 期間内に公開リポジトリをclone・pull・fetchした利用者の数
- 公開リポジトリの再公開の有無
- Joyfill自身による攻撃者の帰属の判断
注
出典
当事者
- [1]Joyfill LLC『Security Advisory: Compromised @joyfill npm beta releases and public github repos』(2026年7月29日公開、2026年9月12日更新)
関係組織
- [2]npm, Inc.『@joyfill/layouts』(公開日不明、2026年11月4日閲覧)
- [3]npm, Inc.『@joyfill/components』(公開日不明、2026年11月4日閲覧)
調査・観測
- [4]OSV(Open Source Vulnerabilities)『MAL-2026-11161: Malicious code in @joyfill/layouts (npm)』(2026年7月28日公開、2026年8月5日更新)
- [5]OSV(Open Source Vulnerabilities)『MAL-2026-11160: Malicious code in @joyfill/components (npm)』(2026年7月28日公開、2026年8月5日更新)
- [6]StepSecurity, Inc.『Compromised npm Packages: @joyfill/components and @joyfill/layouts Ship an Obfuscated Remote Access Trojan』(2026年7月28日公開)
- [7]SafeDep『Joyfill npm Packages Compromised with Blockchain C2 Loader』(2026年7月29日公開)
- [8]OpenSourceMalware『PolinRider Caused Dozens of npm, Go, PHP Compromises』(2026年7月31日公開)
報道機関
- [9]The Hacker News『Two Compromised joyfill npm Packages Run RAT When Imported Into Node.js』(2026年7月29日公開、2026年8月3日更新)
背景資料
- [10]Joyfill LLC『Release Notes』(2026年7月9日公開)
更新記録
- 2026年8月13日:公開した。
- 2026年9月12日:Joyfill LLCの9月12日の事後報告(出典1の改訂版)で示された侵入から公開までの経路を「経緯」に、再発防止の措置を「対応と現状」に加え、影響を受けた版を6版から2版に改めて「影響範囲」と「時系列」を書き直した。
