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


45

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

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

回答:


53

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

などの一部の一般的なユーティリティは、passwdrootが所有し、必要にpasswd応じてこの方法で構成されます(/etc/shadowrootのみが読み取れるアクセスが必要です)。

これを行う際の最善の戦略は、スーパーユーザーとして必要なことをすぐに実行し、rootの実行中にバグや誤用が発生しにくいように特権を下げることです。これを行うには、プロセスの実効 uidをその実際の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()実際の uidを取得します。
  • geteuid()有効な uidを取得します。
  • seteuid()有効な uidを設定します。

実行可能ファイルにsetuidビットが設定されている場合を除き、実際のuidに不適切な最後のものでは何もできません。これを試すには、コンパイルします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($<)のための特別な変数が組み込まれています。しかし、あなたは同じしようchownchmodコンパイルされた実行可能(C、前の例から)、それはどんな違いをすることはありませんと一緒に使用します。スクリプトは特権を取得できません。

これに対する答えは、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。これで、実行することができます任意の、0の実効uidとでperlスクリプトを関係なく、それを所有している人の。

同様の戦略がphp、pythonなどで機能しますしかし...

# 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ます。

さらに、多くのスクリプト言語では、環境変数がスクリプトの実行方法を変更できることに注意してください。たとえば、環境変数により、呼び出し元が提供するライブラリがロードされ、呼び出し元がルートとして任意のコードを実行できるようになります。あなたは通訳やスクリプト自体の両方が可能なすべての環境変数に対してロバストであることを知っている限りこのように、これをしません

それでは、代わりに何をすべきですか?

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


1
多くのシェルは、-privilegedシェルとして呼び出された場合の動作が異なり、責任あるスクリプトから呼び出された場合も、suid rootその方法で安全に呼び出すことができます。しかし、責任あるスクリプトの概念は、現実の世界ではちょっとした夢想にすぎないのではないかと思います。とにかく、それは...それはすべての可能なしばらくの権限が上昇しているのならば、すべての種類の書き込みを回避することがいかに重要であるか、おそらく言及する価値がある
mikeserv

@mikeserv「-privilegedシェル」と「責任あるスクリプト」の概念に慣れていません。シェルについて別の答えをしたいですか?私はもちろんあなたにダニを約束することはできません;)これを行うには多くの良い理由はありません- sudo可能であればはるかに良いオプションです-私はそれをPHPのシステムパスワード認証に関する初期の質問に由来する一般的な答えとして書きました。
goldilocks 14年

1
私はそれをしたくない- 責任あるスクリプト本当に難しい -私を超えて、おそらく。しかし、私はあなたの分析に同意しないと言わなければならないsudo-私はそれがそうではないふりをするという点sudo精神病と見なします。シェルグロブをに入れることもできます。私の意見では、スクリプト作成の目的には適していますが、ライブアプリケーションには適しています。しかし、どちらも持つスクリプトと、明示的なようではありませんbanglineまたは何でです。rootsudoerssusudo#!/bin/ksh -psuid ksh$PATH
mikeserv 14年

この回答はこの質問に対応していますが、setuidスクリプトはローカル権限昇格の脆弱性にさらされるためのレシピです。スクリプトを簡単にsetuidできないのには理由があります。ここでの正しい答えはsudoです。
mdadm 14年

私はあなたの答えを自由に編集して、少なくとも環境変数の巨大な脆弱性について言及しました。安全でないメソッド(スクリプトのsetuidラッパー)について多くの余談が生じるため、私はこれを正規の回答としてはあまり好みません。sudoを推奨し、別のスレッドのために拡張ディスカッションを残すのが最善でしょう(ちなみに、そこで説明しました)。
ジル「SO-悪であるのをやめる」14年

11

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

ただし、通常のユーザーがプログラム(特にスクリプト!)をrootとして実行できるようにすることには、多くのセキュリティ上の問題があります。ほとんどのユーザーは、プログラムがそれを処理するために具体的かつ非常に慎重に記述されていない限り、それを行う必要があり、悪意のあるユーザーはそれを使用して簡単にルートシェルを取得できます。

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

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

プログラムが実際にルートとして作業の一部を行う必要がある場合、通常のユーザーとして実行するように修正するのがおそらく最善ですが、suidヘルパープログラムを介して絶対にルートを必要とする部分を実行します(可能な限りサニタイズした後)。

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


1
出向。参照安全のためのPOSIXのプログラマのシリーズで呼び出し例をman sh行うには失敗したことさえして- command -p env -i ...。結局のところ、するのはかなり難しいです。それでも、あればそれは、明示的な目的のために、正しく行われている、それはより良いにすることができsuid可能インタプリタを使用するよりsusudo-のいずれか1の行動は常に、スクリプトのコントロール外となりますよう(けれどもsuそれは傾向としてにくいカーブボールを投げることです少し狂気になります)。全体のパッケージをバンドルする唯一の本命である-それはちょうど良いことしていたことを確認すべてです。
mikeserv 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.