シェルスクリプトでsetuidを許可する


185

setuid許可ビットではなく、エグゼキュータの所有者の実効ユーザーIDを使用してプログラムを実行するためのLinuxに指示します:

> cat setuid-test.c

#include <stdio.h>
#include <unistd.h>

int main(int argc, char** argv) {
    printf("%d", geteuid());
    return 0;
}

> gcc -o setuid-test setuid-test.c
> ./setuid-test

1000

> sudo chown nobody ./setuid-test; sudo chmod +s ./setuid-test
> ./setuid-test

65534

ただし、これは実行可能ファイルにのみ適用されます。シェルスクリプトはsetuidビットを無視します。

> cat setuid-test2

#!/bin/bash
id -u

> ./setuid-test2

1000

> sudo chown nobody ./setuid-test2; sudo chmod +s ./setuid-test2
> ./setuid-test2

1000

ウィキペディアによると

セキュリティ上の欠陥の可能性が高まるため、多くのオペレーティングシステムは、実行可能なシェルスクリプトに適用されるとsetuid属性を無視します。

これらのリスクを受け入れるつもりなら、Linuxにsetuidビットを実行可能ファイルと同じようにシェルスクリプトで処理するように指示する方法はありますか?

そうでない場合、この問題の一般的な回避策はありますか?私の現在の解決策は、パスワードプロンプトを回避するために、実行したいユーザーとして特定のスクリプトを実行sudoersできるようALLにするエントリを追加することですNOPASSWD。それの主な欠点は、sudoersこれを行うたびにエントリが必要になることと、呼び出し元sudo some-scriptが単にsome-script

回答:


203

