Ubuntu - Un utilisateur non root peut-il exécuter un processus en prison chroot?

18

Est-il possible pour un utilisateur non root d'exécuter un processus chroot sur Ubuntu?

Oeil de faucon
la source
Cet ancien thread FreeBSD couvre la même question: lists.freebsd.org/pipermail/freebsd-security/2003-April/… Réponse courte: Non, vous ne pouvez pas exécuter un processus en tant que root dans une prison chroot non root.
David Harrison
les prisons chroot sont spécifiques à bsd. un chroot sous linux n'est pas une prison. Enfin, j'ai vérifié qu'il n'était pas possible de chrooter en tant qu'utilisateur.
xenoterracide
1
@xenoterracide Les prisons sont spécifiques à BSD, mais chroot est communément appelé «chroot jail» dans la communauté Linux. C'est assez confus.
pehrs
2
Qu'essayez-vous de faire et pourquoi? Il existe des outils comme fakechroot et schroot qui constituent une alternative viable en fonction de vos besoins.
Zoredache
Il y a également eu des discussions plus approfondies sur Comment «emprisonner» un processus sans être root? avec plus d'approches de travail ou provisoires pour résoudre cette tâche répertoriées.
imz - Ivan Zakharyaschev

Réponses:

12

Sous Linux, l' appel système chroot (2) ne peut être effectué que par un processus privilégié. La capacité dont le processus a besoin est CAP_SYS_CHROOT.

La raison pour laquelle vous ne pouvez pas chrooter en tant qu'utilisateur est assez simple. Supposons que vous ayez un programme setuid tel que sudo qui vérifie / etc / sudoers si vous êtes autorisé à faire quelque chose. Maintenant, mettez-le dans un chroot chroot avec vos propres / etc / sudoers. Soudain, vous avez une escalade de privilèges instantanée.

Il est possible de concevoir un programme pour se chrooter et l'exécuter en tant que processus setuid, mais cela est généralement considéré comme une mauvaise conception. La sécurité supplémentaire du chroot ne motive pas les problèmes de sécurité avec le setuid.

pehrs
la source
3
Avec les nouvelles possibilités des espaces de noms sous Linux, il est peut-être possible de créer (annuler le partage) un nouvel espace de noms "utilisateur", où il y aurait un utilisateur racine "incorporé", et d'effectuer chrootensuite.
imz - Ivan Zakharyaschev
1
@ imz - IvanZakharyaschev Vous avez tout à fait raison, et j'espère que cela ne vous dérange pas d'avoir pris la liberté d'écrire cela comme une réponse facilement testable.
hvd
@hvd Super! Il doit être très utile, car il montre comment utiliser les nouvelles fonctionnalités Linux inconnues avec des commandes concrètes.
imz - Ivan Zakharyaschev
6

@ imz - IvanZakharyaschev commente la réponse de pehrs que cela pourrait être possible avec l'introduction d'espaces de noms, mais cela n'a pas été testé et publié comme réponse. Oui, cela permet en effet à un utilisateur non root d'utiliser chroot.

Étant donné un lien statique dash, un lien statique busyboxet un bashshell en cours d'exécution en tant que non root:

$ 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

L'ID utilisateur racine dans cet espace de noms est mappé à l'ID utilisateur non root en dehors de cet espace de noms, et vice versa, c'est pourquoi le système affiche les fichiers appartenant à l'utilisateur actuel comme appartenant à l'ID utilisateur 0. Un standard ls -al root, sans unshare, ne les afficher comme appartenant à l'utilisateur actuel.


Remarque: il est bien connu que les processus qui sont capables d'utiliser chroot, sont capables de sortir d'un chroot. Étant donné que unshare -rdonnerait des chrootautorisations à un utilisateur ordinaire, ce serait un risque pour la sécurité si cela était autorisé dans un chrootenvironnement. En effet, il n'est pas autorisé et échoue avec:

unshare: unshare a échoué: opération non autorisée

qui correspond à la documentation unshare (2) :

EPERM (depuis Linux 3.9)

CLONE_NEWUSER a été spécifié dans des indicateurs et l'appelant est dans un environnement chroot (c'est-à-dire que le répertoire racine de l'appelant ne correspond pas au répertoire racine de l'espace de noms de montage dans lequel il réside).

hvd
la source
L'exécution de pivot_root dans un espace de noms de montage a un effet similaire à chroot mais évite le conflit avec les espaces de noms des utilisateurs.
Timothy Baldwin
1
On peut échapper à un chroot ou monter un espace de noms en descendant dans / proc s'il s'agit d'un processus extérieur avec le même UID dans le même PID ou dans les espaces de noms utilisateur.
Timothy Baldwin
2

