파일 기능을 사용하여 setuid-root 바이너리가 필요하지 않은 젠투 강화 상자를 관리합니다 (예 : /bin/ping
CAP_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 /
.
답변
귀하의 경우 glibc는이 합리적 현재, 그리고 devpts가 제대로 설정되어 호출 할 필요가 없을 것 pt_chown
전혀 도우미.
에서 setuid-root를 제거 하는 알려진 / 잠재적 인 문제가 발생 했을 수 있습니다 pt_chown
.
grantpt()
glibc-2.7devfs
에서 지원되는 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 및 모드가 기대와 일치해야한다는 것입니다 (당신, 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로 제한 할 수 없으므로 권장하지 않습니다 .
그 도움이되지 않는 경우는 패치, 그것을 해결 sshd
및 screen
glibc는 패치보다 약간 덜 불쾌하다. 문제는 glibc에 있기 때문에,보다 명확한 접근 방식은 더미를 구현하기 위해 DLL 주입 을 선택적으로 사용하는 것입니다grantpt()
.