世界を動かす技術を、日本語で。

Linuxの能力の再考

概要

  • LinuxのCapabilities は、root権限を細分化し、必要最小限の特権付与を可能にする仕組み。
  • setcapコマンド でバイナリ単位に権限を設定可能、攻撃者による悪用リスクも存在。
  • getcapやLinPEAS 等のツールで、設定済みCapabilitiesの検出・監査が重要。
  • Capabilitiesは SUID/SGIDに代わる新たな権限管理リスク として注目されている。
  • 監査・運用時には getcapによる全ファイルのチェック が推奨。

Linux Capabilitiesの基礎

  • Capabilities とは、従来のroot(スーパーユーザー)権限を 細かい単位 に分割したアクセス制御方式。
  • 各プロセスやバイナリに 必要な権限のみを付与 し、最小権限での運用を実現。
  • 例: 特権ポートへのバインド権限だけ を持つプロセスなど、柔軟な権限設計が可能。

Capabilitiesの確認方法

  • サポートされているCapabilities数/proc/sys/kernel/cap_last_cap で確認。
  • capsh --print コマンドで、現在のシェルやプロセスのCapabilitiesセットを一覧表示。
  • 各Capabilityは 特定の管理操作 に紐づいている(例:cap_setuid、cap_net_bind_service等)。

setcapによる権限付与と悪用例

  • setcapコマンド でバイナリにCapabilitiesを付与可能。
    • 例:setcap cap_setuid+ep /usr/bin/python3.12 でPythonにUID操作権限を付与。
  • e(Effective)/p(Permitted)フラグで 有効/許可 状態を指定。
  • 攻撃例: 一般ユーザーがcap_setuid権限付きのPythonでrootシェルを取得 可能。
    • SUIDビット変更不要、バイナリ自体の改変も不要なため、 ステルス性の高いバックドア 実現手段。

Capabilities設定ファイル・バイナリの探索

  • 従来の SUID/SGID探索 だけでなく、Capabilities付与バイナリの検出も必須。
  • getcap -r / で全ファイルシステムを再帰的に調査可能。
    • 例:/usr/bin/python3.12 cap_setuid=ep
  • /proc/[pid]/status でプロセスごとのCapability状態確認。
  • capsh --decodegetpcaps [PID] でCapability値のデコード・表示。

Capabilitiesの削除

  • setcap -r [バイナリパス] でCapabilitiesを削除。
    • 例:setcap -r /usr/bin/python3.12

LinPEASによる自動監査

  • LinPEAS は、Capabilities探索やSUIDファイルチェックを自動化。
    • 現在シェル・親プロセスのCapabilities確認
    • getcapによる全ファイル調査

Capabilitiesの監査・検知ルール

  • Elastic 等のSIEM製品でsetcapコマンド利用の監視ルールを設定可能。
  • 例:setcapコマンド実行を検知し、特定パスやプロセス名を除外条件に指定。

Capabilitiesの格納場所と確認方法

  • CapabilitiesやACL等の拡張属性 は、ファイルのinode内に格納。
  • lsでは表示不可、getcapやgetfattrで確認。
  • getfattr -d -m - [ファイルパス] で全拡張属性を一覧表示。
    • security.capability属性がCapabilities情報。

まとめ

  • SUID/SGIDチェックと同様に、Capabilitiesの監査も現代の必須セキュリティ対策
  • Capabilitiesは 柔軟かつステルス性の高い権限付与手段 であり、悪用リスクも高い。
  • getcap等のツールで定期的な全ファイル監査 を推奨。
  • /etc/security/capability.confやサービスのAmbientCapabilitiesなど、 ユーザー/サービス単位の設定項目にも注意

参考資料

  • Capabilities – Linux Privilege Escalation
  • Linux Capabilities

Hackerたちの意見

共有されたグローバルネームスペースがあると、まともな能力ベースのセキュリティシステムを構築するのがかなり難しくなるんだよね。自分が持っているアクションのセットに限定されたネームスペースと、子供が親の持っている能力にしかアクセスできない階層的な能力が必要なんだ。プログラムの抽象化を考えるのが簡単になるように、こういう入れ子の能力の階層があると、システムのさまざまな部分の特権について考えやすくなる。今のところ、誰も何が何にアクセスできるのかをうまく理解できないスープみたいになってる。

そんなに細かくなくても、OpenBSDのプレッジは本当にうまくできてると思う。それに続いて、FreeBSDのカプシカムに投票するかな。

これは単にrootが持っている特権を細分化するためのフラグのセットに過ぎないけど、以前のものよりは改善されてるかもしれない。ただ、AS/400やiAPX 432での「能力」が特権が付随するシステムオブジェクトへの参照だった本当の能力ベースのセキュリティとは全然違うよね。POSIXライクなシステムにこれを取り入れることは可能だと思う。高校のときにVMSオペレーティングシステムを使ったVAX-11/730を思い出すよ。プロセスが持てる特権の長いリストがあって、「特権A、B、Cを持っていればSETPRVを取得してマシンを乗っ取れる」みたいなパスを調査するのが一般的なゲームだった。

