web-dev-qa-db-ja.com

Ubuntu-非rootユーザーはchroot刑務所でプロセスを実行できますか?

非rootユーザーがUbuntuでchrootプロセスを実行することは可能ですか?

19
Hawkeye

Linuxでは、 chroot(2) システムコールは特権を持つプロセスのみが実行できます。プロセスが必要とする機能はCAP_SYS_CHROOTです。

ユーザーとしてchrootできない理由は非常に簡単です。何かを許可されているかどうかを/ etc/sudoersでチェックするSudoなどのsetuidプログラムがあるとします。次に、独自の/ etc/sudoersを使用してchroot chrootに配置します。突然、特権のエスカレーションがすぐに発生します。

プログラムをそれ自体をchrootし、setuidプロセスとして実行するようにプログラムを設計することは可能ですが、これは一般に悪い設計と見なされます。 chrootの追加のセキュリティは、setuidのセキュリティ問題の動機にはなりません。

14
pehrs

@ imz--IvanZakharyaschevは、名前空間の導入で可能になる可能性があるというpehrsの回答にコメントしていますが、これはテストされておらず、回答として投稿されていません。はい、それは確かに非rootユーザーが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:unshare failed:操作は許可されていません

nshare(2) ドキュメントと一致します:

[〜#〜] eperm [〜#〜](Linux 3.9以降)

CLONE_NEWUSERflagsで指定され、呼び出し元がchroot環境にある(つまり、呼び出し元のルートディレクトリ)が存在するマウント名前空間のルートディレクトリと一致しません)。

7
hvd

最近では、chroot/BSD jailの代わりにLXC(Linux Containers)を見たいと思っています。これはchrootと仮想マシンの間のどこかにあり、多くのセキュリティ制御と一般的な設定機能を提供します。ユーザーとして必要なのは、必要なファイル/デバイスを所有するグループのメンバーになることだけですが、機能/システム権限も関係している可能性があります。いずれにしても、LinuxカーネルにSELinuxなどが追加されてからかなり後のことですが、LXCはごく最近のことなので、それは非常に可能です。

また、スクリプトをrootとして作成するだけで、Sudoを使用してそれらのスクリプトを実行するための安全なアクセス許可をユーザーに与えることもできます(必要に応じて、パスワードなしで、スクリプトが安全であることを確認してください)。

2
Lee B

Fakeroot/fakechrootの組み合わせは、ファイルがrootによって所有されているように見えるtarアーカイブを作成するなどの単純なニーズに対して、chrootのシミュレーションを提供します。 Fakechrootのマンページは http://linux.die.net/man/1/fakechroot です。

ただし、新しい権限は取得しませんが、呼び出す前にディレクトリ(fake-distroなど)を所有している場合

fakechroot fakeroot chroot ~/fake-distro some-command

これで、rootで、偽のディストリビューション内のすべてを所有しているようなコマンドを探します。

1
sylvainulg

ユーザー名前空間を使用すると、実際にはrootなしでchrootを実行できるようです。以下は、それが可能であることを示すサンプルプログラムです。私はLinux名前空間がどのように機能するかを調査し始めたばかりなので、このコードがベストプラクティスであるかどうかは完全にはわかりません。

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にコピーされました。

1
cheshirekow

いいえ。私が正しく思い出せば、chrootがカーネルレベルでそれを妨げていることがいくつかあります。そのことを思い出せません。 GentooのCatalyst Buildツールをいじくったとき、私はそれを調査し直しました(そしてgentooのchrootはubuntuのchrootと同じです)。 passwdなしでそれを実行することは可能ですが...そのようなことは、潜在的なセキュリティの脆弱性の領域に任されており、あなたが何をしているかを確実に知っています。

0
xenoterracide