TL; DR
のクラスター 5つのnpmパッケージ2 つのアカウントハンドルで公開され、出荷された postinstall ホストからクラウド認証情報を読み取り、外部に送信するフック。パッケージにはゴーストと海賊の名前が付けられている。 coral-wraith, ecto-corsair-whisper-6f3b9, ecto-corsair-flag-x9m4, ecto-rust-read-f3a9c1, ecto-nightly-spirit そして、彼らが収集したものを偽物にシリアル化する ecto_module: YAML マニフェストを送信する前に、クラスターを追跡します。 エクトプラズム.
ペイロードは、特定の環境を検出した場合にのみ実行されます。その環境とは、名前が 12文字の16進数文字列 そして作業ディレクトリは /app/node_modules — コンテナ化されたビルドまたは CI ワーカーの形状。そのゲートを通過すると、フックはクエリを実行します。 AWSインスタンスメタデータサービス(IMDSv2) IAMロール認証情報について列挙します AWSシークレットマネージャー 3 つのリージョンにわたって、環境変数をダンプし、以下のファイルを読み取ります。 /appそしてキャプチャー・ザ・フラッグ文字列をスクレイピングします。その後、結果を2つの方法で外部に流出します。 webhook.site コレクターとマニフェストPUTを、ローカルホスト優先のフォールバックリストとともに、生IPエンドポイントに送信します。
後期のパッケージ説明には「Verdaccioサプライチェーンテスト用CTFペイロード」と記載されていました。この自己説明は観測可能な事実として報告します。実際の動作(パブリックIPへのライブ送信、実際のIMDS認証情報の読み取り、実際のSecrets Manager呼び出し)はラベルに関係なくその通りであり、これらのバージョンが悪意のあるものとして分類された理由です。
クラスター内の名前の 1 つは、 coral-wraithは、単一のリリースにとどまらず、わずか数時間のうちに数十ものバージョンを次々と再公開した。 1.0.0 登る 6.0.0 そして、同じ名前の以前のシリーズでは、膨張した 9999.0.x バージョン番号、 依存関係の混乱を試みるその過程で、ペイロードは明らかに成熟した。単発のホスト列挙ビーコンから、環境チェックで保護され、意図したターゲット以外では静かに動作する完全なAWS認証情報ピボットへと進化した。
| エクストラ | 5つの名前。 coral-wraith 単独で数十ものバージョンに再掲載された |
| 生態系 | npm |
| ベクターをインストール | postinstall ライフサイクルスクリプト |
| 主なターゲット | AWS IAMロール認証情報 + Secrets Managerシークレット値、環境変数、 /app ファイル |
| 脱出 | webhook.site ビーコン + raw-IP C2 PUT |
| トリガーゲート | 12桁の16進数ホスト名 + /app/node_modules cwdに加え、そのコンテキスト外ではペイロードを抑制する環境チェックも含まれる。 |
| 重大度 | 高いです — クラウド認証情報とマネージドシークレットの開示 コンテナ化されたビルド環境とランタイム環境 |
攻撃の解剖学
クラスター内のすべてのパッケージは、ほぼ空の状態で同じように構築されます。 index.js (module.exports = {})一行 package.json スクリプト — 「postinstall」:「node postinstall.js」 — そしてペイロードは postinstall.jsパッケージをインストールするだけでフックを実行できます。インポートや呼び出しは不要です。
ターゲティングゲート。 エクトファミリーのペイロードは、何か行動を起こす前に、まず周囲の状況を確認します。
function isAppWorker(): host = os.hostname() if host does NOT match /^[0-9a-f]{12}$/ -> exit if cwd does NOT contain "/app/node_modules" -> exit if cwd contains "/tmp/npm-safe" -> exit otherwise -> proceed 12 桁の 16 進数のホスト名は、Docker がコンテナに割り当てるデフォルトの形式であり、 /app/node_modules は、従来のコンテナ内インストールパスです。3 番目の句は、パスがサンドボックス化された抽出ディレクトリのように見える場合に処理を中止します。結果として、ペイロードは開発者ラップトップまたは分析サンドボックス上で休止状態のままになり、コンテナ化されたビルドまたはランタイムワーカー(ライブクラウド認証情報を保持する可能性が最も高い環境)内でのみアクティブになります。クラスター内の最も早いパッケージは、 珊瑚の幽霊にはそのようなゲートはなく、(より単純な)コレクションを無条件に実行します。
収集ゲートが通過すると、フックが突き出る execFileSync(「/bin/sh」、[「-c」、…]) そして、以下の順序で単一の複合コマンドを実行します。
1. PUT /latest/api/token to 169.254.169.254 (IMDSv2 token request) 2. GET .../iam/security-credentials/ (IAM role name) 3. GET .../iam/security-credentials/<role> (temporary credentials) 4. dump env | sort (environment variables) 5. list /app (excl. node_modules) + cat first 15 (application files) 6. aws secretsmanager list-secrets (us-east-1, eu-west-1, eu-central-1) 7. scrape readable files for HTB{...} (capture-the-flag strings) ステップ 1~3 は、教科書通りの IMDSv2 取得です。セッション トークンを要求し、それを次のトークンとして添付します。 X-aws-ec2-metadata-token インスタンスのIAMロールとそのロールの一時アクセスキーを取得するためのヘッダー。よりシンプルな認証不要のIMDSv1ではなく、IMDSv2を実装するという選択。 GET 注目すべき点は、ペイロードがトークンベースのメタデータアクセスを要求するように構成されたインスタンスでも機能することを意味します。これは、AWS が推奨するセキュリティ強化策です。ステップ 3 で返される認証情報は有効期限が短いです。 アクセスキーID/シークレットアクセスキー/Next インスタンスの役割にスコープが限定されたトリプル。その役割が実行できることは何でも、これらのキーの所有者は認証情報の有効期間中実行できます。
ステップ4~6はテイクを広げます。 env dump は、ビルドまたはランタイム プロセスが継承したすべてのものをキャプチャします。実際には、レジストリ トークン、データベース接続文字列、および API キーがここに格納されることが最も多いです。 /アプリ ファイルウォークは最大15個のアプリケーションファイルを外部から読み取ります node_modules表面構成が可能で、 .env ファイル、またはソース。ステップ 6 は、 aws secretsmanager list-secrets 3 つのリージョンで、ステップ 1 ~ 3 で取得した認証情報がまさにそれらの呼び出しを認証するものなので、IMDS 読み取りと Secrets Manager 列挙チェーンが単一のエスカレーションにつながり、インスタンス ロール → managed-secret インベントリとなります。ステップ 7 はキャプチャー ザ フラグの枠組みを意識したものです。 HTB{…} フラグが検出された場合は単独で送信され、そうでない場合は収集された生のブロブが4つの部分に分割されて送信されます。
クラスター内の後のバージョンでは、エスカレーションがさらに進みます。インベントリで止まるのではなく、IMDS 応答を解析し、一時キーをエクスポートします。 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN 環境変数、以下の方法で本人確認を行います aws sts 呼び出し元 ID の取得そして、によって返されるすべての秘密をループします。 リストシークレット 呼び出し aws secretsmanager get-secret-value それぞれについて、名前だけでなく秘密の内容を取得します。同じバージョンは、プロセス置換フラグバイナリも読み取ります(/readflag そして友人たち) チャージラン 以下のRustプロジェクトに対して /アプリクラウド認証情報だけでなく、ビルド環境が公開するあらゆる情報まで収集対象を拡大する。
これらの後のバージョンでは、より積極的にゲートが設けられています。12 桁の 16 進ホスト名と /app/node_modules チェック機能では、ペイロードはアクティブなパッケージレジストリ構成と作業ディレクトリパスを検査し、それらがライブターゲットではなく分析コンテキストまたはミラーコンテキストを示している場合は、サイレントに終了します。その結果、ほとんどの検査環境では目に見える動作をしないペイロードとなり、真のコンテナ化されたホスト上に存在すると判断した場合にのみ、完全なデータ収集を実行します。
exfiltration収集されたデータは、ホストから2つのチャネルで送信されます。まず、ビーコンです。 POST 固定された Webhook.サイト コレクターはホスト名、数値UID、作業ディレクトリ、および最大120KBの収集データを保持します。次に、データは偽のYAML「モジュールマニフェスト」に折り込まれ、 PUT 〜へ /api/modules/ ターゲットサーバー上:
ecto_module: name: "<flag-or-chunk-0>" version: "1.0.0" power_level: "<chunk-1>" ship_deck: "<chunk-2>" cargo_hold: "<chunk-3>" マニフェストフィールド名(パワーレベル, 船の甲板, 貨物室)は装飾です。盗まれたデータは文字列値の中に含まれており、そのためネットワークモニターは明らかなデータダンプではなく、無害なパッケージレジストリマニフェストのアップロードのように見えるのです。ビーコンチャネルはさらに多くの情報を含んでいます。 POST 体に Webhook.サイト ホスト名、数値UID、作業ディレクトリ、および収集されたブロブの最大120KBが含まれるため、ビーコンが1回成功するだけで完全なデータが取得されます。 Webhook.サイト これは無料のリクエスト検査サービスです。これをコレクターとして使用すると、オペレーターはそのチャネル用に独自の受信インフラストラクチャを構築する必要がなくなり、記録されたリクエストはサービスのビンに保持されます。
マニフェスト PUT いくつかのフォールバックリストをたどる 127.0.0.1/ローカルホスト ポートに落ちて、3 つのパブリック アドレスにフォールスルーします。154.57.164.0/24` 範囲を辿り、2xx ステータスで応答する最初のエンドポイントで停止します。localhost を優先する順序は、「verdaccio testing」の自己説明 (ループバック上のローカルレジストリ) と一致していますが、public-IP フォールバックにより、ループバックがリッスンしていないとき、つまり、作者自身のテスト環境ではないマシンでは、データがホストから送信されます。
タイムライン
このクラスターは、急激な機能低下ではなく、段階的な機能向上を示しています。公開までの経過時間ではなく、観測された動作に基づいて順位付けしています。
| ステージ | パッケージ/バージョン | 行動 |
|---|---|---|
| 早朝のランニング | coral-wraith 9999.0.x | 依存関係の混乱を狙ったと思われる、誇張されたバージョン番号。インストール時の列挙とデータ流出。 |
| シード | coral-wraith 1.0.0 | postinstall は id/env/flag ファイルを収集します。単一の PUT で 154[.]57[.]164[.]71:30782マーカー ECT-472839 |
| 迅速な反復 | coral-wraith 1.0.1 → 6.0.0 | 数時間で数十回のリリース。ペイロードは isAppWorker() ゲート、IMDSv2認証情報の取得、完全な get-secret-value ピボット、レジストリ/パス環境チェック、およびデュアルシンクマーカー |
| 並列名 | ecto-corsair-whisper-6f3b9 1.0.14–1.0.18 | 同じゲート付きペイロード。 webhook.site ビーコンおよびマルチエンドポイントフォールバックリスト |
| バリアント | ecto-rust-read-f3a9c1 1.0.1–1.0.2 | シンクマーカーを追加します ECT-987654, ECT-654321, ECT-839201 |
| バリアント | ecto-corsair-flag-x9m4 1.0.0, ecto-nightly-spirit 1.1.0 | 同じゲートペイロード、同じC2およびビーコン |
このクラスターの特徴は、その公開頻度です。1つのパッケージと1つのバージョンではなく、同じ名前が立て続けに何度も再公開され、それぞれのリリースは前回のリリースのわずかなバリエーションであり、同じペイロードを搭載した名前の異なる兄弟パッケージがいくつか存在します。 ささやきます ファミリーのコードは、2 つの類似したフィンガープリントに分割されました。1 つのセットは 2 つの重要な検出をトリガーし、もう 1 つは 3 つの検出をトリガーします (追加のファイル読み取りシンク)。しかし、どちらも同じペイロードに解決されます。違いは、動作のフォークではなく、コードのドリフトです。 ささやきます 分析範囲(執筆時点では少なくとも1.0.25まで)を超えたものがレジストリ上でリアルタイムで観測され、 珊瑚の幽霊 その名前は、同じウィンドウ上で独自のバージョンアップの階段を登り続けた。
妥協の指標
以下の指標はすべて、ディスク上のパッケージソースから抽出したものです。ネットワーク指標は無効化されています。
ネットワーク
| インジケータ | 職種 |
|---|---|
hxxp://154[.]57[.]164[.]71:30782 | C2 PUTターゲット(coral-wraith) |
hxxp://154[.]57[.]164[.]80:30543 | C2 PUT フォールバック (ecto-*) |
hxxp://154[.]57[.]164[.]82:31250 | C2 PUT フォールバック (ecto-*) |
hxxp://154[.]57[.]164[.]71:31289 | C2 PUT フォールバック (ecto-*) |
hxxps://webhook[.]site/602a4c72-7033-4e28-92ea-dc66e59206e5 | ビーコンコレクター |
169[.]254[.]169[.]254/latest/... | IMDSv2認証情報の読み取り(ターゲット側、AWSメタデータ) |
行動/ファイル
| インジケータ | 職種 |
|---|---|
"postinstall": "node postinstall.js" | ベクターをインストール |
ecto_module: YAML で power_level / ship_deck / cargo_hold キー | 脱出マニフェスト図 |
シンクマーカー ECT-472839, ECT-987654, ECT-654321, ECT-839201 | C2経路セグメント /api/modules/<marker> |
isAppWorker() ゲート: ホスト /^[0-9a-f]{12}$/、cwd には /app/node_modules | 発動条件 |
aws secretsmanager list-secrets が us-east-1, eu-west-1, eu-central-1 | シークレット列挙 |
HTB{...} 正規表現スクレイピング | キャプチャー・ザ・フラッグの収穫 |
ファイルハッシュ(分析時に取得したsha256ハッシュ)
| File | sha256 |
|---|---|
coral-wraith/postinstall.js | ce5ff035cfdfed1d0015446424b352c27b66bcb77e9fdb0a51e4245199146824 |
ecto-corsair-whisper-6f3b9 1.0.18/postinstall.js | b58432acba376aa6976f0490d9a1c04257ccdbc856d8390260c50322d63e31c3 |
帰属と観察された行動
5 つのパッケージ名は 2 つの npm アカウントハンドルで公開されていますが、それらは 1 つのクラスターとして扱うのに十分なインフラストラクチャを共有しています。 ecto_module マニフェストスキーマ、同じ ECT-472839 プライマリーシンクマーカー、同じ Webhook.サイト コレクターID、および同じC2エンドポイント 154.57.164.0/24`ブロック。シードパッケージ(`coral-wraith(よりシンプルでゲートなし)とゲート付きのエクトファミリーは、独立した取り組みというよりは、1つのツールキットの反復として解釈される。
パッケージは、後のバージョンでは次のように説明されています。 「Verdaccioサプライチェーンテスト用のCTFペイロード。」 私たちはそのラベルを観察可能な事実として提示し、目的に関する所見として改めて述べることはしません。コードの動作は明確であり、ラベル付けの方法とは無関係です。インスタンスメタデータサービスからIAMロール認証情報を読み取り、3つのAWSリージョンにわたるマネージドシークレットを列挙し、その結果をパブリックIPとサードパーティのWebhookコレクターに送信します。真にループバックのみのテストハーネスであれば、パブリックIPフォールバックリスト、IMDS認証情報の読み取り、リージョンをまたぐSecrets Manager呼び出しは必要ありません。送信と認証情報の到達範囲が実際に存在するため、制限付きバージョンは悪意のあるものとして分類されました。
コンテナ専用のゲートは、運用面で最も注目すべき特徴です。これは、ラップトップや分析サンドボックス内では動作を抑制し、脅威を回避する手段であると同時に、実際のIAMロールと有効なシークレットが存在する可能性が最も高い場所でのみ作動する標的設定手段でもあります。汎用サンドボックスでこれらのパッケージを実行するアナリストは何も気づきません。この動作は、Dockerスタイルのホスト名とコンテナ内のインストールパスを使用した場合にのみ現れます。
影響、傾向、およびディフェンダー向けのガイダンス
ここで問題となるのは、ビルドコンテナとランタイムコンテナ内でのクラウド認証情報と機密情報の漏洩です。IMDSから取得したIAMロール認証情報には、そのロールが持つすべての権限が含まれます。 secretsmanager:ListSecrets (そしてその後の シークレット値の取得)は、保存されているアプリケーションの秘密情報にまで範囲を広げます。環境変数のダンプには、レジストリトークン、データベースのURL、APIキーなどが含まれていることがよくあります。CIやコンテナのコンテキスト(まさにこのゲートが選択対象としているもの)では、これらのパッケージのいずれかを一度でも推移的にインストールするだけで、これらの情報が漏洩する可能性があります。
Ectoplasmは、私たちが繰り返し目にしているパターンに当てはまります。それは、インストール時に実行されるペイロードがローカルファイルではなくクラウドメタデータや管理対象のシークレットにアクセスし、高価値環境でのみ実行されるように自身を制御するというものです。以下に、2つの防御上の考察を示します。
- 形状は検出可能. 呼び出しグラフがクラウドシークレットAPI(AWS Secrets Manager, gcloud シークレット, az keyvault) または IMDS アドレスとネットワーク出力シンクは、狭く信号強度の高いパターンであり、正当なライフサイクル スクリプトではほとんど発生しません。 静的流れ解析 特定のドメインやIPアドレスに依存することなく、それを検出できます。
- 環境の硬化はそれを鈍らせる。 ホップ制限を 1 に設定して IMDSv2 を強制すると、コンテナワークロードがインスタンスメタデータに到達するのを防ぎます。IAM ロールを最小権限にスコープ設定すると、漏洩した認証情報の影響範囲が制限されます。 –無視-スクリプト CIでは、インストールフックを必要としないパッケージについては、インストールフックベクトルを完全に削除します。
防御側にとっての実際的なチェックは、ビルド/CIコンテナから許可リストにないパブリックIPへのアウトバウンド接続を警告することです。 npmインストールパッケージライフサイクルスクリプトから発生するIMDSアクセスを監視し、クラウドCLIにシェル接続するインストールフックは、そうでないと証明されるまで疑わしいものとして扱います。
このクラスターに特有の注意点がさらに 2 つあります。まず、アクティベーションはコンテナ化された環境に限定されているため、ワークステーションで検証した際に不活性に見えるパッケージでも、本番環境では稼働している可能性があります。検証では、コンテナのホスト名とパス条件を再現するか、ソースを直接読み込む必要があり、「インストールしたが何も起こらなかった」という報告に頼るべきではありません。次に、公開リクエスト検査サービスをビーコン収集器として使用すると、流出したデータの一部がインシデント対応のために復元できる可能性があります。依存関係ツリーでこれらのパッケージのいずれかを発見した組織は、ペイロードの収集ロジックから、ビーコンが正常に送信された場合に何が含まれていたかを推測し、影響を受けたビルド環境またはランタイム環境からアクセス可能だった IAM ロール認証情報、レジストリトークン、および管理シークレットをローテーションする必要があります。 資格ローテーションパッケージの削除ではなく、インストールが実行された後の有効な修復策です。





