SUIDの概念は非特権ユーザーがプログラムを特権ユーザーとして実行できるようにすることであると理解していますが、SUIDは通常、いくつかの回避策がないとシェルスクリプトで機能しないことがわかりました。私の質問は、シェルスクリプトとバイナリプログラムの二分法がよくわかりません。シェルスクリプトで実行できることは何でも、Cでも実行でき、バイナリにコンパイルできるようです。シェルスクリプトに対してSUIDが安全でない場合、バイナリに対しても安全ではありません。では、なぜバイナリではなくシェルスクリプトがSUIDの使用を禁止されるのでしょうか。
Shebang(_#!
_)の一般的な実装方法に固有の競合状態があります。
#!
_で始まることがわかります。argv[1]
_として)、インタープリターを実行します。この実装でsetuidスクリプトが許可されている場合、攻撃者は、既存のsetuidスクリプトへのシンボリックリンクを作成し、それを実行して、カーネルがステップ1を実行した後、インタープリターが処理を開始する前にリンクを変更することにより、任意のスクリプトを呼び出すことができます。最初の引数を開きます。このため、現代のすべてのuniceは、シバンを検出するとsetuidビットを無視します。
この実装を保護する1つの方法は、インタプリタがスクリプトファイルを開くまでカーネルがスクリプトファイルをロックすることです(これにより、ファイルのリンク解除や上書きだけでなく、パス内のディレクトリ名の変更も防ぐ必要があります)。しかし、UNIXシステムは必須のロックを回避する傾向があり、シンボリックリンクは、正しいロック機能を特に困難かつ侵襲的にします。私は誰もこのようにそれをしないと思います。
いくつかのUNIXシステムは、追加機能を使用して安全なsetuid Shebangを実装しています。パス_/dev/fd/N
_は、ファイル記述子で既に開かれているファイルを参照しています[〜#〜] n [〜#〜](つまり、_/dev/fd/N
_を開くことはdup(N)
とほぼ同じです)。
#!
_で始まることがわかります。実行可能ファイルのファイル記述子が3であるとしましょう。/dev/fd/3
_を引数リストに挿入し(_argv[1]
_として)、インタープリターを実行します。Linuxを含むすべての最新のUNIXバリアントは_/dev/fd
_を実装していますが、ほとんどはsetuidスクリプトを許可していません。デフォルト以外のカーネル設定を有効にすると、OpenBSD、NetBSD、Mac OS Xでサポートされます。 Linuxでは、それを可能にするパッチが作成されていますが、それらのパッチがマージされることはありません。 Sven MascheckのShebangページsetuid support を含む、ユニバース全体のShebangに関する多くの情報があります。
さらに、昇格された特権で実行されるプログラムには固有のリスクがあり、インタープリターが特別に設計されていない限り、高レベルのプログラミング言語では通常制御が困難です。その理由は、プログラム独自のコードがこのデータをサニタイズする前に、プログラミング言語ランタイムの初期化コードが、低い特権の呼び出し元から継承したデータに基づいて、昇格された特権でアクションを実行する可能性があるためです。 Cランタイムはプログラマーにとってほとんど機能しないので、Cプログラムは、何か問題が発生する前に、制御を取り、データをサニタイズするより良い機会を持っています。
OSがsetuid Shebangをサポートしているか、ネイティブバイナリラッパー(Sudo
など)を使用したことが原因で、プログラムをrootとして実行できたと仮定します。セキュリティホールを開けましたか?多分。ここでの問題は、解釈されたプログラムとコンパイルされたプログラムについてではありません。問題は、ランタイムシステムが特権で実行された場合に安全に動作するかどうかです。
動的にリンクされたネイティブバイナリ実行可能ファイルは、プログラムに必要な動的ライブラリをロードする動的ローダー(例:_/lib/ld.so
_)によって解釈されます。多くのuniceでは、環境を通じて動的ライブラリの検索パスを構成でき(_LD_LIBRARY_PATH
_は環境変数の一般的な名前です)、実行されたすべてのバイナリに追加のライブラリをロードすることもできます(_LD_PRELOAD
_)。プログラムの呼び出し元は、特別に細工された_libc.so
_を_$LD_LIBRARY_PATH
_に(他の戦術の中で)配置することにより、そのプログラムのコンテキストで任意のコードを実行できます。すべての正常なシステムは、setuid実行可能ファイルの_LD_*
_変数を無視します。
Sh、csh、デリバティブなどのシェルでは、環境変数が自動的にシェルパラメータになります。 PATH
、IFS
などのパラメーターを通じて、スクリプトの呼び出し元は、シェルスクリプトのコンテキストで任意のコードを実行する多くの機会を持っています。一部のシェルは、スクリプトが特権付きで呼び出されたことを検出した場合、これらの変数を適切なデフォルトに設定しますが、信頼できる特定の実装があるかどうかはわかりません。
ほとんどのランタイム環境(ネイティブ、バイトコード、インタープリター)には同様の機能があります。 setuid実行可能ファイルで特別な注意を払う人はほとんどいませんが、ネイティブコードを実行するものは、動的なリンク(注意が必要)よりも優れた機能を実行しません。
Perlは注目すべき例外です。安全にsetuidスクリプトを明示的にサポートします。実際、OSがスクリプトのsetuidビットを無視した場合でも、スクリプトはsetuidを実行できます。これは、Perlにsetuidルートヘルパーが付属しているためです。このヘルパーは、必要なチェックを実行し、目的のスクリプトに対して目的の特権でインタプリタを再度呼び出します。これは perlsec マニュアルで説明されています。以前は、setuid Perlスクリプトには_#!/usr/bin/suidperl -wT
_ではなく_#!/usr/bin/Perl -wT
_が必要でしたが、最近のほとんどのシステムでは_#!/usr/bin/Perl -wT
_で十分です。
ネイティブのバイナリラッパーを使用しても、これらの問題を防ぐこと自体は何も行われないことに注意してください。実際には、ランタイム環境が特権で呼び出されたことを検出し、ランタイムの構成可能性をバイパスすることをランタイム環境が妨げる可能性があるため、状況を悪化させる可能性があります。
ネイティブバイナリラッパーは、環境をサニタイズする場合、シェルスクリプトを安全にすることができます。スクリプトは、多くの仮定(現在のディレクトリなど)を行わないように注意する必要がありますが、これは問題ありません。環境をサニタイズするように設定されていれば、Sudoを使用できます。変数をブラックリストに登録するとエラーが発生しやすくなるため、常にホワイトリストに登録してください。 Sudoでは、_env_reset
_オプションがオンになっていること、setenv
がオフであること、および_env_file
_と_env_keep
_に無害な変数のみが含まれていることを確認してください。
これらの考慮事項はすべて、特権の昇格に等しく適用されます:setuid、setgid、setcap。
https://unix.stackexchange.com/questions/364/allow-setuid-on-Shell-scripts/2910#291 からリサイクル
多くのカーネルは、新しく実行されたプロセスがsetuidするときと、コマンドインタープリターが起動するときの間で、シェルスクリプトを選択した別の実行可能ファイルと交換できる競合状態に悩まされています。あなたが十分に固執しているなら、理論的にはあなたが望むプログラムをカーネルに実行させることができます。
また、そのリンクで見つかった他の理由(ただし、カーネルの競合状態が最も危険です)。もちろん、スクリプトは、障害が忍び寄る場所であるバイナリプログラムとは異なる方法でロードされます。
これを読むと面白くなるかもしれません 2001 Dr. Dobbの記事 -より安全なSUIDシェルスクリプトを作成するための6つのステップを実行し、ステップ7に到達するだけです。
レッスンセブン-SUIDシェルスクリプトを使用しないでください
すべての作業を終えた後でも、安全なSUIDシェルスクリプトを作成することはほぼ不可能です。 (ほとんどのシステムでは不可能です。)これらの問題のために、一部のシステム(Linuxなど)はシェルスクリプトのSUIDを受け入れません。
この履歴 競合状態を修正したバリアントについて説明します。リストは思ったよりも大きいですが、setuidスクリプトは、他の問題のため、または安全なバリアントと安全でないバリアントのどちらで実行しているかを覚えるよりも簡単に落胆するため、依然として大部分は落胆しています。
これは、昔は十分に大きな問題でしたが、 Perl がどのように積極的にそれを補償するように取り組んだかについて読むことは有益です。