web-dev-qa-db-ja.com

setuidが機能しない

setuidがどのように機能するかを学ぼうとしていました。

そこで、現在のユーザーを出力するだけのダミープログラムを作成しました。

#include<bits/stdc++.h>
using namespace std;


int main(){
    cout << system("id -a") << "\n";
    cout << system("whoami")  << "\n";
}

ユーザーanmolの下に実行可能ファイルmy-binaryをコンパイルして作成しました。

-rwxrwxr-x 1 anmol anmol 9972 Feb  1 16:54 my-binary

次に、chmod +sを使用してsetuidオプションを設定します。

-rwsrwsr-x 1 anmol anmol 9972 Feb  1 16:54 my-binary

正常に実行すると、次の出力が得られます。

uid=1000(anmol) gid=1000(anmol) groups=1000(anmol),4(adm),24(cdrom),27(Sudo),30(dip),46(plugdev),116(lpadmin),122(sambashare)
anmol

ここで、su user2を使用して別のユーザーに変更し、それを実行すると、次のようになります。

uid=1001(user2) gid=1001(user2) groups=1001(user2)
user2

そして、Sudo ./my-binaryを使用して実行すると、次のようになります。

uid=1001(root) gid=1001(root) groups=1001(root)
root

私が理解している限り、どのように実行しても、毎回最初の出力を取得するべきではありませんか?

ここで他の同様の質問を確認し、ファイルシステムがnosuidオプションを使用してマウントされているかどうかを確認するように提案されたため、mount | /dev/sda1を実行して出力を取得しました。

/dev/sda1 on / type ext4 (rw,relatime,errors=remount-ro,data=ordered)

これは、このオプションが有効になっていないことを意味します。

期待した出力が得られない理由についてのヒントはありますか?

1

system(3)ライブラリ関数はコマンド引数を_/bin/sh -c_に渡すことで実行し、Linuxの_/bin/sh_(bashdash、またはmksh)は、呼び出されない限り、setuidまたはsetgid特権を放棄します。 _-p_オプションを使用します。

Bashのマンページには次のように書かれています。

有効なユーザー(グループ)IDが実際のユーザー(グループ)IDと等しくない状態でシェルが開始され、_-p_オプションが提供されていない場合[...]有効なユーザーIDは実際のユーザーIDに設定されます。

dash(Debian/Ubuntuの_/bin/sh_)を使用すると、これは種類の新しいです。Debian9Stretch(2017)ではまだそうではありませんでした。 Debian固有の 変更 のみで、まだアップストリームにはありません ソース 2020-02-04現在。 bashは、2.Xバージョン(RedHat 7.X、2000に最初に含まれている)以来これを持っています。

これはまだそうではありません他のシェル(_ksh93_、zshなど)または他のシステムの_/bin/sh_( OpenBSD、FreeBSD、Solaris;しかしそれがあったNetBSDではない 変更された bashのように動作する)。

彼らがそれを持っている場合、彼らの特権モードはbashとは異なって動作します:シェルが実行されるとデフォルトで オン になりますsetuidモードであり、シェルにsetuid特権をドロップさせるには、_set +p_で off をオフにする必要があります。


system()popen()を使用する場合、またはsetuidバイナリから直接シェルまたは実行可能シェルスクリプトを実行する場合は、「スプリットパーソナリティ」を放棄して完全に切り替える必要があります。 _setres[ug]id_を介して実際のまたは効果的な資格情報に:

_% cat a.cc
#include <unistd.h>
#include <stdlib.h>
int main(){
    uid_t euid = geteuid();
    setresuid(euid, euid, euid);
    system("id");
}
% c++ a.cc
% chmod u+s a.out
% ./a.out
uid=1002(fabe)
% su -c ./a.out
uid=1002(fabe)
_

バイナリが実際に有効な資格情報を切り替えたことを確認したいだけの場合は、system()を介して外部プログラムを呼び出すのではなく、直接行ってください。

_#include <iostream>
#include <unistd.h>
#include <pwd.h>
using namespace std;
int main(){
    uid_t euid = geteuid(); struct passwd *pw = getpwuid(euid);
    cout << "euid=" << euid;
    if(pw) cout << ", " << pw->pw_name;
    cout << endl;
}
_
5
user392879