Ubuntu - ผู้ใช้ที่ไม่ใช่รูทสามารถดำเนินการในคุก chroot ได้หรือไม่?


18

เป็นไปได้หรือไม่ที่ผู้ใช้ที่ไม่ใช่รูทใช้กระบวนการ chroot บน Ubuntu?


เธรด FreeBSD เก่านี้ครอบคลุมคำถามเดียวกัน: lists.freebsd.org/pipermail/freebsd-security/2003-April/…คำตอบสั้น ๆ : ไม่คุณไม่สามารถเรียกใช้กระบวนการเป็นรูทภายในคุก chroot ที่ไม่ใช่รูท
เดวิดแฮร์ริสัน

chroot jails จำเพาะกับ bsd chroot ใน linux ไม่ใช่คุก ครั้งล่าสุดที่ฉันตรวจสอบมันเป็นไปไม่ได้ที่จะ chroot ในฐานะผู้ใช้
xenoterracide

1
@xenoterracide Jails เป็น BSD ที่เฉพาะเจาะจง แต่ chroot เป็นที่รู้จักกันทั่วไปว่าเป็น "chroot jail" ในชุมชน Linux มันค่อนข้างสับสน
pehrs

2
คุณพยายามทำอะไรและทำไม มีเครื่องมือเช่น fakechroot และ schroot ที่ให้ทางเลือกใช้การได้ขึ้นอยู่กับความต้องการของคุณ
Zoredache

นอกจากนี้ยังมีการสนทนาที่เกี่ยวข้องเพิ่มเติมที่วิธีการ“ คุก” กระบวนการโดยไม่ต้องรูท? ด้วยวิธีการทำงานหรือลังเลที่จะแก้ปัญหานี้ในรายการ
imz - Ivan Zakharyaschev

คำตอบ:


12

บน Linux การเรียกระบบchroot (2)สามารถทำได้โดยกระบวนการที่ได้รับการยกเว้นเท่านั้น ความสามารถที่กระบวนการต้องการคือ CAP_SYS_CHROOT

เหตุผลที่คุณไม่สามารถ chroot เนื่องจากผู้ใช้นั้นค่อนข้างเรียบง่าย สมมติว่าคุณมีโปรแกรม setuid เช่น sudo ที่ตรวจสอบ / etc / sudoers หากคุณได้รับอนุญาตให้ทำอะไรบางอย่าง ตอนนี้ใส่ไว้ใน chroot chroot กับ / etc / sudoers ของคุณเอง ทันใดนั้นคุณมีการยกระดับสิทธิ์ทันที

เป็นไปได้ที่จะออกแบบโปรแกรมเพื่อ chroot ตัวเองและรันมันเป็นกระบวนการ setuid แต่โดยทั่วไปถือว่าเป็นการออกแบบที่ไม่ดี ความปลอดภัยเพิ่มเติมของ chroot ไม่ได้กระตุ้นให้เกิดปัญหาด้านความปลอดภัยกับ setuid


3
ด้วยความเป็นไปได้ใหม่ของเนมสเปซใน linux อาจเป็นไปได้ที่จะสร้างเนมสเปซ "ผู้ใช้" ใหม่ซึ่งจะมีผู้ใช้รูท "ฝังตัว" และดำเนินการในchrootภายหลัง
imz - Ivan Zakharyaschev

1
@ imz - IvanZakharyaschev คุณถูกต้องอย่างแน่นอนและฉันหวังว่าคุณจะไม่รังเกียจฉันที่ได้รับเสรีภาพในการเขียนที่เป็นคำตอบที่ทดสอบได้ง่าย
hvd

@hvd ยอดเยี่ยม! มันจะต้องมีประโยชน์มากเพราะมันแสดงให้เห็นถึงวิธีการใช้คุณสมบัติลินุกซ์ที่ไม่คุ้นเคยกับคำสั่งที่เป็นรูปธรรม
imz - Ivan Zakharyaschev

