1. 概要
AhnLab Security intelligence Center (ASEC) では、Linux 環境を対象とするさまざまな脅威を継続的にモニタリングしている。マルウェアや侵害の痕跡を隠蔽するために Linux カーネルを改変する手法は長期間にわたって使用されており、Syslogk もこのような手法で動作するルートキットの1つである。本ブログでは、Syslogk ルートキットの主な機能と動作方式を分析するとともに、その分析結果に基づいて当社製品による検知および駆除方法について説明する。
ASEC の分析結果、一部のセキュリティ製品で検知の空白が確認された実際の Syslogk 隠蔽マルウェアに対して、AhnLab V3 Net for Linux Server 3.6.25.4 (Build 1487) は、隠蔽検知機能を迅速に反映し、実際の感染環境において隠蔽されたマルウェアを検知および駆除できるよう対応した。
2. ルートキット機能
Syslogk ルートキットは、プロセス、ネットワーク通信、ファイルを隠蔽するためにインラインフックと VFS テーブルフックを使用し、特定のカーネル API 関数の実行フローを改変する。
以下では、2つのフック方式について説明し、それぞれの方式でフック対象となる API 関数とその使用目的について記載する。
2.1. プロセス、TCP 通信の隠蔽 (インラインフック)
インラインフック手法は、API 関数のプロローグ (OP コード領域) を直接パッチすることで、元の API 関数の呼び出しを横取りし、攻撃者が定義した関数を優先的に実行する。
特定の関数のアドレスは、カーネルシンボルテーブル (/proc/kallsyms) を通じて確認し、後で使用するためにあらかじめ定義された構造体に保存する。

[図1] kallsyms を利用して特定の API 関数のアドレスを検索するコード
メモリへの書き込み保護を解除するため、CRo レジスタ (保護モードや書き込み保護など、プロセッサの動作ポリシーを制御するグローバル設定レジスタ) の Write Protect ビットを0に変更する。その後、API 関数のアドレスが含まれるメモリページの PTE (Page Table Entry) を取得し、書き込み権限を設定する。この処理により、該当ページのデータを変更できるようになる。

[図2] 書き込み権限を設定し、特定の API 関数のプロローグを改変するコード
その後、API 関数のプロローグ部分を以下のようなアセンブリコードに改変する。このようにパッチされたプロローグにより、API が呼び出された際に攻撃者が定義した関数が実行される。
| MOV RAX, [攻撃者が意図した関数のアドレス] PUSH RAX RET |
[表1] 攻撃者が定義した関数の実行例
インラインフックが適用されるカーネル関数は、プロセスを隠蔽するための proc_root_readdir と、TCP ソケットを隠蔽するための tcp4_seq_show の2つであり、それぞれの関数のフック目的は以下のとおりである。
2.1.1. プロセスの隠蔽 (proc_root_readdir)
proc_root_readdir 関数は、/proc ディレクトリ内のプロセスエントリを巡回し、登録された Callback 関数を呼び出す役割を担う。ps、top、pstree などのプロセス関連ユーティリティでプロセス一覧を表示する際に呼び出される。
フックされた状態でこの関数が呼び出されると、以下の図のように攻撃者が意図した関数が実行される。元々渡されていた正常な Callback 関数のアドレス (3番目の引数) をバックアップし、攻撃者が定義した Callback 関数を3番目の引数に挿入した後、元の proc_root_readdir 関数を呼び出す。

[図3] 既存の Callback 関数をバックアップした後、攻撃者が意図した Callback 関数が実行されるよう設定するコード
プロセス名が “was_sys_relay” である場合、または該当プロセスが親プロセスもしくは祖父母プロセスである場合は、そのエントリを返さずに隠蔽する。それ以外の PID については、正常な Callback 関数を実行して該当プロセスが表示されるようにする。

[図4] 特定のプロセスを隠蔽するコードの一部
隠蔽条件の詳細は以下のとおりである。
| 隠蔽条件 | プロセスの隠蔽有無 |
|---|---|
| PID < 2 | 非隠蔽 |
| “was_sys_relay” プロセス | 隠蔽 |
| “was_sys_relay” が親プロセスであるプロセス | 隠蔽 |
| “was_sys_relay” が祖父母プロセスであるプロセス | 隠蔽 |
| その他のプロセス | 非隠蔽 |
[表2] 隠蔽条件
2.1.2. TCP ソケットの隠蔽 (tcp4_seq_show)
tcp4_seq_show 関数は、/proc/net/tcp を読み込む際に各 TCP ソケットの情報を出力する役割を担い、netstat などの TCP 関連コマンドを実行する際に呼び出される。1つのソケットを出力するたびに1回呼び出され、1番目の引数 (seq_file 構造体) に出力データが保存されるため、攻撃者はこれを改変して特定のソケット情報を隠蔽する。
フックされた状態でこの関数が呼び出されると、まず元の関数を実行して1つのソケット情報を出力する。その後、以下の図のように特定のポート番号が含まれている場合、そのエントリを削除し、ユーザーが確認した際に該当ポートの通信を確認できないようにする。

