ルートキットと特権昇格の原理
なぜルートキットは見えなくなり、一般ユーザーが root を奪えるのか。フック・DKOM による隠蔽、setuid 悪用とカーネル脆弱性の昇格経路、整合性測定とブート検証による対抗を原理から押さえ、侵害の検知と防御設計の勘所をつかむ。
- 1.ルートキットの本質は「隠蔽」。ユーザランドは共有ライブラリやコマンドの差し替え、カーネルルートキットは syscall テーブル等のフックや DKOM(カーネル構造体の直接改ざん)でプロセス・ファイル・接続を観測APIから消す。
- 2.特権昇格の典型経路は二系統。setuid バイナリの悪用(PATH/環境変数・シンボリックリンク・引数注入)と、カーネル脆弱性(UAF・整数オーバーフロー)でカーネル権限のコード実行に至り cred 構造体を書き換える経路。
- 3.対抗の主軸は事前防御より「検知不能化の阻止」。IMA による実行前のハッシュ測定、署名カーネルと lockdown、Secure Boot からの信頼の連鎖で、改ざんされたコードがそもそも特権を得て常駐できないようにする。
ルートキットの目的は「権限の奪取」ではなく「隠蔽」
ルートキット(rootkit)という語は誤解されがちですが、その本質は権限を奪うことではありません。すでに得た特権を使って自分自身と攻撃活動を観測から消すことが目的です。攻撃の流れは通常「(1) 初期侵入 → (2) 特権昇格 → (3) ルートキット導入による永続化・隠蔽」と分かれ、ルートキットは三段目を担います。だから「特権昇格」と「ルートキット」は別概念であり、本記事では両方を扱います。
隠蔽の対象は典型的に、攻撃者のプロセス、ファイル、ネットワーク接続、ログインセッション、そしてルートキット自身のモジュールです。これらを管理者の ps / ls / netstat から見えなくするには、情報がユーザーに届く経路のどこかに割り込んで結果を改竄する必要があります。割り込む層がどこかで、ルートキットは大きく二種類に分かれます。
ユーザランドルートキット:観測経路の差し替え
ユーザランドルートキットはカーネルに触れず、ユーザー空間の観測経路を改竄します。代表的な手口は三つです。
| 手法 | 改竄対象 | 隠蔽の仕組み |
|---|---|---|
| バイナリ差し替え | ps, ls, netstat 等の実体 | コマンド自身を、特定の名前を結果から除くよう改造した版に置換 |
| 共有ライブラリフック | libc の readdir, open 等 | LD_PRELOAD やローダ改竄で関数を横取りし戻り値を間引く |
| 設定・PATH 操作 | プロファイル, /etc/ld.so.preload | 正規ツールより先に偽ツールを読ませる |
最も洗練された形は共有ライブラリの関数フックです。たとえばディレクトリ列挙の readdir を横取りし、攻撃者のファイル名に一致するエントリだけ呼び出し側に返さないようにすれば、その実装に依存する ls も GUI ファイラもまとめて欺けます。LD_PRELOAD 環境変数や /etc/ld.so.preload に悪意あるライブラリを登録すると、動的リンカが正規 libc より先にそれを解決するため、フックが成立します。
ユーザランドルートキットは速く導入でき、カーネルをクラッシュさせない利点がある一方、原理的にカーネルの真実から逃れられません。/proc を直接読む、静的リンクされた検査ツールを使う、別ホストから当該マシンのカーネル状態を観測する、といった手段でフックを迂回されると、隠蔽が剥がれます。だから本格的な永続化を狙う攻撃者はカーネル層を狙います。
カーネルルートキット:フックと DKOM
カーネルルートキットはカーネルの権限で動くため、ユーザー空間のどんな検査ツールよりも「下」に位置します。観測結果はすべてカーネルを経由するので、ここを掌握すれば隠蔽はほぼ完全になります。手段は大きくフックと DKOM の二系統です。
フック(hooking) は、カーネルがシステムコールを処理する経路に割り込みます。古典的には sys_call_table(システムコール番号から処理関数へのジャンプテーブル)の該当エントリを、攻撃者の関数アドレスに書き換えます。たとえば getdents(ディレクトリ読み取り)のエントリを差し替え、本来の関数を呼んだ後に攻撃者のファイル名を結果バッファから削ってユーザーへ返せば、すべてのプロセスに対して一貫してファイルを隠せます。近年は VFS の関数ポインタや ftrace の仕組みを悪用するフックも一般的です。
DKOM(Direct Kernel Object Manipulation) はさらに直接的で、カーネルが管理するデータ構造そのものを書き換えます。Linux のプロセスは task_struct の双方向連結リストで管理されますが、攻撃者のプロセスの task_struct を**リストから外す(前後のノードのポインタを繋ぎ替えて自分を飛ばす)**と、プロセス列挙はそのプロセスを見落とします。重要なのは、スケジューラは別のたどり方(実行キュー)でそのプロセスを参照し続けるため、列挙からは消えても実行は続く点です。
DKOM によるプロセス隠蔽(task_struct のアンリンク)
隠蔽前: ... <-> [A] <-> [TARGET] <-> [B] <-> ... ← 列挙でTARGETが見える
隠蔽後: ... <-> [A] <----------> [B] <-> ... ← 列挙でTARGETを飛ばす
(TARGET 自体は破棄せず、scheduler の実行キューには残す)
フックは「テーブルに想定外のアドレスが入る」ため、正規アドレス範囲との比較で検知しやすいのに対し、DKOM は正規の操作(ポインタ書き換え)しか行わないため検知が困難です。ただし無痕跡ではありません。プロセスを連結リストから外しても、スケジューラの実行キューやメモリ上の task_struct の実体は残るため、列挙経路の異なる二つのカーネルビューを突き合わせる(クロスビュー検知)と不整合が露呈します。メモリフォレンジック(Volatility 等)はこの原理で隠蔽プロセスを暴きます。
特権昇格の典型経路
ルートキットをカーネルに載せるにも、まずカーネル権限や root が必要です。一般ユーザーがそれを得る経路は、おおむね二系統に整理できます。
1. setuid バイナリの悪用
setuid ビットの立った実行ファイルは、起動したユーザーではなくファイル所有者の権限で動きます(典型的には root 所有の passwd や ping)。これは 最小権限 の原則に反する強力な仕組みで、実装にわずかな隙があれば昇格の踏み台になります。代表的な落とし穴は次の通りです。
- PATH / 環境変数依存:setuid バイナリが絶対パスでなく
system("cmd")のように外部コマンドを呼ぶと、攻撃者がPATHを操作して同名の偽コマンドを root 権限で実行させられる。 - シンボリックリンク・TOCTOU:ファイルの存在確認(check)と実際の操作(use)の間に攻撃者がリンクを差し替え、root 権限で別ファイルを書かせる競合(time-of-check to time-of-use)。
- 権限のドロップ忘れ:作業後に
setuidで実効 UID を戻すべき場面で戻さず、子プロセスへ root 権限が漏れる。 - GTFOBins 的悪用:本来限定機能の setuid バイナリ(エディタ・ページャ等)が持つシェル脱出やファイル書き込み機能を使い、root シェルを得る。
2. カーネル脆弱性による昇格
setuid 経路が塞がれていても、カーネル自体の脆弱性を突けば一気にカーネル権限へ到達できます。攻撃者の最終目標は多くの場合、自プロセスの資格情報構造体 cred の書き換えです。Linux ではおおむね commit_creds(prepare_kernel_cred(0)) 相当の操作で、実行中プロセスの UID/GID をすべて 0(root)に書き換えられます。ここに至る土台になるのが、メモリ破壊系の脆弱性です。
| 脆弱性クラス | 昇格に使われる理由 | 関連解説 |
|---|---|---|
| Use-After-Free | 解放済みカーネルオブジェクトを再確保し、関数ポインタや cred を制御下に置く | メモリ破壊の典型 |
| 整数オーバーフロー | サイズ計算を破綻させ、想定外の領域へ書き込ませる(ヒープ溢れの起点) | 境界チェックの回避 |
| 競合状態(race) | Dirty COW 等、同一資源への並行アクセスで保護を回避し書き込み | TOCTOU と同根 |
| 型混同 / OOB | オブジェクトを別型として扱わせ、隣接データを上書き | メモリ安全性違反 |
これらは メモリ破壊系エクスプロイト の延長線上にありますが、ユーザー空間ではなくカーネル空間で成立する点が決定的です。カーネルにはユーザー空間のような明確な特権境界の外側がないため、カーネル内での任意コード実行は事実上の最高権限を意味し、その時点で cred 書き換えもルートキット導入も自由になります。カーネルは SMEP/SMAP・KASLR・スタックカナリア等の緩和で攻撃を困難にしますが、原理的には情報漏洩で KASLR を崩し、書き込みプリミティブで cred や関数ポインタを狙う構図は変わりません。
対抗:整合性測定とブート検証
ルートキットへの根本的な対抗は、ウイルス対策ソフトのような「事後スキャン」ではありません。ルートキットはまさにそのスキャン経路を無力化するために存在するからです。発想を変え、改竄されたコードがそもそも特権を得て常駐できないようにするのが正攻法です。鍵は二つあります。
第一に 整合性測定(integrity measurement)。Linux IMA(Integrity Measurement Architecture)は、実行ファイルやカーネルモジュールがロードされる直前にハッシュを計算して測定します。さらに IMA-appraisal を有効にすると、署名や既知の良ハッシュと照合し、一致しないものの実行を拒否できます。測定値は TPM の PCR へ拡張(extend)して累積記録に残せるため、後から「何が起動したか」を遠隔証明で問えます。改竄されたバイナリはハッシュが変わるため、検証段で弾かれるか、少なくとも証跡に残って隠せません。
第二に 信頼の連鎖(chain of trust)。セキュアブート はファームウェアから OS カーネルまで各段の署名を検証し、未署名・改竄カーネルの起動を阻止します。これにカーネルの lockdown モードと署名モジュールの強制を組み合わせると、署名されていないカーネルルートキットはそもそもロードできず、/dev/mem 等からのカーネルメモリ直接書き換え(DKOM の足場)も塞がれます。電源投入直後の信頼の根から一段ずつ信頼を伸ばすこの構造が、ユーザー空間のあらゆる検査ツールより「下」に居座ろうとするカーネルルートキットへの最も効く防御です。
ルートキットの本質は「特権奪取」ではなく「隠蔽」だと区別できること。ユーザランド型は libc フック(LD_PRELOAD)、カーネル型は sys_call_table 等のフックと DKOM(task_struct のアンリンクで列挙から消すが実行は継続)の違いを説明できること。特権昇格は setuid 悪用(PATH・TOCTOU・引数注入)とカーネル脆弱性(UAF 等で cred 書き換え)の二系統。対抗は IMA による実行前測定・appraisal、署名カーネルと lockdown、Secure Boot の信頼の連鎖、が核心です。
まとめ
ルートキットの本質は隠蔽であり、観測情報がユーザーへ届く経路への割り込みで成立します。ユーザランド型は共有ライブラリのフック(LD_PRELOAD)で readdir 等を横取りし、カーネル型は sys_call_table などのフックや DKOM による task_struct のアンリンクで、プロセス・ファイル・接続を観測 API から消します。DKOM は正規操作しか行わないため検知が難しい一方、列挙経路の異なるビューを突き合わせるクロスビュー検知で露呈します。
それを載せる前提となる特権昇格は、setuid バイナリの悪用とカーネル脆弱性の二系統に大別され、後者は最終的に cred 構造体の書き換えでプロセスを root 化します。最も効く対抗は事後スキャンではなく、**整合性測定(IMA / TPM)**とブート検証による信頼の連鎖で、改竄コードが特権を得て常駐すること自体を阻止する設計です。
セキュリティ Article
ルートキットと特権昇格の原理を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
ルートキット
比較で見る軸
難易度: advanced / カテゴリ: セキュリティ / タグ数: 6
導入後に効く点
特権昇格の典型経路は二系統。setuid バイナリの悪用(PATH/環境変数・シンボリックリンク・引数注入)と、カーネル脆弱性(UAF・整数オーバーフロー)でカーネル権限のコード実行に至り cred 構造体を書き換える経路。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- セキュリティ
- タグ数
- 6
判断チェックリスト
- 自社の用途が「ルートキット / 特権昇格」に近いか確認する。
- 強みである「ルートキットの本質は「隠蔽」。ユーザランドは共有ライブラリやコマンドの差し替え、カーネルルートキットは syscall テーブル等のフックや DKOM(カーネル構造体の直接改ざん)でプロセス・ファイル・接続を観測APIから消す。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。