우분투-루트가 아닌 사용자가 chroot jail에서 프로세스를 실행할 수 있습니까?


18

루트가 아닌 사용자가 Ubuntu에서 chroot 프로세스를 실행할 수 있습니까?


이 오래된 FreeBSD의 스레드가 같은 질문을 다룹니다 lists.freebsd.org/pipermail/freebsd-security/2003-April/... 짧은 대답 : 아니, 당신은 루트가 아닌 chroot로 내 루트로 프로세스를 실행할 수 없습니다.
David Harrison

chroot jail은 bsd에만 해당됩니다. 리눅스의 chroot는 감옥이 아닙니다. 마지막으로 사용자로 chroot 할 수 없다는 것을 확인했습니다.
xenoterracide

1
@xenoterracide Jails는 BSD에 따라 다르지만 chroot는 일반적으로 Linux 커뮤니티에서 "chroot jail"으로 알려져 있습니다. 꽤 혼란 스러워요.
pehrs

2
무엇을하려고하고 왜? fakechroot 및 schroot와 같은 도구는 요구 사항에 따라 실행 가능한 대안을 제공합니다.
Zoredache

루트가 아닌 프로세스를“감옥하는 방법”에 대한 더 자세한 토론도있었습니다 . 이 작업을 해결하기 위해 더 많은 작업 또는 임시 접근 방식을 사용합니다.
imz-Ivan Zakharyaschev

답변:


12

Linux에서 chroot (2) 시스템 호출은 권한있는 프로세스에 의해서만 수행 될 수 있습니다. 프로세스에 필요한 기능은 CAP_SYS_CHROOT입니다.

사용자로서 chroot 할 수없는 이유는 매우 간단합니다. sudo와 같은 setuid 프로그램이 있다고 가정하면 / etc / sudoers를 검사하여 무언가를 할 수 있는지 확인하십시오. 이제 자신의 / etc / sudoers와 함께 chroot chroot에 넣습니다. 갑자기 즉시 권한 상승이 있습니다.

chroot 자체로 프로그램을 디자인하고 setuid 프로세스로 실행할 수 있지만 일반적으로 잘못된 디자인으로 간주됩니다. chroot의 추가 보안은 setuid의 보안 문제를 유발하지 않습니다.


3
리눅스에서 네임 스페이스 의 새로운 가능성으로 , 아마도 "내장 된"루트 사용자가있는 새로운 "사용자"네임 스페이스를 생성 (공유 해제) 할 수 있습니다 chroot.
imz-Ivan Zakharyaschev

1
@ imz--IvanZakharyaschev 당신은 절대적으로 정확하며, 쉽게 테스트 할 수있는 답변으로 작성하는 자유를 취하지 않아도되기를 바랍니다.
hvd December

@hvd 좋아요! 구체적인 명령에 익숙하지 않은 새로운 Linux 기능을 사용하는 방법을 보여주기 때문에 매우 유용해야합니다.
imz-Ivan Zakharyaschev

6

@ imz--IvanZakharyaschev는 네임 스페이스를 도입하여 가능할 수 있다는 pehrs의 답변에 대해 언급하지만, 테스트 및 답변으로 게시되지 않았습니다. 그렇습니다. 루트 사용자가 아닌 사용자도 chroot를 사용할 수 있습니다.

정적으로 링크 dash되고 정적으로 링크 되고 루트가 아닌 busybox것으로 실행중인 bash쉘이 있습니다.

$ mkdir root
$ cp /path/to/dash root
$ cp /path/to/busybox root
$ unshare -r bash -c 'chroot root /dash -c "/busybox ls -al /"'
total 2700
drwxr-xr-x    2 0        0             4096 Dec  2 19:16 .
drwxr-xr-x    2 0        0             4096 Dec  2 19:16 ..
drwxr-xr-x    1 0        0          1905240 Dec  2 19:15 busybox
drwxr-xr-x    1 0        0           847704 Dec  2 19:15 dash

네임 스페이스의 루트 사용자 ID는 해당 네임 스페이스의 루트가 아닌 사용자 ID를 외부로 매핑하고, 사용자 ID가 0 정기 소유로 현재 사용자가 소유 한 시스템 프로그램 파일을 왜 반대 부사장, ls -al root하지 않고는 unshare,하지 현재 사용자가 소유 한 것으로 표시합니다.


