私はコードを書きました:
// a.c
#include <stdlib.h>
int main () {
system("/bin/sh");
return 0;
}
コマンドでコンパイル:
gcc a.c -o a.out
その上にsetuidビットを追加しました:
Sudo chown root.root a.out
Sudo chmod 4755 a.out
Ubuntu 14.04で一般ユーザーとして実行すると、root権限を取得しました。
しかし、Ubuntu 16.04では、現在のユーザーのシェルがまだあります。
なぜ違うのですか?
変更されたのは、_/bin/sh
_がbash
になるか、またはdash
のままになり、bashの動作を模倣する追加のフラグ_-p
_を取得したことです。
(man page で説明されているように、bashはsetuid特権を削除しないように_-p
_フラグを必要とします:
実効ユーザー(グループ)IDが実際のユーザー(グループ)IDと等しくない状態でシェルが起動され、-pオプションが指定されていない場合、起動ファイルは読み込まれず、シェル関数は環境から継承されません。SHELLOPTS 、BASHOPTS、CDPATH、およびGLOBIGNORE変数は、環境に表示されても無視され、実効ユーザーIDは実際のユーザーIDに設定されます。呼び出し時に-pオプションが指定されている場合、起動時の動作は同じですが、有効なユーザーIDはリセットされません。
以前は、dash
はこれを気にせず、(それを防ぐために何もしないことによって)setuidの実行を許可していました。しかし buntu 16.04のdash
のマンページ には、bash
と同様に、説明されている追加のオプションがあります。
-p priv
有効なuidがuidと一致しない場合は、それをリセットしようとしないでください。 これはデフォルトでは設定されておらず、system(3)またはpopen(3)を介したsetuidルートプログラムによる誤った使用を回避するのに役立ちます。
このオプションは pstream には存在しませんでした(提案されたパッチに反応しなかった可能性があります)*)Debian 9でもありませんが、2018年以降にパッチを入手したDebianバスターに存在します。
注:StéphaneChazelasによって説明されているように、system()
は_"/bin/sh -p"
_で指定されたものを実行するため、setuidはすでに削除されているため、system()
で_/bin/sh
_を呼び出すのは遅すぎます。 derobert の答えは、これを処理する方法をsystem()
の前のコードで説明しています。
おそらくシェルは、何らかの理由で、起動の一部として有効なユーザーIDを実際のユーザーIDに戻しています。これを確認するには、次を追加します。
_/* needs _GNU_SOURCE; non-Linux users see setregid/setreuid instead */
uid_t euid = geteuid(), egid = getegid();
setresgid(egid, egid, egid);
setresuid(euid, euid, euid);
_
system()
の前。 (実際には、Linuxの場合でも、おそらく実際のものを設定するだけで済みます。保存されたものはそのままにしておいても構いません。これは、デバッグするための力です。set-idの理由によっては、もちろん、実際のIDもどこかに保存します。)
[また、これがsetidがどのように機能するかを学ぶだけの演習ではない場合、特にシェルを呼び出すときに、心配するべき多くのセキュリティ問題があります。たとえば、シェルの動作に影響を与える多くの環境変数があります。可能であれば、Sudo
のような既存のアプローチを優先してください。]