이 glibc 문제를 해결하는 가장 좋은 방법은 무엇입니까?

이 glibc 문제를 해결하는 가장 좋은 방법은 무엇입니까?

/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 비트를 제거하거나 루트 파일 시스템을 다시 마운트하면 nosuidsshd 및 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것이 screenglibc 패치를 적용하는 것보다 약간 덜 짜증나는 일입니다. 문제는 glibc 내에 있으므로 더 깔끔한 접근 방식이 선택 사항입니다.DLL 주입 사용더미를 구현합니다 grantpt().

관련 정보