/bin/ping
私は、setuid-rootバイナリ(たとえば、CAP_NET_RAWなど)の必要性をほとんど排除するためにファイル機能を使用するGentoo Hardenedボックスを管理しています。
実際、私に残った唯一のバイナリはこれです:
abraxas ~ # find / -xdev -type f -perm -u=s
/usr/lib64/misc/glibc/pt_chown
abraxas ~ #
setuidビットを削除するか、ルートファイルシステムを再マウントすると、nosuid
sshdとGNU Screenがgrantpt(3)
マスター擬似端末を呼び出すため、動作が停止し、glibcは明らかにこのプログラムを実行してスレーブ擬似端末をchownおよびchmodし、/dev/pts/
GNU Screenはこの場合処理します。機能が失敗します。
問題は、マンページgrantpt(3)
にLinuxではファイルシステムをマウントした後、そのようなヘルパーバイナリは必要ないと明示されていることですdevpts
。カーネルは自動的にスレーブのUIDとGIDを開いたプロセスの実際のUIDとGIDに設定します/dev/ptmx
(getpt(3)
。
私はこれを示すために小さなサンプルプログラムを書いた:
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int master;
char slave[16];
struct stat slavestat;
if ((master = getpt()) < 0) {
fprintf(stderr, "getpt: %m\n");
return 1;
}
printf("Opened a UNIX98 master terminal, fd = %d\n", master);
/* I am not going to call grantpt() because I am trying to
* demonstrate that it is not necessary with devpts mounted,
* the owners and mode will be set automatically by the kernel.
*/
if (unlockpt(master) < 0) {
fprintf(stderr, "unlockpt: %m\n");
return 2;
}
memset(slave, 0, sizeof(slave));
if (ptsname_r(master, slave, sizeof(slave)) < 0) {
fprintf(stderr, "ptsname: %m\n");
return 2;
}
printf("Device name of slave pseudoterminal: %s\n", slave);
if (stat(slave, &slavestat) < 0) {
fprintf(stderr, "stat: %m\n");
return 3;
}
printf("Information for device %s:\n", slave);
printf(" Owner UID: %d\n", slavestat.st_uid);
printf(" Owner GID: %d\n", slavestat.st_gid);
printf(" Octal mode: %04o\n", slavestat.st_mode & 00007777);
return 0;
}
上記のプログラムからsetuidビットを削除し、その動作を観察します。
aaron@abraxas ~ $ id
uid=1000(aaron) gid=100(users) groups=100(users)
aaron@abraxas ~ $ ./ptytest
Opened a UNIX98 master terminal, fd = 3
Device name of slave pseudoterminal: /dev/pts/17
Information for device /dev/pts/17:
Owner UID: 1000
Owner GID: 100
Octal mode: 0620
この問題を解決する方法についていくつかのアイデアがあります。
1) プログラムを 0 だけ返すスケルトンに置き換えます。
2)私のlibcにgrantpt()をパッチして何もしないようにしました。
両方の方法を自動化できますが、これらの方法のいずれか、またはこの問題を解決する方法について提案をしている人はいますか?
この問題を解決したら、ついにできるようになりましたmount -o remount,nosuid /
。
答え1
もしあなたのglibcかなり最新で、開発者pt_chown
正しく設定されたら、ヘルパーを呼び出す必要はありません。
あなたは会うことができます既知/潜在的な問題それからsetuid-rootを削除してくださいpt_chown
。
grantpt()
サポートはdevfs
以下で提供されます。glibc-2.7、変更が適用されました。glibc-2.11ただし、明示的に確認するのではDEVFS_SUPER_MAGIC
なく、呼び出しを試みるか置き換える前に実行する必要があることを確認してください。chown()
pt_chown
~からglibc-2.17/sysdeps/unix/grantpt.c
...
uid_t uid = __getuid ();
if (st.st_uid != uid)
{
if (__chown (buf, uid, st.st_gid) < 0)
goto helper;
}
...
同様のスタンザが gid と権限を確認するために使用されます。問題は、uid、gid、modeが期待どおりに行わなければならないことです(you、tty、正確に620;で確認してください/usr/libexec/pt_chown --help
。そうでない場合は、chown()
試してください(バイナリ/プロセスのCAP_CHOWN、CAP_FOWNER関数呼び出しが必要です)。失敗した場合は、pt_chown
外部ヘルパー(setuid-rootである必要があります)を使用してみてください。この機能を使用するには、そのpt_chown
機能(およびglibc)がHAVE_LIBCAP
。しかし、、そうですpt_chown
(現在glibc-2.17、指摘したようにバージョンを指定していませんが)は好きなようにハードコーディングされています。geteuid()==0
にもかかわらずHAVE_LIBCAP
、関連コードは以下で提供されますglibc-2.17/login/programs/pt_chown.c
。
...
if (argc == 1 && euid == 0)
{
#ifdef HAVE_LIBCAP
/* Drop privileges. */
if (uid != euid)
...
#endif
/* Normal invocation of this program is with no arguments and
with privileges. */
return do_pt_chown ();
}
...
/* Check if we are properly installed. */
if (euid != 0)
error (FAIL_EXEC, 0, gettext ("needs to be installed setuid `root'"));
(geteuid()==0
機能を使用する前に期待するのは、実際に機能の精神に反しているようで、問題に対するバグを記録します。)
潜在的な回避策は、影響を受けるプログラムにCAP_CHOWN、CAP_FOWNERを提供することですが、本当におすすめしませんもちろん、ptysに制限することはできないからです。
それでも問題を解決できない場合は、パッチを適用するのがsshd
glibcscreen
パッチを適用するよりも少し迷惑になります。問題はglibc内にあるので、よりきれいなアプローチはオプションです。DLL注入の使用ダミーを実装しますgrantpt()
。