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


26

파일 기능을 사용하여 setuid-root 바이너리가 필요하지 않은 젠투 강화 상자를 관리합니다 (예 : /bin/pingCAP_NET_RAW 등).

사실, 내가 남긴 바이너리는 이것입니다.

abraxas ~ # find / -xdev -type f -perm -u=s
/usr/lib64/misc/glibc/pt_chown
abraxas ~ # 

setuid 비트를 제거하거나 루트 파일 시스템을 다시 마운트 nosuid하면 sshd 및 GNU Screen grantpt(3)이 마스터 psudoterminals 를 호출 하고 glibc가 분명히이 프로그램을 실행하여 슬레이브 pseudoterminal을 chown하고 chmod하기 위해이 프로그램을 실행 /dev/pts/합니다. 실패합니다.

문제는 파일 시스템이 마운트 grantpt(3)된 Linux에서 devpts그러한 도우미 바이너리가 필요하지 않다는 명시 적 맨 페이지입니다 . 커널은 자동으로 슬레이브의 UID & GID를 /dev/ptmx(호출하여 getpt(3)) 열린 프로세스의 실제 UID & GID로 설정합니다 .

나는 이것을 보여주기 위해 작은 예제 프로그램을 작성했다.

#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을 사용했는데 sshd와 GNU Screen이 여전히 작동합니다.
Aaron Jones

실패한 프로그램은 정확히 무엇입니까? 아마도 그들은 깨져서 pty(필요한대로)가 아니라 프로그램을 확인합니까?
vonbrand

CAP_CHOWN 및 CAP_FOWNER가없는 프로그램은 grantpt ()를 호출하고 도우미 바이너리는 EUID == 0으로 시작하지 않으며 grantpt ()에 대한 0이 아닌 리턴 코드를 가지며 프로그램은 PTS 생성을 중단해야합니다. ptmx (4)에 따라
Aaron Jones

3
"솔루션"은 유언장을 제공합니다. 가장 좋은 경우에는 버그를 조사하고 새로운 버그를 만들며 최악의 경우 심각한 보안 취약점을 만듭니다. glibc 개발자와 함께이 문제를 해결하십시오.
vonbrand

3
그런 다음 이것을 glibc 사람들에게보고하십시오.
vonbrand

답변:


2

귀하의 경우 glibc는이 합리적 현재, 그리고 devpts가 제대로 설정되어 호출 할 필요가 없을 것 pt_chown전혀 도우미.

에서 setuid-root를 제거 하는 알려진 / 잠재적 인 문제가 발생 했을 수 있습니다 pt_chown.

grantpt()glibc-2.7devfs 에서 지원되는 glibc-2.11의 변경 사항은 명시 적으로 확인하는 대신 호출을 시도 하거나 다시 호출 하기 전에 작업을 수행 해야하는지 확인 합니다.DEVFS_SUPER_MAGICchown()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 및 모드가 기대와 일치해야한다는 것입니다 (당신, tty, 정확히 620;로 확인하십시오 /usr/libexec/pt_chown --help). 그렇지 않은 경우 chown()(기능 CAP_CHOWN, 호출 2 진 / 프로세스의 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를 제공하는 것이지만 실제로는 pty로 제한 할 수 없으므로 권장하지 않습니다 .

그 도움이되지 않는 경우는 패치, 그것을 해결 sshdscreenglibc는 패치보다 약간 덜 불쾌하다. 문제는 glibc에 있기 때문에,보다 명확한 접근 방식은 더미를 구현하기 위해 DLL 주입 을 선택적으로 사용하는 것입니다grantpt() .


"문제는 glibc에 있기 때문에, 깔끔한 접근 방식은 더미 grantpt ()를 구현하기 위해 DLL 주입을 선택적으로 사용하는 것입니다." -- 훌륭한. 왜 그렇게 생각하지 않았습니까? 감사. :)
Aaron Jones
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.