コンテンツにスキップ

sudo で実行できるプログラム

sudo -l の読み方はユーザーと sudo。このページは、許可コマンドが見つかったあと、そのコマンドが root で何を信用するかを追う。

sudo の見どころは、コマンドを実行できることより、root 実行中にどの低権限入力を信用するかである。

ターミナルウィンドウ
sudo -l
ls -la <allowed-command>
sed -n '1,240p' <script>
  • 許可コマンドが root で実行される。
  • そのプログラムが user-writable file、plugin directory、checkpoint、symlink、環境変数、相対パスを信頼している。
  • SETENV や env_keep があれば、環境変数も入力になる。
ターミナルウィンドウ
sudo <allowed-command>
sudo <allowed-command> <extra-arg>
namei -l <path-read-by-command>
find <app-dir> -maxdepth 5 -writable -ls 2>/dev/null
  • 目的: sudoers の完全一致、引数追加の可否、実行時に読まれるファイル、書ける場所を確認する。
  • 前提条件: 実行が状態変更する場合は、先にコードを読む。restart, backup, train, logs などは副作用がある。
  • 出力の解釈: 引数追加時にパスワードを要求されたら、元の NOPASSWD ルールには一致していない可能性がある。許可コマンドが root 権限で低権限ユーザーの入力を読むなら、権限昇格の候補になる。
  • 失敗した場合の切り分け: NOPASSWD がない、TTY 不足、PATH の違い、環境変数の reset、wildcard の範囲、禁止ルール。
  • 代替: sudo -V, strings <binary>, strace -f -e file <command>。strace はノイズとログを増やすため、必要時だけ使う。
  • 状態変更: sudo -l は対象を変更しないが、認証ログに残る場合がある。許可コマンドの実行による変更は、後片付けの対象として記録する。記録欄はログと検知にある。

読まれるパスの権限はファイルとディレクトリ権限とroot が読む書き込み可能パスで確認する。

許可コマンドは、次の形で権限境界になる。主張するときは、sudoers の全文、対象スクリプトの全文、実行前後の id を残す。

  • 許可コマンドだけでは root shell が出ない。別サービスが持つ状態(メンテナンス窓など)が揃って初めて処理が進む。その状態を変えるプロトコルは HTTP とは限らない。
  • Python の許可コマンドに command injection がなくても、起動時に読む plugin(.pth と site.addsitedir())が root のコード実行になる。流れは独自コードの信頼境界。
  • 禁止サブコマンドがあるルールでは、許可されているサブコマンドの実装だけを読む。
  • インタプリタとスクリプトの完全一致では、引数追加は使えない。境界はスクリプトが読むファイル側にある。
  • sudo が拒否されたら、許可コマンドの変形は打ち切る。次は localhost の root サービスである。

SUID と file capability の列挙はファイルとディレクトリ権限。プロセスの CapEff と、kernel や local exploit を使うときに残す version、namespace、capability、module はコンテナと名前空間で確認する。