참고 :을 사용할 수있는 프로세스 chroot는을 (를) 중단 할 수 있는 것으로 잘 알려져 chroot있습니다. 일반 사용자 unshare -r에게 chroot권한을 부여 하므로 chroot환경 내에서 허용되는 경우 보안 위험이 있습니다. 실제로 허용되지 않으며 다음과 같이 실패합니다.

공유 해제 : 공유 해제 실패 : 작업이 허용되지 않습니다

unshare (2) 문서 와 일치합니다 :

EPERM (Linux 3.9부터)

CLONE_NEWUSER플래그 로 지정되었고 호출자가 chroot 환경에 있습니다 (즉, 호출자의 루트 디렉토리가있는 마운트 네임 스페이스의 루트 디렉토리와 일치하지 않습니다).


마운트 네임 스페이스에서 pivot_root를 실행하면 chroot와 비슷한 효과가 있지만 사용자 네임 스페이스와의 충돌을 피할 수 있습니다.
Timothy Baldwin

1
동일한 또는 하위 PID 및 사용자 네임 스페이스에서 동일한 UID를 가진 외부 프로세스 인 경우 / proc로 내림차순으로 chroot 또는 마운트 네임 스페이스를 이스케이프 할 수 있습니다.
Timothy Baldwin

2

요즘 chroot / BSD jail 대신 LXC (Linux Containers)를보고 싶을 것입니다. chroot와 가상 머신 사이에있어 많은 보안 제어 및 일반 구성 기능을 제공합니다. 사용자로서 필요한 모든 파일 / 장치를 소유 한 그룹의 구성원이어야한다고 생각하지만 기능 / 시스템 권한도 관련 될 수 있습니다. 어느 쪽이든, SELinux 등이 Linux 커널에 추가 된 지 얼마되지 않아 LXC가 최신 버전이므로 매우 가능해야합니다.

또한 스크립트를 루트로 작성할 수 있지만 sudo를 사용하여 원하는 경우 암호없이 스크립트를 실행할 수있는 보안 권한을 사용자에게 부여 할 수 있습니다 (암호는 필요하지만 스크립트는 안전한지 확인하십시오).


1

fakeroot / fakechroot의 조합은 루트가 파일을 소유 한 것처럼 보이는 tar 아카이브를 생성하는 것과 같은 간단한 요구에 대한 비슷한 chroot를 제공합니다. Fakechroot 맨 페이지는 http://linux.die.net/man/1/fakechroot 입니다.

새로운 권한은 없지만 호출하기 전에 디렉토리 (예 : 가짜 디스트로)를 소유 한 경우

fakechroot fakeroot chroot ~/fake-distro some-command

그것은 이제 당신이 root와 같은 명령을 찾고 fake-distro 내의 모든 것을 소유합니다.


이것은 좋은 생각이지만 심볼릭 링크를 예기치 않게 처리하는 것 같습니다. 내 ~/fake-distro용도에 비지 박스, 심볼릭 링크 ls, mv그리고 다른 일반적인 유틸리티 /bin/busybox. 명시 적으로 전화 /bin/busybox mv ...하면 문제가 발생하지만 전화 /bin/mv ...하면을 얻습니다 sh: /bin/mv: not found. export FAKECHROOT_EXCLUDE_PATH=/fakechroot를 실행 하기 전에 설정하면 해당 증상이 해결되지만 다른 심볼릭 링크 (예 :)에서는 끊어집니다 /usr/bin/vim -> /usr/bin/vim.vim.
Ponkadoodle

FAKECHROOT_EXCLUDE_PATH = / : / usr이 도움이 될 것입니다.
sylvainulg

1

사용자 네임 스페이스를 사용하면 실제로 루트없이 chroot하는 것이 가능합니다. 다음은 가능함을 보여주는 예제 프로그램입니다. 나는 리눅스 네임 스페이스가 어떻게 작동하는지 탐구하기 시작했을 뿐이 므로이 코드가 모범 사례인지 확실하지 않습니다.

다른 이름으로 저장하십시오 user_chroot.cc. 로 컴파일하십시오 g++ -o user_chroot user_chroot.cc. 사용법은 ./user_chroot /path/to/new_rootfs입니다.

// references:
// [1]: http://man7.org/linux/man-pages/man7/user_namespaces.7.html
// [2]: http://man7.org/linux/man-pages/man2/unshare.2.html

#include <sched.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <cerrno>
#include <cstdio>
#include <cstring>

