ゾンビプロセスが語る侵害の代償
深夜の障害対応で最も恐ろしいのは、原因不明の負荷上昇だ。今回、筆者が直面したのは、まさにその悪夢を具現化したような光景だった。Kubernetesのワーカーノードで発生したゲームサーバーのラグ。調査を進めると、そこにはsystem CPUが90%、load averageが138という、サーバーが悲鳴を上げているとしか思えない異常値が待っていた。原因を特定するためにプロセスの親子関係を辿ると、そこには5,507個ものゾンビプロセスが積み上がっていた。これらはすべて、Next.jsのサーバープロセス配下で生成されたものだ。我々エンジニアにとって、ゾンビプロセスは単なるリソースの浪費ではない。それは、システムが制御不能な状態に陥り、攻撃者に完全に支配されているという「敗北の証」に他ならない。
侵害されたのは、かつて運用していたが現在は放置されていた旧Kubernetes環境だ。インターネットに公開したまま、依存パッケージの更新も監視も行われていなかったこの環境は、攻撃者にとって格好のハニーポットと化していた。調査の結果、3つのNext.jsワークロードが完全に侵害されていたことが判明した。1つはCPUを食いつぶすマイニング活動、1つは回線を勝手に切り売りするプロキシジャッキング、そして最後はリバースシェルによるバックドアの設置だ。これらは単なるスクリプトキディの悪戯ではない。攻撃者は、Next.js 15.1.0および15.3.3に存在するReact Server Componentsの脆弱性「React2Shell(CVE-2025-66478)」を正確に突き、認証なしで任意のコードを実行していたのだ。
特筆すべきは、攻撃の執拗さと機械的な効率性だ。アクセスログを精査すると、4月16日から7月25日までの間に、実に14,813件もの不審なPOSTリクエストが観測された。攻撃者は特定のアプリケーションを狙い撃ちしているわけではない。公開されているホストに対して、機械的に脆弱性スキャンを行い、応答があれば即座にペイロードを流し込む。5月12日の事例では、57,303バイトのPOSTを受信したわずか17秒後に、next-server配下で不審なプロセスが起動し、ELFファイルが配置されていた。この「自動化された侵入」の速度感は、現代のセキュリティ環境がいかに脆弱であるかを如実に物語っている。我々が寝ている間も、攻撃者のボットは休むことなく、放置された古いコンテナの隙間を探し続けているのだ。
多層化する攻撃手法と隠蔽の技術
今回の侵害で最も興味深く、かつ戦慄を覚えるのは、侵害後の「活動の多層化」である。攻撃者は侵入に成功した後、単一の目的で動くのではなく、複数のマルウェアを段階的に投入していた。ワークロードBを例に挙げると、そこにはTraffMonetizerによる帯域の収益化、Global Socketによる外部トンネル、そして遠隔管理ツールが同居していた。これらはすべて、正規のツールを悪用する「Living off the Land」の手法だ。TraffMonetizerは本来、余った帯域を共有して報酬を得る正規サービスだが、攻撃者はこれを盗んだサーバー上で実行し、他者の通信を中継させることで利益を吸い上げていた。さらに、Global Socketを用いてNATの内側から外部へTLS接続を確立し、バックドアを維持する。これらはすべて、正規の通信に見せかけることで、従来のファイアウォールやIDSの検知をすり抜ける巧妙な設計だ。
また、攻撃者は「偽装」のプロフェッショナルでもある。配置されたELFファイルは、systemd-udevdやkworkerといった、Linuxカーネルやシステム管理プロセスに酷似した名前で実行されていた。さらに、これらのファイルはUPXで圧縮されており、静的解析を困難にしている。ワークロードCに残されていたリバースシェルは、mkfifoで作成した名前付きパイプを介してnetcatへ入出力を繋ぐという、古典的だが極めて強力な手法だ。このプロセスは3月25日に設置されて以来、7月25日に発見されるまで、実に121日間も生き残り続けていた。この事実は、監視の欠如がいかに致命的であるかを突きつけている。
以下の表は、侵害されたワークロードごとの主な被害状況をまとめたものだが、これらは氷山の一角に過ぎない。攻撃者は、コンテナの書き込み層を巧みに利用し、再起動後も永続的に活動を継続させるためのスクリプトを仕込んでいた。
| ワークロード | 主な被害内容 |
|---|---|
| A | CPU占有(マイニング)、大量のゾンビプロセス生成 |
| B | TraffMonetizerによる帯域収益化、Global SocketによるRAT |
| C | リバースシェルによる永続的なバックドア設置 |
我々エンジニアは、コンテナが「使い捨て」であるという前提に甘えていないだろうか。たとえPodを再作成しても、攻撃者がコンテナイメージそのものや、永続ボリューム、あるいはCI/CDパイプラインにまで侵入していれば、被害は連鎖する。今回の事例は、単なる「古い環境の放置」というミスを超え、現代のWebアプリケーションがいかに多くの「見えないバックドア」を抱え得るかという、業界全体への警告である。
エンジニアが問われる「放置」の責任
今回の事案は、単なる「脆弱性の放置」という個人の失敗談として片付けるべきではない。これは、Kubernetesという複雑なオーケストレーション環境を運用するすべてのエンジニアが直面する「技術的負債の爆発」そのものだ。我々は、新しい技術を導入することには熱心だが、そのライフサイクルが終了した後の「安全な廃棄」については、あまりにも無防備である。インターネットに公開されたサービスは、たとえそれが個人の趣味のプロジェクトであっても、世界中の攻撃者から見れば「攻撃対象」でしかない。今回、ゲームサーバーのラグという物理的な違和感がなければ、この侵害はさらに数ヶ月、あるいは数年続いていたかもしれない。その間、筆者のサーバーは攻撃者の踏み台として、世界中のどこかのシステムを攻撃し続けていた可能性があるのだ。
明日から我々が取るべき処方箋は明確だ。まず、公開しているすべてのエンドポイントに対して、インベントリ管理を徹底すること。使わなくなったサービスは、単に停止するのではなく、ネットワークから物理的に隔離し、イメージを破棄する。次に、コンテナのセキュリティ設定を再評価すること。今回、privileged権限やhostPIDが無効であったことは不幸中の幸いだが、それでもこれだけの被害が出た。今後は、Egress通信の厳格な制限(ホワイトリスト方式)や、ランタイムセキュリティツール(Falcoなど)による異常検知の導入が不可欠だ。また、React2Shellのようなフレームワークレベルの脆弱性は、アプリケーションコードの修正だけでは防げない。依存関係の自動更新と、脆弱性スキャンのパイプラインへの組み込みは、もはや「推奨」ではなく「必須」の要件である。
最後に、読者であるあなたに問いかけたい。あなたの管理下にあるクラスタの中に、最後にログインしたのがいつか思い出せない「ゾンビ環境」は存在しないだろうか? その環境は、今この瞬間も、誰かの攻撃のためにCPUを回し、帯域を切り売りしていないと断言できるだろうか? 技術の進化は速いが、攻撃者の進化はそれ以上に速い。我々が「動いているから大丈夫」という怠惰な安心感に浸っている間に、システムは静かに、しかし確実に蝕まれている。この教訓を、単なる他山の石として終わらせるのか、それとも自らの運用体制を根本から見直す契機とするのか。その選択が、次に侵害されるのが「あなたの環境」になるかどうかの分かれ道となるだろう。


コメント