Linuxは、解釈されたすべての実行可能ファイル(つまり、#!行で始まる実行可能ファイル)のsetuid¹ビットを無視します。comp.unix.questionsよくあるご質問は、 setuidさシェルスクリプトでのセキュリティ上の問題を説明しています。これらの問題には、シバン関連とシェル関連の2種類があります。以下で詳細を説明します。

セキュリティを気にせず、setuidスクリプトを許可したい場合、Linuxでは、カーネルにパッチを適用する必要があります。3.xカーネルの時点で、の呼び出しの前install_exec_credsに、load_script関数にの呼び出しを追加する必要があると思いますがopen_exec、テストしていません。


セイド・シバン

shebang(#!)の通常の実装方法に固有の競合状態があります。

  1. カーネルは実行可能ファイルを開き、で始まることを見つけ#!ます。
  2. カーネルは実行可能ファイルを閉じ、代わりにインタープリターを開きます。
  3. カーネルは、スクリプトへのパスを引数リスト(as argv[1])に挿入し、インタープリターを実行します。

この実装でsetuidスクリプトが許可されている場合、攻撃者は、既存のsetuidスクリプトへのシンボリックリンクを作成して実行し、カーネルがステップ1を実行した後、インタープリターが移動する前にリンクを変更することにより、任意のスクリプトを呼び出すことができます最初の引数を開きます。このため、ほとんどの大学は、シバンを検出するとsetuidビットを無視します。

この実装を保護する1つの方法は、インタープリターが開くまでカーネルがスクリプトファイルをロックすることです(これにより、ファイルのリンク解除または上書きだけでなく、パス内のディレクトリ名の変更も防止する必要があります)。しかし、UNIXシステムは必須のロックから遠ざかる傾向があり、シンボリックリンクは正しいロック機能を特に難しく侵襲的にします。私は誰もこの方法でそれを行うとは思わない。

(有効にする設定のカーネルを必要とするすべては、主にOpenBSD、NetBSDのとMac OS X、)いくつかのUNIXシステムでは、実装安全なsetuidさシェバングを追加機能を使用する:パスは、既にファイルディスクリプタで開かれたファイルを参照するN(そう開口部がありますにほぼ等しい)。多くのUNIXシステム(Linuxを含む)にはsetuidスクリプトがありますが、ありません。/dev/fd/N/dev/fd/Ndup(N)/dev/fd

  1. カーネルは実行可能ファイルを開き、で始まることを見つけ#!ます。実行可能ファイルのファイル記述子が3だとしましょう。
  2. カーネルがインタープリターを開きます。
  3. カーネル/dev/fd/3は引数リストを(asとしてargv[1])挿入し、インタープリターを実行します。

Sven Mascheckのshebangページには、 setuidのサポートなど、大学全体のshebangに関する多くの情報があります。


Setuidインタープリター

OSがsetuid shebangをサポートしているため、またはネイティブバイナリラッパー(などsudo)を使用しているため、プログラムをrootとして実行できたと仮定します。セキュリティホールを開けましたか?たぶん。ここでの問題は、解釈されたプログラムとコンパイルされたプログラムに関するものではありません。問題は、ランタイムシステムが特権で実行された場合に安全に動作するかどうかです。

  • 動的にリンクされたネイティブバイナリ実行可能ファイルは、プログラムに必要な動的ライブラリをロードする動的ローダー(例/lib/ld.so)によって解釈されます。多くの大学でLD_LIBRARY_PATHは、環境(環境変数の共通名)を使用して動的ライブラリの検索パスを構成し、実行されたすべてのバイナリに追加のライブラリをロードすることもできます(LD_PRELOAD)。プログラムの呼び出し側は、(他の戦術の中に)特別に細工libc.soしたものを配置することにより、そのプログラムのコンテキストで任意のコードを実行できます$LD_LIBRARY_PATH。すべての健全なシステムはLD_*、setuid実行可能ファイルの変数を無視します。

  • シェルなどの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_fileenv_keepだけ無害な変数が含まれています。


TL、DR:

  • Setuid shebangは安全ではありませんが、通常は無視されます。
  • (sudoまたはsetuidを介して)特権を使用してプログラムを実行する場合、ネイティブコードまたはperlを記述するか、環境をサニタイズするラッパーを使用してプログラムを起動します(env_resetオプションを使用したsudoなど)。

¹ この説明は、「setuid」を「setgid」に置き換えた場合にも同様に適用されます。これらは両方とも、スクリプト上のLinuxカーネルによって無視されます


2
@Josh:セキュアなsetuidシェルスクリプトは可能ですが、シェルの実装者とスクリプト作成者の両方が非常に慎重な場合のみです。ネイティブコードではなく、Perlをお勧めします。Perlでは、スクリプト作成者側の労力をほとんどかけずにsetuidスクリプトをセキュリティで保護する必要があることに注意してください。
ジル

3
どうやらこれらのsuidperlものは廃止され、何年もの間削除のマークが付けられているように見えます(ただし、
それでも変わら

7
実際suidperlにperl 5.11(5.12安定版)で削除されました:perl5110delta:> "suidperl"は削除されました。以前は、適切にサポートしていないシステムでsetuid許可ビットをエミュレートするメカニズムを提供していました。perl5120delta:> "suidperl"はPerlの一部ではなくなりました。以前は、適切にサポートしていないシステムでsetuid許可ビットをエミュレートするメカニズムを提供していました。
ランディストーナー

5
また、perl 5.6.1 docs(ほぼ10年前)の次の行にも注意してください。 suidperlの使用は強く推奨されていません。必要と思われる場合は、sudoなどの代替手段を最初に試してください。courtesan.com/sudoを参照してください。
ランディストーナー

3
わかりません。これは問題の理由の説明のようですが、OPの質問に対する実際の答えはここにありますか?OSにsetuidシェルスクリプトを実行するように指示する方法はありますか?
トム

55

この問題を解決する1つの方法は、setuidビットを使用できるプログラムからシェルスクリプトを呼び出すことです。
sudoのようなもの。たとえば、Cプログラムでこれを実現する方法を次に示します。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    setuid( 0 );   // you can set it at run time also
    system( "/home/pubuntu/setuid-test2.sh" );
    return 0;
 }

setuid-test2.cとして保存します。
コンパイル
今、このプログラムバイナリでsetuidを実行します。

su - nobody   
[enter password]  
chown nobody:nobody a.out  
chmod 4755 a.out  

これで、実行できるようになり、スクリプトは誰のアクセス許可もなしに実行されます。
ただし、ここでも、スクリプトパスをハードコードするか、コマンドライン引数として上記のexeに渡す必要があります。


25
スクリプトをコマンドライン引数として渡すことを提案することはお勧めしません。これは、プログラムを実行できる人なら誰でも、定義されたユーザーとしてスクリプトを実行できるようになるからです。
-dsp

40
スクリプトへのフルパスがハードコードされていても、これは安全ではないことに注意してください。シェルは環境から変数を継承し、それらの多くは呼び出し側が任意のコードを注入できるようにします。PATHそしてLD_LIBRARY_PATH、明らかなベクトルです。シェルによっては、実行$ENVまたは$BASHENVあるいは~/.zshenv、それらは、適切なスクリプトの実行を開始する前であっても、あなたはスクリプト内からで、これらすべてから保護することはできません。特権でシェルスクリプトを呼び出す唯一の安全な方法は、環境クリーンアップすることです。須藤は、それを安全に行う方法を知っています。したがって、独自のラッパーを作成せずに、sudoを使用してください
ジル

28
彼が突然これに落とされているのは気分が悪い-私は特に安全でないバージョンも聞きたいと言っていたし、それを言ったときにシェルスクリプトの引数を取る実行可能ファイルを想像していました。明らかに非常に安全ではありませんが、どのような可能性が存在するのか知りたいと思いました
マイケル・ムロゼック

8
@Gilles:参考までに、Linux LD_LIBRARY_PATHはsetuidビットを検出すると、特に設定を解除します。
悲しみ

3
使用する代わりにsystemexecファミリーのいずれかを使用する方が簡単(かつ効率的)である可能性がありexecveます。そうすれば、新しいプロセスを作成したり、シェルを起動したりすることなく、引数を転送できます(特権スクリプトが引数を安全に処理できると仮定)。
トビースパイト

23

したがって、このボートにあるいくつかのスクリプトに接頭辞を付けます。

#!/bin/sh
[ "root" != "$USER" ] && exec sudo $0 "$@"

これはsetuid現在のファイルを使用せずに単に実行することに注意してくださいsudo


5
これはsetuidを使用せず、sudoプロンプトを表示するだけです。(私にとって、setuidの重要なポイントは、sudoを必要とせずにrootとして実行できるようにすることです。)
Luc

12

呼び出しを避けたい場合は、次のようにしsudo some_scriptます。

  #!/ust/bin/env sh

  sudo /usr/local/scripts/your_script

SETUIDプログラムはroot権限で実行され、ユーザーはそれらを大きく制御できるため、細心の注意を払って設計する必要があります。彼らはすべてを健全性チェックする必要があります。次の理由により、スクリプトではできません。

  • シェルは、ユーザーと頻繁にやり取りする大きなソフトウェアです。すべての健全性チェックはほぼ不可能です。特に、ほとんどのコードはそのようなモードで実行することを目的としていないためです。
  • スクリプトはほとんど手っ取り早い解決策であり、通常はsetuidを許可するような注意を払って準備されていません。潜在的に危険な機能が多数あります。
  • 彼らは他のプログラムに大きく依存しています。シェルがチェックされただけでは不十分です。sedawkなども確認する必要があります

それsudoはいくつかの健全性チェックを提供しますが、それは十分ではないことに注意してください-あなた自身のコードのすべての行をチェックしてください。

最後に、機能の使用を検討してください。これにより、通常はルート権限が必要な特別な権限をユーザーとして実行しているプロセスに付与できます。ただし、たとえば、pingネットワークを操作する必要がある一方で、ファイルにアクセスする必要はありません。ただし、それらが継承されるかどうかはわかりません。


2
する/ust/bin/env必要があり/usr/bin/envますと思います。
ボブ

4

sudo +スクリプトの名前のエイリアスを作成できます。もちろん、エイリアスを設定する必要があるため、これは設定するのにさらに手間がかかりますが、sudoを入力する必要がなくなります。

しかし、恐ろしいセキュリティリスクを気にしないのであれば、シェルスクリプトのインタープリターとしてsetuidシェルを使用してください。それがあなたのために働くかどうかわからないが、私はそれがそうかもしれないと思う。

ただし、実際にこれを行うことはお勧めしません。教育目的で言及しているだけです;-)


5
それが動作します。あなたが言ったように恐ろしく。SETUIDビットを使用すると、所有者権限で実行できます。Setuidシェル(setuidで動作するように設計されていない限り)は、すべてのユーザーのルートとして実行されます。つまり、誰でも実行できますrm -rf /(およびシリーズの他のコマンドは、自宅でITをしないでください)。
マチェイピエチョトカ

9
@MaciejPiechotkaによっては自宅でそれをしないでください、あなたが意味する仕事であることを行うにはお気軽に!:)
ペテルフ14