[図5] 特定のポートが含まれている場合、そのポートを含むエントリを削除するコード
2.2. ファイルの隠蔽 (VFS テーブルフック)
VFS (Virtual File System) テーブルは、さまざまなファイルシステムを1つの共通インターフェースで処理するため、ファイルシステムの操作関数ポインタを保存する構造体である。VFS フック手法では、このような API 関数のアドレスを変更し、攻撃者が定義した関数を優先的に実行する。
攻撃者は VFS テーブルのうち readdir 関数のみをフックしており、VFS テーブル内の readdir ポインタを攻撃者が定義した関数のアドレスに変更している。

[図6] VFS テーブルフックによって readdir 関数のアドレスを変更するコード
攻撃者が改変した readdir は以下の図のとおりであり、上述した proc_root_readdir 関数と同様に、元々渡されていた正常な Callback 関数のアドレス (3番目の引数) をバックアップした後、攻撃者が定義した Callback 関数を挿入して元の readdir 関数を実行する。

[図7] Callback 関数をバックアップした後、攻撃者が定義した Callback 関数が実行されるよう設定するコード
攻撃者が設定した Callback 関数では、ファイルまたはディレクトリ名に “was-patch” という文字列が含まれている場合、そのエントリを返さずに隠蔽する。この条件に該当しない場合は正常な Callback 関数が実行され、その他のファイルやディレクトリは正常に表示される。

[図8] ファイルまたはディレクトリ名に “was-patch” という文字列が含まれている場合に隠蔽するコード
ただし、この方式ではパス全体を隠蔽するわけではないため、ls コマンドでは “was-patch” を含む項目を確認できないものの、cd コマンドを使用して該当ディレクトリにアクセスし、その配下のファイルを確認することはできる。
2.3. ルートキットの隠蔽 (LKM (Loadable Kernel Module) の隠蔽)
LKM は、実行中のカーネルに動的にロードされ、カーネル機能を拡張するコードを含むオブジェクトファイルである。Syslogk は、カーネルモジュール一覧から自身のモジュールノードを削除することで LKM を隠蔽する。
隠蔽関数が呼び出されると、まず mod_list 変数に既存のモジュールノードの位置が保存されているかを確認し、現在の隠蔽状態を判定する。まだ隠蔽されていない場合は、現在のノード位置をバックアップした後、カーネルモジュールを管理する双方向リンクリストから該当ノードを切り離す。これにより、lsmod コマンドで取得されるモジュール一覧に Syslogk が表示されなくなる。
また、/sys/module から自身の kobject ノードを削除する。state_in_sysfs は、該当オブジェクトが sysfs に登録されているかどうかを示すステータス値であり、値が1の場合は登録済みであることを意味する。ノードが削除されるとこの値は0に変更されるため、Syslogk は値を再び1に設定し、登録された状態であるかのように維持する。この処理により /sys/module からも該当モジュールを確認できなくなる。

[図9] ルートキットドライバーを隠蔽するコード
3. ルートキットへの対応
3.1. ルートキットの検知および駆除
Syslogk ルートキットが動作しているシステムでは、[図10] のように実際には存在するファイルの一部が ls コマンドの結果に表示されない。

[図10] Syslogk ルートキットによって一部のファイルが隠蔽された状態
V3 Net for Linux Server 製品で該当システムをスキャンした結果、隠蔽されたカーネルモジュールを検知し、隠蔽状態を解除できることが確認された。また、駆除完了後には、それまで隠蔽されていたファイルが再び正常に表示されることを確認できた。
これにより、一般的なシステム点検では確認が困難なカーネルレベルの隠蔽行為についても、検知および駆除することができる。

[図11] V3 Net for Linux Server による隠蔽カーネルモジュールの検知および隠蔽解除の結果

[図12] 駆除後、隠蔽されていたファイルが再び表示される状態