権限昇格の「隠れた近道」を断つ:SUID/SGIDの徹底的な断捨離術
現場で数多のインシデントを見てきた経験から言わせてもらうと、サーバーが陥落する瞬間の多くは、派手なゼロデイ脆弱性ではなく、「足元に転がっていた小さな権限設定の不備」が原因だ。
特にLinuxにおける SUID (Set User ID) と SGID (Set Group ID) ビットは、攻撃者にとっては「宝の山」に見える。一般ユーザーで侵入した攻撃者が、root権限を奪取してサーバーを完全に掌握するための「踏み台」として、これほど都合の良い仕組みはないからだ。
今日は、教科書的な説明は抜きにして、実戦でどうやってこれらを洗い出し、無効化していくかという「泥臭い防衛術」を伝授する。
—
なぜSUID/SGIDが「悪魔の扉」になるのか
SUIDが設定されたファイルは、誰が実行しても「そのファイルの所有者(多くの場合root)」の権限で動作する。もし、そのプログラムに脆弱性があったり、環境変数を操作できたりすれば、攻撃者は一瞬でrootシェルを手に入れることができる。
攻撃者が狙う「お決まりのPoC」
例えば、古びた find コマンドや vim にSUIDが付与されていたとする。攻撃者は以下のようなコマンドで、あっけなくroot権限を奪う。
# SUIDが付与されたvimを悪用してrootシェルを起動する例
# 攻撃者はただvimを立ち上げ、その中でシェルを呼ぶだけだ
/usr/bin/vim.basic -c ':!/bin/sh'
これだけで、ゲームセットだ。だからこそ、サーバーの要塞化(ハーデニング)において、不要なSUID/SGIDの削除は「最初に行うべき儀式」なのだ。
—
実践:システム内のSUID/SGIDを洗い出す
まず、現状把握だ。以下のコマンドを叩いて、システム内に潜む「爆弾」をリストアップしてほしい。
# 権限昇格の可能性が高いSUID/SGIDファイルを抽出するコマンド
# 誤検知を防ぐため、システムディレクトリを中心にスキャンする
find / -perm /6000 -type f 2>/dev/null | xargs ls -la
ここで重要なのは、「なぜそのファイルにSUIDが必要なのか?」を自問自答することだ。passwd や sudo のように必要なもの以外は、すべて「不要」とみなすべきだ。
—
自動化して「セキュリティの穴」を塞ぎ続ける
手作業での確認は一度で終わりではない。パッチ適用やパッケージの追加で、いつの間にかSUIDビットが付与されることもある。以下のPythonスクリプトを定期実行(Cron)し、不審なSUIDファイルが増えていないか監視する仕組みを構築しておこう。
import os
import subprocess
def check_suid_files():
"""
システム内のSUIDファイルをスキャンし、許可リスト外があれば警告を出す
"""
# 許可するSUIDファイル(最小限に絞るべき)
allowed_suid = {'/usr/bin/passwd', '/usr/bin/sudo', '/usr/bin/chsh'}
# SUID/SGIDファイルを検索
cmd = ["find", "/", "-perm", "/6000", "-type", "f"]
result = subprocess.run(cmd, capture_output=True, text=True)
found_files = result.stdout.splitlines()
for file in found_files:
if file not in allowed_suid:
print(f"[ALERT] 未承認のSUID/SGIDファイルを発見: {file}")
# ここでSlack通知やメールを送る処理を組み込むのがベター
if __name__ == "__main__":
check_suid_files()
—
現場で守るべき「3つの鉄則」
最後に、インフラ設計者として君たちに守ってほしい鉄則を記す。
1. マウントオプションで防御する: データ領域(/home や /tmp)が別パーティションなら、/etc/fstab で nosuid オプションを付与する。これだけで、攻撃者がアップロードしたバイナリにSUIDビットを付けても無効化できる。
# /etc/fstab の例
/dev/sdb1 /home ext4 defaults,nosuid,nodev 0 2
2. 不要なバイナリは削除する: コンパイル環境(gcc 等)や、実行にSUIDを必要とする古いツールをサーバーから取り除く。攻撃者は「現地調達」ができないだけで戦意を喪失する。
3. root権限の分離: コンテナ化を進め、アプリケーションプロセスには決してroot権限を与えない。Dockerfile で USER 指定を忘れずに行うこと。
# セキュアなDockerfileの例
FROM alpine:latest
# root以外のユーザーで実行する
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
CMD ["./my-web-app"]
結び
セキュリティとは、完璧を目指すことではない。「攻撃者がコストを払ってまで突破したくなくなる環境」を作ることだ。
SUID/SGIDの整理は地味だが、攻撃者にとっては非常に嫌なハードルになる。こういった「基礎体力の向上」こそが、大規模な被害を防ぐ唯一の近道だと心得ておいてほしい。明日からの運用で、ぜひこのスキャンを組み込んでみてくれ。健闘を祈る。
コメント