int main(int argc, char** argv) {
    if(argc < 2) {
        printf("Usage: %s <rootfs>\n", argv[0]);
    }

    int uid = getuid();
    int gid = getgid();
    printf("Before unshare, uid=%d, gid=%d\n", uid, gid);

    // First, unshare the user namespace and assume admin capability in the
    // new namespace
    int err = unshare(CLONE_NEWUSER);
    if(err) {
        printf("Failed to unshare user namespace\n");
        return 1;
    }

    // write a uid/gid map
    char file_path_buf[100];
    int pid = getpid();
    printf("My pid: %d\n", pid);

    sprintf(file_path_buf, "/proc/%d/uid_map", pid);
    int fd = open(file_path_buf, O_WRONLY);
    if(fd == -1) {
        printf("Failed to open %s for write [%d] %s\n", file_path_buf, errno, 
               strerror(errno));
    } else {
        printf("Writing : %s (fd=%d)\n", file_path_buf, fd);
        err = dprintf(fd, "%d %d 1\n", uid, uid);
        if(err == -1) {
            printf("Failed to write contents [%d]: %s\n", errno, 
                   strerror(errno));
        }
        close(fd);
    }

    sprintf(file_path_buf, "/proc/%d/setgroups", pid);
    fd = open(file_path_buf, O_WRONLY);
    if(fd == -1) {
        printf("Failed to open %s for write [%d] %s\n", file_path_buf, errno, 
               strerror(errno));
    } else {
        dprintf(fd, "deny\n");
        close(fd);
    }

    sprintf(file_path_buf, "/proc/%d/gid_map", pid);
    fd = open(file_path_buf, O_WRONLY);
    if(fd == -1) {
        printf("Failed to open %s for write [%d] %s\n", file_path_buf, errno, 
               strerror(errno));
    } else {
        printf("Writing : %s (fd=%d)\n", file_path_buf, fd);
        err = dprintf(fd, "%d %d 1\n", gid, gid);
        if(err == -1) {
            printf("Failed to write contents [%d]: %s\n", errno, 
                   strerror(errno));
        }
        close(fd);
    }

    // Now chroot into the desired directory
    err = chroot(argv[1]);
    if(err) {
        printf("Failed to chroot\n");
        return 1;
    }

    // Now drop admin in our namespace
    err = setresuid(uid, uid, uid);
    if(err) {
        printf("Failed to set uid\n");
    }

    err = setresgid(gid, gid, gid);
    if(err) {
        printf("Failed to set gid\n");
    }

    // and start a shell
    char argv0[] = "bash";
    char* new_argv[] = {
        argv0,
        NULL
    };

    err = execvp("/bin/bash", new_argv);
    if(err) {
        perror("Failed to start shell");
        return -1;
    }
}

멀티 스트랩 (루트가 아닌 것으로 실행)으로 생성 된 최소 rootfs에서 이것을 테스트했습니다. 일부 시스템 파일을 좋아 /etc/passwd하고 /etc/groups게스트 rootfs에 호스트 rootfs에서 복사되었습니다.


Failed to unshare user namespaceLinux 4.12.10 (Arch Linux) 에서 나를 위해 실패 합니다.
Ponkadoodle

@wallacoloo는 아마도 printf ()를 perror ()로 수정하고 실제 오류가 무엇인지 확인합니다. 실패한 호출로 인해 발생할 수있는 오류 코드에 대해서는 man7.org/linux/man-pages/man2/unshare.2.html 을 참조하십시오 unshare. : 당신이 더 나은 오류 메시지가있을 수 있습니다 파이썬 버전을 시도 할 수 있습니다 github.com/cheshirekow/uchroot
cheshirekow

1
사실 그것의 커널 빌드에서 아치 비활성화 같은 unpriviledged 사용자 네임 스페이스를 소리 @wallacoloo : lists.archlinux.org/pipermail/arch-general/2017-February/...이
cheshirekow

0

아니요. 올바르게 기억하면 chroot가 수행하는 커널 수준의 일이있어이를 막을 수 있습니다. 그게 뭔지 기억 나지 않습니다. 젠투의 Catalyst Build 도구를 망칠 때 다시 조사했습니다 (그리고 젠투의 chroot는 우분투의 chroot와 동일합니다). 비밀스러운 일없이 일어날 수는 있지만, 그런 일은 잠재적 인 보안 취약점의 영역에 맡겨져 있으며 자신이 무엇을하고 있는지를 확인해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.