4

super [-r reqpath]コマンド[args]

Superは、指定されたユーザーがroot(あたかもroot)であるかのようにスクリプト(または他のコマンド)を実行できるようにします。または、コマンドを実行する前に、コマンドごとにuid、gid、および/または補助グループを設定できます。スクリプトをsetuid rootにすることの安全な代替手段となることを目的としています。Superでは、一般ユーザーが他のユーザーが実行するコマンドを提供することもできます。これらは、コマンドを提供するユーザーのuid、gid、およびグループで実行されます。

スーパーは、「super.tab」ファイルを調べて、ユーザーが要求されたコマンドの実行を許可されているかどうかを確認します。許可が与えられると、superはpgm [args]を実行します。ここで、pgmはこのコマンドに関連付けられているプログラムです。(ルートはデフォルトで実行を許可されていますが、ルールがルートを除外している場合は拒否されます。通常のユーザーはデフォルトで実行を許可されません。)

commandがスーパープログラムへのシンボリックリンク(またはハードリンク)である場合、%command argsを入力することは、%super command argsを入力することと同等です(コマンドはsuperであってはなりません。リンク。)

http://www.ucolick.org/~will/RUE/super/README

http://manpages.ubuntu.com/manpages/utopic/en/man1/super.1.html


こんにちは、サイトへようこそ!ここで回答の詳細を期待しています。回答を編集して、このプログラムとは何か、OPの問題の解決にどのように役立つかを説明してください。
テルドン