Linuxの能力は、従来のオブジェクト能力ベースのセキュリティの概念とはあまり関係がないよ。Appleの権利に近いけど、バイナリに付随するのではなく、フォークを通じて継承される感じ。

あなたが説明しているようなものを既存のカーネルに追加するのは無理だと思う(Linuxが能力を使ったように)。最初からそのように設計しなきゃいけない。デザインによってその種の能力を探求している実験的なOSについて読んだことがあるけど、ほぼタイプ1ハイパーバイザーのプロセス版みたいな感じだね。

子供が親の持っている能力にしかアクセスできない そのルールに従わなくても使えるのが能力のいいところなんだよね。プログラムが自分の作成以外のソースから能力を得ることを許可するプロセスがなければ、プログラム名をACLに追加する方がいいよ。

名前空間は実質的に能力だってことを覚えておいてね。

面白いですね、シェアしてくれてありがとう。

細かいACLに対する問題点は、意図せずセキュリティリスクを追加する可能性があることなんだ。細かいコントロールが追加の特権を得るために悪用されることがあるからね。何かにrootを与えたら、その意味がわかるし、すごく気をつける。でも、何かに許可Xを与えたと思って安全だと思ってたら、それが許可Yやrootを得るために使われることがあって、知らず知らずのうちに危険にさらされることになる。細かい許可を守るためには、もっと大きな表面積を守らなきゃいけないし、各細かい許可がそんなふうに悪用されないようにしなきゃいけないんだ。

これが機能するためには、Linuxはキャパシティを管理するための集中管理の方法が必要だね。ファイルをレビュー(またはdiff)して、ACLをあちこち見なくても、何が変わったのかすぐにわかるようにするべきだと思う。従来のUnixの/etc/groupスタイルみたいに。

細かい権限を持っているか、粗い権限を持っているかに関わらず、全てがチャレンジだよね。粗い権限だとプロキシが必要になるけど、ビジネスロジックに関しては自由にできるのがいいところ。ただ、プロキシやクライアントコードを書かなきゃいけないし、それを維持・拡張し続けるのは面倒だよね。どちらにしても、昇格ベクトルを探すために監査や静的解析をしなきゃいけないし、細かい権限だとそれが厳しくなるけど、克服できないわけじゃない。だから、細かい権限の方が勝つと思う。RBACスタイルとSMACKスタイルのラベル付きセキュリティの中間的なものを実装したんだけど(論文を発表する承認を得ようとしてる)、アプリケーションレベルの認可に使えるんだ。これ、すごく速くて、もし誰かがカーネルで動かしたいなら、そこでも動くはず。多くの異なるラベル(細かい権限)や少数のラベル(粗い権限)を使ってアプリケーション(またはカーネル)オブジェクトにラベルを付けることで、認可を細かくしたり粗くしたりできるシステムなんだ。

それは本当にその通りだね。参考文献に挙げられている別の記事にも、安全でない権限委譲の例が載ってるよ。

Linuxの能力に対するアプローチは期待外れだと思うし、真の能力ベースのシステムにはほど遠いよ。例えば、プログラムに特権ポートをバインドする能力を渡せるけど、特定のポートには渡せない。こういう場合、ポートにバインドされたfdを渡す方がずっとシンプルで安全だよ。他の能力も粗すぎるし、能力が暗黙的に継承されるのもセキュリティ的に良いアプローチとは思えない。後方互換性のためだろうけど、能力は明示的に渡されるべきだし、プロセス間で転送できるべきだと思う。実際、fdを能力のハンドルとして使う方がずっと明確で明示的なアプローチになると思う。

ファイルへのアクセスにfdを使うのは、Linuxが能力モデルに従っているところの一つだよ。偶然ではなく、これは攻撃のターゲットとしてはあまり良くないLinux APIの一部なんだ。

もしそれがFILE*/fdのようにオブジェクトとして渡されないなら、それは本当の能力とは言えない。ただの(きらきらした)細かい環境権限に過ぎないけど、価値はあるからね。

これはその機能の目的を誤解してるよ。ポイントは、「能力ベースのシステム」をゼロから設計することじゃなくて(LSM/selinux/apparmorを参照)、setuidが伝統的に使われてきた「もの」をより細かく分割することだったんだ。setuidバイナリはすでに存在していて、APIの変更なしでそれを(もっと)安全にする手段だったんだよ。

10年から15年くらい前に、ある人たちがLinuxディストリビューションのさまざまなバイナリからsetuidをすべて排除しようと盛り上がってた気がする。Linuxのファイルxattr能力を使ってね。でも、結局これがあまり良いスキームじゃないってことが明らかになった。多くの能力がフルルートに昇格するのに使えるからね。だから、これは知られていると思う。あの頃の良い記事を見つけるのはちょっと大変だけど、ここにリンクを貼っておくよ: https://lwn.net/Articles/632520/ https://forums.grsecurity.net/viewtopic.php?f=7&t=2522&sid=c...

ソラリスの特権システムも同じだね(基本的にはLinuxの能力をもっと良くしたものだけど、同じ抽象モデルだよ)。