6

@ imz - ความเห็นของ IvanZakharyaschev เกี่ยวกับคำตอบของ pehrs ว่าอาจเป็นไปได้ด้วยการเปิดตัว namespaces แต่สิ่งนี้ยังไม่ได้รับการทดสอบและโพสต์เป็นคำตอบ ใช่มันทำให้ผู้ใช้ที่ไม่ใช่รูทสามารถใช้ 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 ผู้ใช้รากใน namespace ที่ถูกแมปไปที่ไม่ใช่รากนอก ID ผู้ใช้ของ namespace นั้นและในทางกลับกันซึ่งเป็นเหตุผลที่ไฟล์ระบบที่แสดงให้เห็นว่าเป็นเจ้าของโดยผู้ใช้ปัจจุบันเป็นเจ้าของโดยผู้ใช้ ID 0. ปกติls -al rootโดยไม่ต้องunshareไม่ แสดงให้พวกเขาเป็นเจ้าของโดยผู้ใช้ปัจจุบัน


หมายเหตุ: มันเป็นที่รู้จักกันดีว่ากระบวนการที่มีความสามารถในการใช้งานมีความสามารถในการทำลายออกของchroot chrootเนื่องจากunshare -rจะให้chrootสิทธิ์แก่ผู้ใช้ทั่วไปจึงเป็นความเสี่ยงด้านความปลอดภัยหากได้รับอนุญาตภายในchrootสภาพแวดล้อม อันที่จริงมันไม่ได้รับอนุญาตและล้มเหลวด้วย:

ยกเลิกการแชร์: ยกเลิกการแชร์ไม่สำเร็จ: ไม่อนุญาตให้ดำเนินการ

ซึ่งตรงกับเอกสารunshare (2) :

EPERM (ตั้งแต่ Linux 3.9)

CLONE_NEWUSERถูกระบุในแฟล็กและผู้เรียกอยู่ในสภาพแวดล้อม chroot (เช่นไดเร็กทอรีรูทของผู้โทรไม่ตรงกับรูทไดเร็กทอรีของเมาต์เนมสเปซที่มันอยู่)


การรัน pivot_root ในเนมสเปซเมาต์มีผลคล้ายกับ chroot แต่หลีกเลี่ยงความขัดแย้งกับเนมสเปซของผู้ใช้
ทิโมธีบอลด์วิน

1
หนึ่งสามารถหลบหนี chroot หรือ mount namespace โดยการลงไปใน / proc หากพวกเขาเป็นกระบวนการภายนอกด้วย UID เดียวกันใน PID เดียวกันหรือเด็ก PID และ namespaces ผู้ใช้
ทิโมธีบอลด์วิน

2

วันนี้คุณต้องการดู LXC (Linux Containers) แทน chroot / BSD jail มันอยู่ที่ไหนสักแห่งระหว่าง chroot และเครื่องเสมือนที่ให้การควบคุมความปลอดภัยจำนวนมากและการกำหนดค่าทั่วไป ฉันเชื่อว่าสิ่งที่คุณต้องใช้เพื่อดำเนินการในฐานะผู้ใช้คือการเป็นสมาชิกของกลุ่มที่เป็นเจ้าของไฟล์ / อุปกรณ์ที่จำเป็น แต่อาจมีความสามารถ / ระบบการอนุญาตที่เกี่ยวข้อง ไม่ว่าจะด้วยวิธีใดก็ควรเป็นไปได้มากเนื่องจาก LXC นั้นค่อนข้างเร็วหลังจาก SELinux เป็นต้นถูกเพิ่มเข้าไปในเคอร์เนล Linux

นอกจากนี้โปรดทราบว่าคุณสามารถเขียนสคริปต์เป็น root แต่ให้สิทธิ์ผู้ใช้ที่ปลอดภัยในการเรียกใช้สคริปต์เหล่านั้น (โดยไม่ต้องใช้รหัสผ่านหากคุณต้องการ แต่ให้แน่ใจว่าสคริปต์นั้นปลอดภัย) โดยใช้ sudo