Nizam、ありがとう。アカウントがファイルのユーザーとして別のユーザーによって実行されることを許可するスーパーのようなものを見つけようとして何時間も。
チャド

2

何らかの理由sudoで利用できない場合は、Cでシンラッパースクリプトを記述できます。

#include <unistd.h>
int main() {
    setuid(0);
    execle("/bin/bash","bash","/full/path/to/script",(char*) NULL,(char*) NULL);
}

そして、コンパイルしたらのように設定setuidchmod 4511 wrapper_scriptます。

これは別の投稿された答えに似ていますが、クリーンな環境でスクリプトを実行し、/bin/bashによって呼び出されたシェルの代わりに明示的に使用するsystem()ため、潜在的なセキュリティホールを閉じます。

これにより、環境が完全に破棄されることに注意してください。脆弱性を公開せずにいくつかの環境変数を使用したい場合、本当に使用する必要がありますsudo

明らかに、スクリプト自体がrootによってのみ書き込み可能であることを確認する必要があります。


-3

すべての答えに納得できないこの質問を最初に見つけましたが、ここにははるかに優れたものがあります。

自明であるべきです。

#include <string>
#include <unistd.h>

template <typename T, typename U>
T &replace (
          T &str, 
    const U &from, 
    const U &to)
{
    size_t pos;
    size_t offset = 0;
    const size_t increment = to.size();

    while ((pos = str.find(from, offset)) != T::npos)
    {
        str.replace(pos, from.size(), to);
        offset = pos + increment;
    }

    return str;
}

int main(int argc, char* argv[])
{
    // Set UUID to root
    setuid(0);

    std::string script = 
R"(#!/bin/bash
whoami
echo $1
)";

    // Escape single quotes.
    replace(script, std::string("'"), std::string("'\"'\"'"));

    std::string command;
    command = command + "bash -c '" + script + "'"; 

    // Append the command line arguments.
    for (int a = 0; a < argc; ++a)
    {
        command = command + " " + argv[a];
    }

    return system(command.c_str());
}

その後、実行します

g++ embedded.cpp -o embedded
sudo chown root embedded
sudo chmod u+s embedded

なぜダウン投票なのか???
セオドアR.スミス

2
おそらく、文字列操作を行い、シェルコードが埋め込まれている非常に複雑なソリューションだからです。単純なランチャーを作成するか、sudoを使用します。これはすべてのオプションの中で最悪です。
siride
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.