web-dev-qa-db-ja.com

setuidビットを適切に使用する

通常のユーザーが実行するときにroot権限を必要とするプロセスがあります。どうやら私はこれを達成するために「setuidビット」を使用することができます。 POSIXシステムでこれを行う適切な方法は何ですか?

また、インタープリター(bash、Perl、python、phpなど)を使用するスクリプトでこれを行うにはどうすればよいですか?

45
goldilocks

setuidビット を実行可能ファイルに設定して、実行時に、プログラムが実際のユーザーではなくファイルの所有者の権限を持っている場合、それらの権限が異なるようにすることができます。これは、effectiveuid(ユーザーID)とrealuidの違いです。

passwdなどの一部の一般的なユーティリティは、rootが所有しており、必要に応じてこのように構成されています(passwdは_/etc/shadow_にアクセスする必要があり、これはrootだけが読み取ることができます)。

これを行うときの最善の戦略は、スーパーユーザーとしてすぐに実行する必要があることをすべて実行してから、権限を下げ、rootの実行中にバグや誤用が発生する可能性を低くすることです。これを行うには、プロセスのeffectiveuidを実際のuidに設定します。 POSIX Cの場合:

_#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void report (uid_t real) {
    printf (
        "Real UID: %d Effective UID: %d\n",
        real,
        geteuid()
    );
}

int main (void) {
    uid_t real = getuid();
    report(real);
    seteuid(real);
    report(real);
    return 0;
}
_

関連する関数。POSIXシステムで一般的に使用されている場合、ほとんどの高水準言語で同等の機能が必要です。

  • getuid() real uidを取得します。
  • geteuid() effective uidを取得します。
  • seteuid() effective uidを設定します。

最後の1つが実際のuidに不適切な場合、setuidビットが実行可能ファイルに設定されている場合を除き、何もできません。これを試すには、_gcc test.c -o testuid_をコンパイルします。次に、特権を使用して以下を実行する必要があります。

_chown root testuid
chmod u+s testuid
_

最後のものは、setuidビットを設定します。ここで、通常のユーザーとして_./testuid_を実行すると、デフォルトでプロセスが実効uid 0、rootで実行されることがわかります。

スクリプトはどうですか?

これはプラットフォームごとに異なります ですが、Linuxでは、バイトコードを含むインタープリターを必要とするものは、インタープリターで設定されていない限り、非常に愚かですが、setuidビットを使用できません。 )。上記のCコードを模倣した単純なPerlスクリプトは次のとおりです。

_#!/usr/bin/Perl
use strict;
use warnings FATAL => qw(all);

print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine Perl...
print "Real UID: $< Effective UID: $>\n"; 
_

* nixyルーツに忠実に、Perlには有効なuid(_$>_)と実際のuid(_$<_)用の特別な変数が組み込まれています。しかし、コンパイルされた(前の例のCからの)実行可能ファイルで使用されているのと同じchownおよびchmodを試しても、違いはありません。スクリプトは特権を取得できません。

これに対する答えは、setuidバイナリを使用してスクリプトを実行することです。

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

int main (int argc, char *argv[]) {
    if (argc < 2) {
        puts("Path to Perl script required.");
        return 1;
    }
    const char *Perl = "Perl";
    argv[0] = (char*)Perl;
    return execv("/usr/bin/Perl", argv);
}
_

この_gcc --std=c99 whatever.c -o perlsuid_をコンパイルし、次に_chown root perlsuid && chmod u+s perlsuid_をコンパイルします。 any Perlスクリプトを、実効uid 0、誰が所有しているかに関係なく実行できるようになりました。

同様の戦略は、php、pythonなどでも機能します。But...

_# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face
_

PLEASE PLEASEこのようなものを放置しないでください.ほとんどの場合、実際に絶対パスとしてスクリプトを記述します。つまり、main()のすべてのコードを次のコードに置き換えます:

_    const char *args[] = { "Perl", "/opt/suid_scripts/whatever.pl" }
    return execv("/usr/bin/Perl", (char * const*)args);
_