De nos jours, vous voulez regarder LXC (conteneurs Linux) au lieu de la prison chroot / BSD. C'est quelque part entre un chroot et une machine virtuelle, ce qui vous donne beaucoup de contrôle de sécurité et de configurabilité générale. Je crois que tout ce dont vous avez besoin pour l'exécuter en tant qu'utilisateur est d'être membre du groupe qui possède les fichiers / périphériques nécessaires, mais il peut également y avoir des capacités / autorisations système impliquées. Quoi qu'il en soit, cela devrait être très faisable, car LXC est assez récent, longtemps après que SELinux etc. ait été ajouté au noyau Linux.

Gardez également à l'esprit que vous pouvez simplement écrire des scripts en tant que root mais donner aux utilisateurs une autorisation sécurisée pour exécuter ces scripts (sans mot de passe si vous le souhaitez, mais assurez-vous que le script est sécurisé) à l'aide de sudo.

Lee B
la source
1

La combinaison de fakeroot / fakechroot donne un simulacre de chroot pour des besoins simples tels que la production d'archives tar où les fichiers semblent appartenir à root. La page de manuel de Fakechroot est http://linux.die.net/man/1/fakechroot .

Vous n'obtenez cependant aucune nouvelle autorisation, mais si vous possédez un répertoire (par exemple, fake-distro) avant d'invoquer

fakechroot fakeroot chroot ~/fake-distro some-command

il recherche maintenant une commande comme si vous étiez root et que vous possédiez tout dans fake-distro.

sylvainulg
la source
C'est une bonne idée, mais elle semble gérer les liens symboliques de manière imprévisible. Mon ~/fake-distroutilisation busybox, qui symlinks ls, mvet d' autres utilitaires communs à /bin/busybox. Si j'appelle explicitement /bin/busybox mv ..., les choses fonctionnent, mais si j'appelle, /bin/mv ...je reçois sh: /bin/mv: not found. Le réglage export FAKECHROOT_EXCLUDE_PATH=/avant d'exécuter le fakechroot corrige ce problème, mais il se casse ensuite sur d'autres liens symboliques (par exemple /usr/bin/vim -> /usr/bin/vim.vim).
Ponkadoodle du
peut-être que FAKECHROOT_EXCLUDE_PATH = /: / usr aiderait alors?
sylvainulg
1

Il semble qu'avec les espaces de noms d'utilisateurs, il soit en fait possible de chrooter sans root. Voici un exemple de programme qui démontre que c'est possible. J'ai seulement commencé à explorer le fonctionnement des espaces de noms Linux et je ne suis donc pas tout à fait sûr de savoir si ce code est la meilleure pratique ou non.

Enregistrer sous user_chroot.cc. Compilez avec g++ -o user_chroot user_chroot.cc. L'utilisation est ./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;
    }
}

J'ai testé cela sur un rootfs minimal généré avec multistrap (exécuté en tant que non root). Certains fichiers système aiment /etc/passwdet /etc/groupsont été copiés des rootfs hôtes vers les rootfs invités.

cheshirekow
la source
Échoue Failed to unshare user namespacepour moi sur linux 4.12.10 (Arch Linux).
Ponkadoodle du
@wallacoloo peut-être modifier printf () en perror () et voir quelle était l'erreur réelle. reportez-vous à man7.org/linux/man-pages/man2/unshare.2.html pour connaître les codes d'erreur pouvant résulter d'un unshareappel infructueux . Vous pouvez également essayer cette version de python qui pourrait avoir une meilleure messagerie d'erreur: github.com/cheshirekow/uchroot
cheshirekow
1
En fait, @wallacoloo, cela ressemble à arch désactive les espaces de noms d'utilisateurs non privilégiés dans sa construction du noyau: lists.archlinux.org/pipermail/arch-general/2017-F February
cheshirekow
0

Non. Si je me souviens bien, chroot fait quelque chose au niveau du noyau qui l'empêche. Je ne me souviens pas de ce que c'était. Je l'ai étudié en jouant avec l'outil Catalyst Build de Gentoo (et un chroot sur gentoo est identique à un chroot sur ubuntu). Bien qu'il soit possible de le faire sans un mot de passe ... mais de telles choses sont laissées au domaine des failles de sécurité potentielles et s'assurent que vous savez ce que vous faites.

xénoterracide
la source