こんにちは!日々の開発やインフラの管理、本当にお疲れ様です。「Dockerコンテナって便利だけど、ホストOSから中身をどうやって覗き見るの?」「セキュリティの調査って難しそう……」そんな風に感じていませんか?
今回は、インシデントレスポンス(DFIR)の現場でも非常によく使われる「Dockerコンテナのメモリフォレンジック」について、身近な例えを交えながら一歩ずつ優しく紐解いていきたいと思います。難しい専門用語が出てきても置いてけぼりにしませんので、安心して読み進めてくださいね!
—
1. 家の鍵に例える「コンテナの隔離」と「ホストの視点」
まずは、DockerコンテナとホストOSの関係を、私たちの身近な「マンションの部屋」に例えてみましょう。
- ホストOS(お父さん・お母さん、あるいはマンションの管理人さん): 全体の建物全体を把握しており、どこにどんな部屋があるかすべて見渡せる立場にあります。
- Dockerコンテナ(各部屋): 一人暮らしのワンルームです。鍵がしっかりかかっており、自分の部屋の中(コンテナ内部)からは隣の部屋が見えませんし、他の部屋に勝手に入ることもできません。
開発者や攻撃者から見ると、Dockerコンテナは「外から隔離された安全な小部屋」のように見えます。そのため、コンテナの中で悪意あるプログラム(マルウェアなど)が動いたとしても、「うちの部屋は施錠されているから大丈夫!」と思いがちです。
しかし、「マンションの管理人さん(ホストOS)」の視点に立てばどうでしょう? 管理人さんは、マスターキーを持っています。つまり、ホストOSのメモリ(記憶領域)全体をダンプ(丸ごとファイルとして保存)してしまえば、たとえ各部屋の住人が見えないように隠れていても、その足跡をすべて暴き出すことができるのです。これが、今回お話しするメモリフォレンジックの基本概念になります。
—
2. 攻撃者はどうやって「名前空間(Namespaces)」を逆手に取るのか?
Linuxのコンテナ技術を支えている重要な仕組みに、「名前空間(Namespaces)」というものがあります。これは先ほどのマンションの例えでいうと、各部屋の「すりガラス」のようなものです。自分の部屋からは外の景色が見えず、自分が世界の一番中心にいるような錯覚を起こさせます。
しかし、セキュリティインシデント(サイバー攻撃や情報漏洩)が起きたとき、私たちはこのすりガラスの向こう側を覗き見なければなりません。
なぜホストのメモリから抽出する必要があるのか?
もしコンテナが乗っ取られたとき、攻撃者はコンテナ内部のログを改ざんしたり、証拠を隠滅したりしてしまいます。コンテナの中だけで証拠を探そうとすると、すでにきれいに片付けられた部屋の床を探すようなもので、決定的な証拠が見つからないことが多いのです。
だからこそ、私たちは「ホストOS側のメモリ」を回収します。ホストのメモリには、コンテナの中で何が起きていたのか、どんなプロセスが動いていたのかという「生きた記憶」が丸ごと残されています。
—
3. 実践!ホストメモリからコンテナプロセスを特定・分離する
それでは、実際にインシデントレスポンスの現場で行われる手順を、実用的なコマンドを交えながら見ていきましょう。今回は、オープンソースのメモリフォレンジックフレームワークである Volatility 3 を使ったアプローチを想定します。
ステップ1:ホストOSのメモリダンプを取得する
まずは、被害に遭った(あるいは調査対象の)ホストOSのメモリをファイルとして保存します。実務では LiME などのツールや、クラウド環境であればスナップショット機能を使います。
# 【解説】LiMEモジュールを使用してホスト全体のメモリをファイルに出力する例です
# outputの指定先は外部ストレージや安全なネットワーク上の領域を指定します
sudo insmod lime-.ko "path=/mnt/secure_storage/host_mem_dump.lime format=raw"
ステップ2:プロセス一覧から怪しい挙動を見つける
取得したメモリダンプから、当時稼働していたプロセスの一覧を抽出します。ここでのポイントは、どのプロセスがどの「名前空間(Namespaces)」に属しているかを確認することです。
# 【解説】Volatility 3を使用して、メモリダンプからプロセス一覧(pslist)を取得し、
# 各プロセスの名前空間情報をあわせて確認するためのコマンド例です
python3 vol.py -f /mnt/secure_storage/host_mem_dump.lime linux.pslist.PsList
実行結果の中には、ホスト上で動く正当なプロセスと、Dockerコンテナ内部で動いているプロセスが混ざり合って表示されます。ここで、コンテナ特有の mnt_ns(マウント名前空間)や pid_ns(プロセス名前空間)の識別子を頼りに、怪しいコンテナ内のプロセスを絞り込んでいきます。
ステップ3:コンテナのメモリ空間だけを切り出す
特定のプロセスのPID(プロセスID)が分かったら、そのプロセスが使っていた仮想アドレス空間をダンプし、詳細な解析を行います。
# 【解説】特定した怪しいプロセスのPID(例: 12345)を指定して、
# そのプロセスのメモリ空間(プロセスイメージ)を抽出するコマンド例です
python3 vol.py -f /mnt/secure_storage/host_mem_dump.lime linux.dumpmap.DumpMap --pid 12345
こうして抽出したバイナリファイルを、さらにマルウェア解析ツールにかけたり、文字列検索(strings コマンドなど)で怪しい通信先やパスワードが含まれていないか確認したりします。
—
4. 日頃から私たちができるセキュリティの備え
「メモリフォレンジックって、事件が起きてからの話でしょ?」と思われるかもしれませんが、実は日頃のインフラ構築やコンテナ設計の工夫が、いざという時の調査スピードを劇的に変えてくれます。
1. 特権コンテナ(Privileged)を安易に使わない
- コンテナに
privileged: trueを設定してしまうと、先ほどの「すりガラス」が取り払われた状態になり、ホストOSへの逃げ道を与えてしまいます。原則として最小権限の原則を守りましょう。
2. ログの外部転送を必ず行う
- コンテナ内のファイルシステムは消えやすいため、アプリケーションログやアクセスログは必ずfluentdやSyslogなどを通して外部の安全なサーバーへリアルタイムに飛ばすように設定しておきましょう。
3. 定期的なメモリ保全の訓練をしておく
- いざインシデントが起きた時、「どうやってメモリを取るんだっけ?」と慌てないよう、ステージング環境などでダンプ取得の手順を実際に試しておくことが最高の防御につながります。
—
まとめ
今回は、Dockerコンテナのメモリ空間分離と、ホストメモリからの抽出手法について解説しました。
一見すると難解な「名前空間」や「メモリフォレンジック」も、「マンションの管理人さんと各部屋の住人」という仕組みに置き換えてみると、少し身近に感じられたのではないでしょうか?
セキュリティの世界は、一つひとつの仕組みを丁寧に紐解いていけば、決して恐ろしいものではありません。ぜひ今回の知識を、日々のインフラ運用やセキュリティ向上に役立ててみてくださいね。一歩ずつ、確実にスキルアップしていきましょう!
コメント