権限委譲の罠:Linuxカーネルが信頼するものと、攻撃者が悪用する盲点
インフラストラクチャのセキュリティ監査において、私が最も頻繁に目にする致命的な見落としの一つが、ファイルシステム権限の設計不備、特に SUID(Setuid) と SGID(Setgid) ビットの野放図な付与、そして一時ディレクトリのマウントオプションの不備である。
多くのジュニアエンジニアや、時としてベテランのインフラエンジニアですら、これらのパーミッション設定を「OSの基本設定」として軽く見ている。しかし、攻撃者の視点から見れば、これらは特権昇格(Privilege Escalation)のラダー(梯子)そのものだ。コンテナ化やマイクロサービスが進んだ現代であっても、ひとたびWebアプリケーションの脆弱性などを突いてコンテナやホストの初期シェル(低権限ユーザー)を奪取された瞬間、最初に彼らが実行するスクリプトは、決まって「SUIDファイルの列挙」である。
今回は、Linuxカーネルのプロセス実行モデルにおける根本的な挙動を踏まえながら、不要なSUID/SGIDの排除と、/tmp や /var/tmp に対する noexec / nosuid の強制が、なぜ防衛ラインの最後の砦となるのか、そのアーキテクチャと実践的な硬化(ハーデニング)手法を解説する。
—
1. SUID/SGID機構の低レイヤ挙動と特権昇格のメカニズム
カーネル空間における実効ユーザーID(euid)の書き換わり
Linuxカーネルがプロセスを実行する際、システムコールである execve() が呼び出される。このとき、カーネルは対象のバイナリに SUID ビットが立っているかどうかを検証する。
もし SUID ビットが有効であり、かつ所有者が root(UID 0)であれば、カーネルはプロセスを起動したユーザーの権限(リアルUID: ruid)に関わらず、プロセスの実効ユーザーID(euid)を 0 に昇格させる。これが、通常の一般ユーザーが通常は実行できないシステム管理コマンド(例えば passwd など)を実行できる理由である。
しかし、この仕組みは「プログラムの設計にバグがない」という性善説に基づいている。もしそのバイナリに、以下のような脆弱性が潜んでいた場合どうなるか?
- バッファオーバーフロー(Stack/Heap Buffer Overflow): 入力値の検証不足により、スタック上のリターンアドレスが書き換えられ、攻撃者の任意のシェルコードへ制御が移る。このとき、プロセスの
euidは0であるため、生成されるシェルも当然root権限となる。 - 環境変数の悪用(
LD_PRELOADやIFSの操作): 脆弱なSUIDバイナリが内部で外部コマンドを相対パスで呼び出している場合や、セキュアでないライブラリのロードを行っている場合、攻撃者は特権コンテキストで任意のコードを実行させることが可能になる。
GTFOBinsに代表される「既製兵器」の悪用
現実のインシデントにおいて、攻撃者が自作のゼロデイexploitを使うことは稀だ。彼らは GTFOBins のようなオープンソースのデータベースを参照し、OSに標準インストールされている正当なバイナリ(例: find, vim, less, tar など)に誤って付与されたSUID権限を悪用する。
例えば、find コマンドに SUID が付与されている環境は、攻撃者にとって「お墨付きのバックドア」を手に入れたも同然である。以下のコマンド一発で、root権限のシェルが容易に取得されてしまう。
# SUIDが付与されたfindコマンドを悪用してrootシェルを起動する例(攻撃者の視点)
./find . -exec /bin/sh -p \;
この背景にあるのは、「便利だから」「昔からの慣習で」という理由で放置された不必要、かつ過剰な特権の付与に他ならない。
—
2. 不要なSUID/SGIDバイナリの探索と厳格な監査
インフラストラクチャのハーデニングにおいて、まず行うべきは「システム全体のSUID/SGIDファイルの棚卸し」である。ディストリビューションのデフォルトで必要な最小限のSUIDファイル(passwd, sudo, su, mount など)を除き、サードパーティ製アプリケーションや開発者が独自にビルドしたバイナリに付与されたSUIDは即座に剥奪すべきである。
以下のシェルスクリプトは、システム全体からSUID/SGIDビットが有効なファイルを効率的に検出し、ログに出力するための監査スクリプトの実例である。
#!/bin/bash
# ==============================================================================
# スクリプト名: audit_suid_sgid.sh
# 目的: システム内の不要なSUID/SGIDファイルを検出し、レポートする
# 実行権限: root
# ==============================================================================
REPORT_FILE="/var/log/security_suid_audit_$(date +%Y%m%d).log"
echo "=== SUID / SGID ファイル監査レポート: $(date) ===" > "$REPORT_FILE"
echo "--------------------------------------------------------" >> "$REPORT_FILE"
# ルートファイルシステム以下を走査し、SUID または SGID が設定されているファイルを抽出
# ※ リンク切れやデバイスファイルのエラーを抑制するため 2>/dev/null を付与
find / -xdev \( -perm -4000 -o -perm -2000 \) -type f 2>/dev/null | while read -r filepath; do
# パーミッション、所有者、グループ、ファイルパスを取得
file_info=$(ls -l "$filepath")
echo "$file_info" >> "$REPORT_FILE"
done
echo "--------------------------------------------------------" >> "$REPORT_FILE"
echo "監査が完了しました。レポート出力先: $REPORT_FILE"
# 危険な既知のバイナリが含まれていないか簡易チェック(例として一部を列挙)
echo "=== 危険なバイナリの検出チェック ===" >> "$REPORT_FILE"
for bin in /bin/vi /usr/bin/vi /bin/vim /usr/bin/vim /usr/bin/find /bin/nc /usr/bin/ncat; do
if [ -f "$bin" ]; then
permissions=$(stat -c "%a" "$bin")
# パーミッションの先頭が 4 または 2(SUID/SGID)である場合
if [[ "$permissions" =~ ^[24] ]]; then
echo "[警告] 危険なバイナリに特権ビットが付与されています: $bin (Permissions: $permissions)" >> "$REPORT_FILE"
fi
fi
done
SUID/SGIDの剥奪コマンド
監査の結果、不要であることが判明したファイルから特権ビットを剥奪するには、chmod コマンドを使用する。
# 特定のファイルからSUIDビットを削除する
chmod u-s /path/to/unnecessary/binary
# 特定のファイルからSGIDビットを削除する
chmod g-s /path/to/unnecessary/binary
—
3. /tmp と /var/tmp の要塞化:noexec, nosuid, nodev の強制
ファイルシステム権限の厳格化において、SUID/SGIDの削除と同等以上に重要なのが、動的な一時ディレクトリ(/tmp, /var/tmp, /dev/shm)に対するマウントオプションの制限である。
なぜ一時ディレクトリが狙われるのか?
Webアプリケーションやデータベース、APIサーバーなどの脆弱性(例: ファイルアップロード脆弱性、リモートコード実行など)を突いた攻撃者が、最初にマルウェアやペネトレーションテスト用のツール(バックドア、エスカレーション用スクリプトなど)を設置する場所は、決まって書き込み権限が誰にでもある /tmp や /var/tmp である。
これらのディレクトリに実行権限(exec)が残っていると、攻撃者は外部から持ち込んだ悪意あるバイナリやスクリプトをその場で直接実行できてしまう。
必須のマウントオプション
このリスクを根絶するため、/etc/fstab を適切に設定し、一時ディレクトリに対して以下のオプションを強制する。
1. nosuid: このファイルシステム上にあるSUID/SGIDビットを完全に無効化する。仮に攻撃者がSUIDファイルを持ち込んでも、特権昇格には利用できない。
2. noexec: このファイルシステム上にあるバイナリやスクリプトの直接実行を禁止する(Permission denied となる)。メモリ上での動体ロードや一部のスクリプトインタプリタの挙動に影響を与える可能性はあるが、セキュリティの観点からは極めて強力な防衛策である。
3. nodev: デバイスファイル(ブロックデバイスやキャラクターデバイス)の作成を禁止する。これにより、デバイスドライバを介したカーネル空間への不正アクセスを防ぐ。
/etc/fstab の設定例と実装手順
既存の /tmp がルートファイルシステムの一部として直属している場合、別パーティションとして切り出すか、あるいは tmpfs としてメモリ上に安全にマウントし直すのがモダンなアプローチである。
以下は、tmpfs を用いて /tmp をセキュアにマウントする /etc/fstab の設定例である。
# /etc/fstab の設定例
# tmpfsを用いて /tmp をマウントし、同時に厳格なセキュリティオプションを付与する
tmpfs /tmp tmpfs defaults,rw,nosuid,nodev,noexec,relatime,size=2G 0 0
もし /var/tmp が独立したパーティション、あるいはLVMボリュームとして存在する場合は、/etc/fstab を以下のように修正する。
# /var/tmp のマウントオプション設定例(既存のブロックデバイスを使用する場合)
UUID=xxxx-xxxx-xxxx-xxxx-xxxx /var/tmp ext4 defaults,rw,nosuid,nodev,noexec,relatime 0 0
設定を反映させるためには、システムを再起動するか、あるいは以下のコマンドで再マウントを実行する。
# /tmp を tmpfs として再マウント(既存のプロセスがファイルを開いている場合は注意が必要)
mount -o remount /tmp
# 設定が正しく適用されているか確認
mount | grep -E '/tmp|/var/tmp'
この確認コマンドを実行した際、出力結果に nosuid,nodev,noexec がしっかりと含まれていることを確認してほしい。これが確認できて初めて、一時ディレクトリ経由の第一波の侵攻を防ぐ防壁が完成したと言える。
—
4. チーフセキュリティオフィサーからの提言:多層防御の徹底
ファイルシステム権限の厳格化(SUID/SGIDの最小化、一時ディレクトリの noexec/nosuid 化)は、単なる「CISベンチマークのチェックリスト消化」ではない。これは、万が一アプリケーション層やネットワーク層の境界防御が突破され、内部ネットワークへ侵入された「最悪のシナリオ(Assume Breach)」において、攻撃者の横方向への移動(Lateral Movement)と特権昇格を極限まで遅延させるための、極めて実効性の高い「遅延型防衛(Delay-based Defense)」である。
セキュリティとは、単一の堅牢な壁を作る事ではなく、幾重もの防御層(Defense in Depth)を重ねることで、攻撃コストを無限に跳ね上げることだ。今夜、あなたの管理するLinuxサーバーの /tmp と SUIDファイルを今一度見直してほしい。そこに「不要な信頼」が残されていないことを、プロフェッショナルとして祈る。
コメント