/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
것이 screen
glibc 패치를 적용하는 것보다 약간 덜 짜증나는 일입니다. 문제는 glibc 내에 있으므로 더 깔끔한 접근 방식이 선택 사항입니다.DLL 주입 사용더미를 구현합니다 grantpt()
.