こんにちは!セキュリティの世界へようこそ。
日々の開発やインフラ管理、本当にお疲れ様です。「なんだかサーバーの様子が変だな」「見慣れない通信をしている気がする…」そんな不安を感じたことはありませんか?
今回は、インシデントレスポンス(DFIR)の現場で強力な武器となる「メモリフォレンジック」、そして市販のツールだけでは見つけられない厄介な攻撃を見つけ出す「カスタムプラグインの開発」について、一緒に優しく学んでいきましょう!
小難しい専門用語が出てきても大丈夫です。身近な例えを交えながら、一歩ずつ紐解いていきますね。
—
1. メモリフォレンジックってなに?(おうちの「記憶」を覗き見する技術)
皆さんは、家を出るときに鍵をちゃんとかけたか不安になったり、昨日どこに財布を置いたか忘れてしまったりした経験はありませんか?パソコンも同じです。パソコンが動いている「今」、その頭の中(メモリ=RAM)には、実行中のプログラムや開いているファイル、通信の履歴など、あらゆる「記憶」が詰まっています。
もし、泥棒(マルウェア)が家に侵入してきても、警察が来る前に証拠を隠してしまうことがあります。でも、パソコンのメモリという空間には、泥棒がどんな足跡を残したか、どんな道具(悪意あるコード)を使っていたかがリアルタイムで残されているのです。
このメモリのデータを丸ごとファイルとして保存し、中身をじっくり解析する技術をメモリフォレンジックと呼びます。
市販のツールだけでは足りない理由
「じゃあ、専用の便利な解析ツールを使えばバッチリだね!」と思いますよね。もちろん、世の中には Volatility 3(ボラティリティ・スリー)という、メモリ解析の超有名で強力なフレームワークが存在します。
しかし、巧妙な攻撃者は賢いです。彼らはセキュリティソフトや既存の解析ツールが「見つけられないようにする裏技」を使って侵入してきます。防犯カメラの死角を突くようなものですね。
そんなとき、「うちのシステム特有の、こんな悪さをする泥棒を見つけたい!」というときに役立つのが、今回学ぶ「カスタムプラグイン(自分だけの専用虫眼鏡)の自作」なんです。
—
2. Volatility 3 用カスタムプラグイン開発の基本
Pythonというプログラミング言語を使って、Volatility 3向けのプラグインを作ってみましょう。「えっ、プログラミングで作るの?」と身構えてしまうかもしれませんが、基本の型さえ覚えてしまえば、意外とシンプルなんですよ。
今回は例として、「特定の怪しい名前のプロセス(実行中のプログラム)がメモリ上に隠されていないか」を探す、シンプルなプラグインを作ってみます。
プラグインの基本構造
Volatility 3のプラグインは、基本クラスを継承(引き継ぎ)して作ります。家の設計図で言えば、「既存の立派な家ベースに、自分好みの隠し部屋を付け足す」ようなイメージです。
以下のPythonコードを見てみてください。
# 必要なモジュールをインポートします
from volatility3.framework import interfaces, renderers
from volatility3.framework.configuration import requirements
from volatility3.plugins.windows import pslist
class FindSuspiciousProcess(interfaces.plugins.PluginInterface):
"""メモリ上から特定の怪しいプロセスを探すカスタムプラグイン"""
@classmethod
def get_requirements(cls):
# このプラグインが動くために必要な前提条件を定義します(Windowsのプロセス一覧機能を利用)
return [
requirements.PluginRequirement(name='pslist', plugin=pslist.PsList, version=(1, 0)),
]
def _generator(self):
# プロセス一覧を取得して、1つずつチェックしていくメインの処理です
# pslistプラグインからプロセスリストをもらいます
for task in pslist.PsList.list_processes(
context=self.context,
layer_name=self.config['primary'],
symbol_table=self.config['pslist.symbol_table']
):
process_name = task.ImageFileName.decode('utf-8', 'ignore').strip()
process_id = task.UniqueProcessId
# 例として「evil_malware.exe」という名前のプロセスを探します
if "evil_malware.exe" in process_name.lower():
# 見つかったら、画面に出力するためのデータを渡します
yield (0, (process_id, process_name, "【警告】不審なプロセスを検出しました!"))
def run(self):
# 画面に結果を表示するためのフォーマットを指定します
return renderers.TreeGrid(
[("PID", int), ("プロセス名", str), ("ステータス", str)],
self._generator()
)
コードのポイントを優しく解説
1. PluginInterface の継承
Pythonのクラスという仕組みを使って、Volatility 3の仕組みにピタッとハマるように作っています。
2. get_requirements メソッド
「このプラグインを使うには、Windowsのプロセス一覧を見る機能(pslist)を一緒に使わせてね」と、お母さんに頼むお使いのような設定をしています。
3. _generator メソッド
メモリの中にあるすべてのプロセスを順番に覗き見して、名前が evil_malware.exe(いかにも怪しい名前ですね!)に似ていないかをチェックしています。
—
3. 現場で役立つ開発のコツと注意点
実際にインシデント(セキュリティ事故)が起きた現場でプラグインを書くとき、初心者のうちはいくつかハマりやすいポイントがあります。現場の先輩たちがよく口にする注意点をいくつかシェアしますね。
- 焦って複雑なコードを書かない
最初は、プロセス名や特定の通信ポートなど、目に見えやすい「目印(シグネチャ)」を1つだけ探すシンプルなコードから始めましょう。複雑にすると、調査の緊迫した現場でバグ(プログラムのエラー)が出てしまい、かえって時間がかかってしまいます。
- 文字コードのエラー(デコード)に気をつける
メモリ上のデータは、人間が読みやすい文字ではなく「バイト列」という機械語の数字の羅列で保存されています。これを無理やり文字に変換しようとするとエラーで止まってしまうことがあるため、上のコードのように decode('utf-8', 'ignore') を使って、読めない部分は優しくスルーする工夫が大切です。
—
まとめと次のステップ
今回は、メモリフォレンジックの基本的な考え方と、Volatility 3を使ったカスタムプラグイン開発の第一歩についてご紹介しました。
- メモリにはパソコンの「今の記憶」が残っており、隠された証拠を見つける鍵になること
- 市販のツールで発見できないものは、Pythonを使って自分専用の虫眼鏡(プラグイン)を作れること
セキュリティの勉強は、覚えることが多くて最初は圧倒されてしまうかもしれません。でも、一つひとつ紐解いていけば、決して越えられない壁ではありません。
「自分の手で怪しいプログラムを見つけ出せたときの達成感」は、この仕事の醍醐味の一つです。
まずはご自身のテスト環境で、小さなコードを動かすところから一歩ずつ始めてみましょう。あなたのセキュリティエンジニアとしての第一歩を、心から応援しています!
コメント