背景
Dockerコンテナを立てたらマルウェアに感染したのでサイバーセキュリティの啓蒙を兼ねてメモ書きしてみました。
注意事項
マルウェアに感染した被害の対処方法を記述しています。マルウェア自体の機能や解析の解説ではなく一般利用者ユーザーの視点から感染経路と対応方法についての記述になります。
マルウェア感染状況
症状
Dockerコンテナを稼働させたホストのロードアベレージ(CPU負荷)が常時4を超える状況になっていました。つまり400%でホストがフル回転してた訳ですな。
例えるならエヴァンゲリオン初号機が暴走してマヤちゃんがコンソール画面に向かって叫んでいるところです(違)
こうなるとクラウドサービスのAWSとかだと英文で警告アラートが飛んで来ますし毎日課金されで膨大な利用料金請求が来ることになります。恐ろしい!!
状況の調査
CPUの利用状況やメモリの使用量などを調査するツール類がありますので、まずはCPU負荷を極端に使用しているサービスとプロセスを調査します。
docker stats
各Dockerコンテナのリソース利用状況は「docker stats」コマンドで確認できます。
$ docker stats
実際にマルウェアに感染した状況は以下の通り。
postgresのDockerコンテナのCPU負荷が400%超えています!
該当するDockerコンテナを停止すれば解消されますが原因の調査の為に次の確認作業をします。
ps aux
ホスト側で稼働プロセスのCPU負荷状況を確認する為に「ps aux」コマンドを使用します。
$ ps aux
表示されたプロセス一覧の中からCPU負荷を極端に使用しているプロセスを探したところありました。
- 「/tmp/kinsing」
- 「/tmp/kdevtmpfsi」
この2つのプロセスがCPU負荷300%を超えていました。
kinsing(kdevtmpfsi)マルウェア
このDockerマルウェアは感染したDockerホストで仮想通貨をマイニングしてCPU負荷を掛ける仮想通貨マイニングマルウェアです。ここではマルウェア自体の機能と解析の解説は記述しません。
詳しくは以下のリンク先を参考にしてください。
- 脅威:コンテナ環境を対象としたマルウェア「Kinsing」が増加中 #AquaSecurity #セキュリティ #コンテナ #マルウェア
- コンテナを標的にしたマルウェア、Aqua Security Softwareが攻撃手法を解説
感染経路
誤って外部公開されたDocker APIが主な感染経路ですが他にも様々な脆弱性を利用して感染するので必ずしもDocker API経由とは限りません。
外部公開されたサービスポートの全てで感染経路は疑われます。
事象の分析
今回の感染状況から以下の事が推測出来ます。
- Docker APIは外部公開を有効化していない
- 勝手にDockerコンテナを作成されてはいない
- 複数あるDockerコンテナのうちpostgresのDockerコンテナだけに発生
どうやらDocker APIではなく既存のpostgresqlのDockerコンテナに原因がありそうです。
このpostgresqlコンテナは検疫された公式Dockerイメージなのでマルウェア混入は無いとは限りませんが調査の優先順位は低いです。
となると感染経路として外部公開されたサービスポートを疑うことが最優先です。
サービスポートの外部開放
Dockerコンテナではサービスポートをオプションの「-p」でホスト側にポートフォワードして外部公開します。
同一ホスト内であればサービスポートを外部公開する事はありませんが、別のホストから参照する場合にはサービスポートを外部公開します。よくあるのがwebサービスのHTTP(80/TCP)やHTTPS(443/TCP)などがそうですね。
データベースのPostgreSQLやMySQLも別のホストから参照する為に外部公開する事があります。
今回のpostgresqlコンテナは「5432/TCP」サービスポートをホスト側にポートフォワードしています。
ファイアウォール設定
一般的にサービスポートはファイアウォール設定で許可範囲を管理します。
その際にファイアウォールの設置箇所としては以下があります。
- ホストの外側
クラウドのAWSではセキュリティグループ設定、オンプレ環境ではルーター機器やファイアウォール装置 - ホスト側
Linuxのubuntuの場合はufwやiptables
一般公開するwebサービスは許可範囲を限定せずに「0.0.0.0/0」として外部開放しますがデータベースのサービスは参照元ホストをファイアウォールで限定すべきです。
今回はオンプレ環境のルーター機器でインターネット側のグローバルIPアドレスをホスト側にstatic NATしていましたのでホスト側でファイアウォール設定します。
Dockerコンテナのファイアウォール設定の失敗
ホスト側のOSはUbuntu Serverなので一般的にufwを使ってファイアウォール設定します。
ところがDockerコンテナの場合はufwの設定に関わらずiptablesにて直接設定されます。
ufwでファイアウォール設定していたつもりがiptablesで外部公開されていたのが今回の感染経路の原因の1つでした。
そんな訳で外部から制限無く「5432/TCP」サービスポートに接続出来る状況でした。
正しくUbuntuでDockerコンテナのファイアウォール設定をする方法については以下の記事を作成していますので参考にして下さい。
アカウント設定の失敗
外部公開されたサービスポートから脆弱性を突いてハッキング攻撃される事はよくあります。
今回の場合は検疫された公式Dockerイメージの最新版なので未知の脆弱性の可能性が無いとは限りませんが調査の優先順位は低いです。
それよりもサービスポートに接続してログインに成功さえすれば不正なプログラムを仕込む事は安易です。PostgreSQLのデータベースにもログイン認証が必要ですのでアカウント設定を見直します。
postgresのDockerコンテナはDocker Composeで作成していますが初期アカウント設定は以下になります。
services:
db:
image: postgres:14.5
container_name: postgres14
ports:
- "5432:5432"
environment:
- POSTGRES_USER
- POSTGRES_PASSWORD
「environment」の各変数「POSTGRES_USER」「POSTGRES_PASSWORD」でアカウント情報を渡しています。
実際のアカウント情報は別の「.env」ファイルに記述しています。
果たしてそこにあったアカウント情報はパスワードと共に「postgres」に設定していました。2つ目の原因はアカウント情報の不備でした。
これでは易々とデータベースにログインして不正なプログラムを仕掛ける事も可能でした。
マルウェアの駆除方法
今回のマルウェアが仕掛けたプログラムを単に削除しても再度感染してしまうので順を追って駆除していきます。
Dockerコンテナの停止と削除
postgresのDockerコンテナを停止して削除します。
これでホストのロードアベレージ(CPU負荷)は解消されます。
$ docker stop postgres14
$ docker rm postgres14
マルウェアの削除
ホストのロードアベレージ(CPU負荷)は解消されますが不正プログラムはまだ残っていますので以下の様に探して削除します。
$ sudo find / -name kinsing
/var/lib/docker/overlay2/b076a.../diff/tmp/kinsing
/var/lib/docker/overlay2/b076a.../merged/tmp/kinsing
$ sudo rm /var/lib/docker/overlay2/b076a.../diff/tmp/kinsing
$ sudo rm /var/lib/docker/overlay2/b076a.../merged/tmp/kinsing
$ sudo find / -name kdevtmpfsi
/var/lib/docker/overlay2/b076a.../diff/tmp/kdevtmpfsi
/var/lib/docker/overlay2/b076a.../merged/tmp/kdevtmpfsi
$ sudo rm /var/lib/docker/overlay2/b076a.../diff/tmp/kdevtmpfsi
$ sudo rm /var/lib/docker/overlay2/b076a.../merged/tmp/kdevtmpfsi
セキュリティ対策
再びマルウェアに感染しないようにセキュリティ対策をします。
正しくファイアウォールを設定する
サービスポートが意図と違って外部公開されていましたので正しくファイアウォール設定をします。
UbuntuでDockerコンテナのファイアウォール設定をする方法については以下の記事を作成していますので参考にして下さい。
ホストのサービスポートが外部公開されていないか無料で確認するサービスがSHODANなど複数ありますので利用しましょう。
別のホストからnmapコマンドを使用しても良いですね。
正しくアカウント設定する
簡単に類推できるようなパスワードではなく英数字記号を複数含む複雑で長い文字列のパスワードを設定します。
基本的なセキュリティ対策
使用しているOSやアプリケーションを常に最新版に更新して不要なサービスは停止するなど基本的なセキュリティ対策は当然行います。
しかし意図しない設定からハッキングされてサイバー攻撃を受ける事は個人や組織を問わず規模の大小に関わらず起こりえる事です。
今回の件は路傍の石かも知れませんが教訓として少しでも参考にして頂ければ幸いです。

