
2025/12/18 6:13
I got hacked: My Hetzner server started mining Monero
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
ヘツナー VPS 上で Coolify をホストし、Next.js ベースの Umami アナリティクスを含む複数コンテナを実行していた。12 月 7 日に、Umami コンテナ内に Monero マイニングボット(
javae/xmrig)が出現し、CPU スパイクが約 15 倍に増大した。著者はマイナーをコンテナに追跡し、CVE‑2025‑66478 ― Next.js の React Server Components “Flight” プロトコルにおける不安全なデシリアライゼーション(Puppeteer を介さずリモートコード実行が可能)を特定した。HTTP リクエストを巧妙に作成することで RCE が発動し、マイナーがインストールされた。ホストファイルシステムのチェック(/tmp/.XIN-unix/javae)ではエスケープは確認できず、コンテナは非 root の nextjs ユーザーとして実行され、特権モードやボリュームマウントも無いため、すべての悪意あるプロセスは名前空間内に留まった。
著者は侵害されたコンテナを停止・削除し、CPU 負荷を通常状態へ戻した。UFW をデフォルトで受信トラフィックを拒否するよう設定し、SSH、HTTP、および HTTPS のみ許可することで、オープンな PostgreSQL / RabbitMQ ポートを効果的に遮断した。ヘツナーは 2025‑12‑17 にネットワークスキャン検知後、アブズケース警告を送付し、著者が侵害と対策を説明するとともにチケットはクローズされた。
重要な教訓として、十分に隔離されているコンテナでも基盤フレームワークに脆弱性がある場合は突破可能であり、「Next.js を使っていない」状態が第三者ツールの依存関係によって偽りになるケースがあることを指摘した。この事例は、ファイアウォールルール、非 root ユーザー設定、特権モード無し、監視・ fail2ban の導入、およびタイムリーなパッチ適用という防御層の重要性を強調した。
行動計画
- Umami を廃止する
- すべてのコンテナに対してユーザー権限とマウントを監査する
- SSH アクセスを強化し、アラートを設定する
- セキュリティパッチを定期的に適用し、将来のインシデントを防止する
本文
編集:
HN の数名の方から「この記事は少し LLM が生成したように聞こえる」と指摘されました。実際には私がパニックになって Claude と話している様子をトランスクリプト化したものですので、読みづらい点があればすみません ― 事件は本当に起きたことです!
「Next.js を使わない」っていうのは「あなたの依存関係が Next.js を使っていない」という意味ではないと知った経緯
午前8:25 – メール
Hetzner からこんなメールで目覚めました:
Dear Mr Jake Saunders,
We have indications that there was an attack from your server. Please take all necessary measures to avoid this in the future and to solve the issue.
We also request that you send a short response to us… The attached evidence shows network scanning from my server to some IP range in Thailand.
「おはよう」よりも、インフラを 4 時間以内に停止させる脅威がある方がずっと怖いですね。
背景: 私は Hetzner のサーバーで Coolify を動かしています。そこには以下のサービスがあります:
- IoT サイドプロジェクト
- このブログ
- アナリティクス
- 父親の電気工事サイト
午前8:30 – まずは…
最初にやったことは SSH でサーバーに接続し、負荷平均を確認することでした:
$ w 08:25:17 up 55 days, 17:23, 5 users, load average: 15.35, 15.44, 15.60
通常の負荷は 0.5–1.0 程度なのに、15 は明らかに異常です。
ps aux を実行すると:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND 1001 714822 819 3.6M 2464788 2423424 ? Sl Dec16 9385:36 /tmp/.XIN-unix/javae ...
javae と名付けられたプロセスが /tmp/.XIN-unix/ 内で CPU を 819 % 使用し、さらに xmrig プロセスも複数走っていました ― 文字通り暗号マイニングソフト(Monero)です。
12 月 7 日から誰かのためにマイニングしていたので、10 日間で CPU が 1000 %+ を占めていました。
調査
最初は「もうダメだ」と思いましたが、すべてのプロセスが ユーザー 1001 として実行されていることに気付きました。root やシステムユーザーではありませんでした。
$ docker ps -q | while read container; do echo "=== $container ===" docker exec $container ls -la /app/node_modules/next/dist/server/lib/ 2>/dev/null | grep xmrig done
出力:
=== a42f72cb1bc5 === drwxr-xr-x 2 nextjs nogroup 4096 Dec 17 05:11 xmrig-6.24.0
これは Umami アナリティクスコンテナです。マイニングコマンドは次の通りでした:
/app/node_modules/next/dist/server/lib/xmrig-6.24.0/xmrig \ --url auto.c3pool.org:443 \ --user <wallet> \ --pass <password> \ --donate-level 0
誰かが私のアナリティクスコンテナを乗っ取り、CPU を使って Monero をマイニングしていたのでした。
「Next.js を使わない」― その落とし穴
数日前に Reddit で CVE‑2025‑66478(Next.js/Puppeteer の重大な RCE)についての記事を見ました。すぐに「私は Next.js を走らせていない」と思って笑いました。
しかし Umami は Next.js で構築されており、脆弱性は Next.js の React Server Components (RSC) のデシリアライズ(“Flight” プロトコル)にあります。攻撃者は任意の App Router エンドポイントへ悪意ある HTTP リクエストを送ることでサーバー上でコードを実行できます。
攻撃フロー:
- 攻撃者が Umami の Next.js エンドポイントへ偽造リクエストを送信
- RSC が悪意のペイロードをデシリアライズ
- 任意コード実行
- 暗号マイナーをダウンロード・インストール
- 利益獲得
マルウェアはコンテナから抜けたか?
/tmp/.XIN-unix/javae というプロセスリストはホストパスのように見えます。もしホスト上に存在していれば、マルウェアは逃げていたことになります。
$ ls -la /tmp/.XIN-unix/javae ls: cannot access '/tmp/.XIN-unix/javae': No such file or directory
実際にはホスト上にはありませんでした。Docker は
ps aux にコンテナプロセスを表示しますが、マウント namespace で隔離されているため、ホストファイルシステムに触れることはできません。
コンテナの詳細:
$ docker inspect umami-bkc4kkss848cc4kw4gkw8s44 | grep '"User"' "User": "nextjs", $ docker inspect umami-bkc4kkss848cc4kw4gkw8s44 | grep '"Privileged"' "Privileged": false, $ docker inspect umami-bkc4kkss848cc4kw4gkw8s44 | grep -A 30 "Mounts" "Mounts": [],
マルウェアができること:
- コンテナ内で動作
- 暗号を採掘
- ネットワークスキャン(Hetzner のレポート原因)
- CPU を消費
できないこと:
- ホストファイルシステムへアクセス
- cron や systemd サービスの設置
- 再起動後も残る
- 他コンテナへ移行
- ルートキットをインストール
結果として、コンテナ隔離は機能しました。よかったです。
重要性 – Dockerfile と自動生成イメージの違い
私は自分で Dockerfile を書いています。Nixpacks のようにデフォルトで
USER root に設定される自動生成ツールは使いません。root で走っていた Reddit ユーザーのコンテナは、cron や systemd サービスを設置できたためサーバー再起動後も継続していました。
修復手順
-
侵害されたコンテナを削除
$ docker stop umami-bkc4kkss848cc4kw4gkw8s44 $ docker rm umami-bkc4kkss848cc4kw4gkw8s44 -
CPU 使用率の確認
$ uptime 08:45:17 up 55 days, 17:43, 1 user, load average: 0.52, 1.24, 4.83 -
UFW(Uncomplicated Firewall)を有効化
$ sudo ufw default deny incoming $ sudo ufw default allow outgoing $ sudo ufw allow ssh $ sudo ufw allow 80/tcp $ sudo ufw allow 443/tcp $ sudo ufw enable -
Hetzner に報告
Investigation complete. The scanning originated from a compromised Umami analytics container (CVE‑2025‑66478 - Next.js/Puppeteer RCE). The container ran as non-root user with no privileged access or host mounts, so the compromise was fully contained. Container removed and firewall hardened.
Hetzner は 1 時間以内にチケットをクローズしました。
学んだこと
-
依存関係が本当に何を使っているか確認する
「X を使わない」だけでは安全とは限らず、サードパーティツールでも X が内部で使用されている可能性があります。 -
適切に設定すればコンテナ隔離は機能する
root ユーザーを避け、privileged モードや不要なマウントをしないようにし、自前の Dockerfile を書くことが重要です。 -
高度化されたマルウェアも境界で制限される
悪意あるパスや偽装プロセス名は使われていたものの、コンテナ境界によって影響範囲が限定されました。 -
防御の重層化が重要
– 早期にファイアウォール設定
– Fail2Ban による SSH ブルートフォース対策
– CPU 負荷やネットワーク活動の監視/警告
– CVE 公開時の即時更新
今後の方針
- Umami は廃止し、Go ベースの代替(GoatCounter など)を検討中
- すべてのサードパーティコンテナを監査:ユーザー、ボリューム、アップデート状況、必要性
- SSH を鍵認証のみで強化、パスワード認証無効化、Fail2Ban を導入
- CPU 使用率・負荷平均・ネットワーク活動の適切な監視とアラート設定
- 定期的にセキュリティアップデートを実施し、脆弱性が判明したら即座にパッチまたは削除
奇妙な銀色の裏返し
本当に侵害された際にインシデントレスポンスを実践できたこと、コンテナ隔離が機能することを証明できたこと、Docker の namespace と権限境界について学べたこと、そしてデータ損失なしでインフラを強化できたこと。仕事の前に 2 時間ほど奪われただけでした――もっと悪い事態になっていた可能性もありました。
TL;DR
- Umami(Next.js ベース)に Puppeteer RCE が存在
- 攻撃され、10 日間 Monero をマイニングする暗号マイナーが稼働し、CPU 1000 %+ 使用
- コンテナは非 root でマウント無しだったため隔離に成功
- 対策:
、ファイアウォール有効化docker rm umami - 教訓:依存関係を正確に把握し、コンテナ設定を適切に行うこと。