1

การรวมกันของ fakeroot / fakechroot ให้เสมือนจริงของ chroot สำหรับความต้องการง่ายๆเช่นการสร้างคลังเก็บ tar ที่ไฟล์ดูเหมือนว่าจะเป็นเจ้าของโดยราก Fakechroot manpage เป็นhttp://linux.die.net/man/1/fakechroot

คุณไม่ได้รับอนุญาตใหม่ แต่ถ้าคุณเป็นเจ้าของไดเรกทอรี (เช่นปลอม distro) ก่อนที่จะเรียกใช้

fakechroot fakeroot chroot ~/fake-distro some-command

ตอนนี้ให้มองหาคำสั่งบางอย่างเช่นคุณหยั่งรากและเป็นเจ้าของทุกสิ่งภายในโรงกลั่นน้ำมันปลอม


นี่เป็นความคิดที่ดี แต่ดูเหมือนว่าจะจัดการ symlinks อย่างคาดไม่ถึง ฉัน~/fake-distroใช้ busybox ซึ่ง symlinks ls, mvและระบบสาธารณูปโภคอื่น ๆ /bin/busyboxทั่วไปที่จะ ถ้าผมชัดเจนโทร/bin/busybox mv ..., สิ่งที่ทำงาน แต่ถ้าผมเรียกฉันได้รับ/bin/mv ... sh: /bin/mv: not foundการตั้งค่าexport FAKECHROOT_EXCLUDE_PATH=/ก่อนใช้งานการปลอมแปลงรากเทียมจะแก้ไขอาการที่เกิดขึ้น แต่หลังจากนั้นจะหยุดลงบน symlink อื่น ๆ (เช่น/usr/bin/vim -> /usr/bin/vim.vim)
Ponkadoodle

บางที FAKECHROOT_EXCLUDE_PATH = /: / usr จะช่วยได้ไหม
sylvainulg

1

ดูเหมือนว่าด้วย user-namespaces ในความเป็นจริงเป็นไปได้ที่จะ 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 น้อยที่สุดที่สร้างขึ้นด้วย multistrap (ดำเนินการในฐานะที่ไม่ใช่ราก) ไฟล์ระบบบางไฟล์เช่น/etc/passwdและ/etc/groupsถูกคัดลอกจากโฮสต์ rootfs ลงใน guest rootfs


ล้มเหลวFailed to unshare user namespaceสำหรับฉันที่ linux 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
จริงๆแล้ว @wallacoloo ดูเหมือนว่า arch จะปิดการใช้งานเนมสเปซของผู้ใช้ที่ไม่มีสิทธิพิเศษในการสร้างเคอร์เนล: lists.archlinux.org/pipermail/arch-general/2017- กุมภาพันธ์ 2013
cheshirekow

0

ไม่ถ้าฉันจำได้อย่างถูกต้องมีบางสิ่งในระดับเคอร์เนลที่ chroot ทำเพื่อป้องกัน ฉันจำไม่ได้ว่ามันคืออะไร ฉันตรวจสอบมันกลับมาเมื่อทำ messing ด้วยเครื่องมือ Catalyst Build ของ Gentoo (และ chroot บน gentoo นั้นเหมือนกับ chroot บน Ubuntu) แม้ว่ามันจะเป็นไปได้ที่จะทำให้มันเกิดขึ้นโดยไม่ต้องใช้รหัสผ่าน ... แต่สิ่งต่าง ๆ เหล่านี้อยู่ในขอบเขตของความเสี่ยงด้านความปลอดภัยที่อาจเกิดขึ้นและทำให้แน่ใจว่าคุณรู้ว่าคุณกำลังทำอะไรอยู่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.