それらは、_/opt/suid_scripts_とその中のすべてが非rootユーザーに対して読み取り専用であることを確認してください。そうしないと、誰かが何でも_whatever.pl_に入れ替えることができます。

さらに、多くのスクリプト言語では、環境変数がスクリプトの実行方法を変更できるようになっていることに注意してください。たとえば、環境変数により、呼び出し元から提供されたライブラリが読み込まれ、呼び出し元がルートとして任意のコードを実行できるようになります。したがって、インタプリタとスクリプト自体の両方がすべての可能な環境変数に対して堅牢であることを知らない限り、 DO N'T DO THIS です。

では、代わりに何をすべきでしょうか?

非rootユーザーがrootとしてスクリプトを実行できるようにするより安全な方法は、 Sudo ルールを追加して、ユーザーに_Sudo /path/to/script_を実行させることです。 Sudoはほとんどの環境変数を取り除き、管理者が誰がどの引数でコマンドを実行できるかを細かく選択できるようにします。例は パスワードプロンプトなしで特定のプログラムをrootとして実行する方法? を参照してください。

54
goldilocks

はい、できますが、おそらく非常に悪い考えです。通常、実行可能ファイルに直接SUIDビットを設定するのではなく、 Sudo(8) または su(1) を使用して実行します(実行できるユーザーを制限します)。

ただし、通常のユーザーがプログラム(特にスクリプト!)をrootとして実行できるようにすることには、多くのセキュリティ上の問題があることに注意してください。それらを処理するためにプログラムが特別かつ非常に慎重に書かれていない限り、悪意のあるユーザーは簡単にルートシェルを入手することができます。

したがって、たとえそれを行う場合でも、最初にrootとして実行するプログラムへのすべての入力を、環境、コマンドラインパラメータ、STDINとそれが処理するファイル、ネットワーク接続からのデータを含めてサニタイズすることをお勧めします。開くなど。非常に難しく、正しく行うのがさらに困難です。

たとえば、エディタで一部のファイルのみを編集するユーザーを許可できます。しかし、エディターは外部ヘルパーのコマンド実行または一時停止を許可するため、ユーザーにルートシェルに希望どおりの操作を与えることができます。または、セキュリティが非常に厳しく見えるシェルスクリプトを実行している可能性がありますが、ユーザーは変更された$ IFSまたは他の環境変数を使用してスクリプトの動作を完全に変更して実行できます。または、PHP "ls"を実行するはずのスクリプトですが、ユーザーは$ PATHを変更してスクリプトを実行するため、 "ls"という名前のシェルを実行します。または他の何十ものセキュリティ問題。

プログラムが本当にその一部をrootとして実行する必要がある場合は、通常のユーザーとして実行されるように変更するのがおそらく最善ですが、suidヘルパープログラムを通じてrootが絶対に必要な部分を(できるだけサニタイズした後に)実行するだけです。

すべてのローカルユーザーを完全に信頼している場合(たとえば、rootパスワードを与える場合)は、意図しないセキュリティによる監視(PHP =オンラインスクリプト、または弱いパスワードなど)により、リモートの攻撃者が突然マシン全体を完全に危険にさらすことができます。

11
Matija Nalis

私はこの種の解決策を持っています。

  1. Shebangラインで使用することを目的としたCラッパーを作成します。ルートを設定します。
  2. 起動直後、ラッパーは引数として指定されたスクリプトがsetuid/setgidであるかどうかをチェックします。
    • いいえの場合-ルート特権を削除します。
    • はいの場合-setuid/setgidビットを反映するように有効なuid/gidを変更します。
  3. 指定されたスクリプトが通常のファイルかどうかを確認し、そうでない場合は終了します。
  4. 「Perl -xスクリプト」を介してPerlを実行します。

より厳密な/制限されたバージョンでもこれを行うことができます(および/または):

  • setuid/setgid rootであるスクリプトの実行を禁止し、
  • 汚染されたモードでPerlを実行します(「Perl -x -Tスクリプト」)。

さらに高度なバージョンでは、Sudoを介してPerlを起動できます( "exec Sudo ... Perl -x script")。

0
user404221