Comments
分からなかったので質問します。
これらのファイルは、Dockerホスト側のパスだと書かれてますが、
PostgreSQLのDockerコンテナに侵入され、中で任意のコマンドを実行できると
Dockerホスト側のファイルシステムにアクセスできるのですか?
挙げられている解説サイトによれば、
spre.shはコンテナネットワーク内のほかのコンテナも感染させるようですが、
ホストまで感染させるとは書かれてないので……。
今回のケースは、感染したのはDockerホスト側PCということでしょうか。
ありがとうございます。いい質問ですね。
仮想化VM(VirtualBoxやVMWareなど)は仮想マシンをホスト上に別で作成されますが、
Dockerコンテナの場合はホスト側とリソースをオーバーレイで透過的に共有されます。
コンテナのストレージも実態はホスト側に保存されています。
そんな訳でマルウェア感染したのはDockerコンテナですがホスト側にも直接影響しますね。
初期パスワードのpostgresqlを外部公開してたら秒でログインされるのはまあ当たり前でしょうが、なんでそれがファイルシステムに不正プログラムを配置されることに繋がったのかが分かりません。
ここがよく分かりませんでした
5432ポートを介してファイルシステムに任意のファイルを置いたり、さらにそれらを実行することって容易なんでしょうか??
postgresqlに脆弱性がない限りそんなことは到底できないと思ったのですが、、
有用な記事ありがとうございます。
.env を直接閲覧はできないが、簡単なID・パスワードにしていたため、不正ログインされてしまった、という理解で合ってますでしょうか?
@sataken2さん
dockerコンテナのボリュームはホスト側に保存されます。
https://docs.docker.com/storage/volumes/
コメントの意図としては、5432ポートは単にSQLを通じてDBへのアクセスができるだけで、任意のファイルを作ったり実行することはできないのではないかという意味でした。(コンテナ内であってもホストOS上であっても)
また、ボリュームのホストサーバーでのパスは
/var/lib/docker/volumes~かと思います。記事では
/var/lib/docker/overlay2~にマルウェアがあったとのことなので、ボリュームではない気がしました・・間違ってたらすいません(仮に実行中インスタンスにマルウェアが感染した場合、コンテナインスタンスを削除することでoverlayからもファイルは消えるはずですのでそこが残っていたというのも気になりました。イメージの時点ですでに感染していたのでは?と思いました)
@LaboZeroKHさん
その理解で良いです。
@satken2さん
ありがとうございます。これは鋭い、いい質問ですね。
Dockerコンテナを削除してデータも一緒に消えたら困りますので、
Dockerではデータの永続性を確保する方法としてvolumeとbind mountと言う方法があります。
volumeコンテナはdocker範囲にありますがbind mountはホスト側ディレクトリに直接マウントされます。
今回のケースですとbind mountの方になりますね。
便利な機能ですが実はこれが大変危険な方法でしてホスト側に対して強力な権限があります。
なのでpostgresのコンテナにログイン出来た時点でアウト試合終了なんですね。
その辺の方法は書くと各方面から叱られて垢バンされる気がしますので察して頂ければ幸いです。
Dockerのストレージに関しては公式マニュアルにも記載されていますので参考にして下さい。
bind mountの注意点も記述されています。
@ohhara_shiojiri さん
dockerは長年使ってるのでボリュームもバインドも把握しておりますが、バインドマウントでなぜ/var/lib/dockerにファイルが入るのでしょうか?
まさかホストサーバーのルートディレクトリをバインドしてるなんてわけではないですよね?
@satken2
この辺を読みましょう。postgresユーザはある意味何でもできます。
https://www.postgresql.jp/document/14/html/lo-funcs.html
https://www.postgresql.jp/document/14/html/sql-load.html
@satken2 さん
投稿の本筋から少し逸脱しますが、Dockerのストレージに関する基本的な質問なので、
ここを読みに来て頂いている他の多くの方が参考になるように回答しておきます。
他意はありません。ご了承ください。これもいい質問です。
Dockerのストレージドライバのオーバーレイで保存されている実際の場所がそこになります。
仕組みとしてDockerコンテナ上で作成されたファイルや修正されたファイルはその都度レイヤー毎に保存されます。
ファイルを削除した場合も実際に削除される訳では無く別のレイヤーに移行されて削除された事にします。
なのでDockerコンテナ上で削除したはずがホスト側の空き容量を圧迫するなんて事もあり得ます(笑)
Dockerのストレージドライバーの仕組みについて以下の公式マニュアルを参考にして下さい。
@ohhara_shiojiri
いいえ、それはDockerfileでRUNコマンドを使って新たなレイヤーを加えた場合の話ですね。実行中のインスタンス内でファイルを作成して削除した場合にファイルが別のレイヤーに移行されて保存されるということはありません。
自分の認識が違うのかと思い、思わず試してしまいました。
以下のようにお手元で試してみてください。
@cheetaso
指摘ありがとうございます!これは知りませんでした。
気を付けて使わないとセキュリティホールを作り込んでしまいそうですね。
@satken2 さん
はい、そうですね。Dockerコンテナ上では削除できます。
削除の件はbind mountしたホスト側からの話でした。
publicやDMZゾーンのサーバでDocker運用はネットワーク周りをよく理解して、適切に考慮してあげないと結構な打撃をくらうことになりますよね。
今回の例だけでみるとpg_hba.confを適切に設定しておけば防げそうな気がしますが。
@yuichi_sano さん
コメントありがとうございます。
まず前提としてサービスポートを無闇に外部へオープンにしてはダメよねって事ですね。
Let's comment your feelings that are more than good