나는 읽었다initramfs에 관한 Linux 문서그리고소스 코드의 switch_root
.
문서에는 다음과 같이 나와 있습니다.
다른 루트 장치로 전환할 때 initrdivov_root를 실행한 다음 램디스크를 제거합니다. 그러나 initramfs는 rootfs입니다. ivot_root rootfs를 사용하거나 마운트 해제할 수 없습니다. 대신, rootfs의 모든 항목을 삭제하여 공간을 확보하세요(-xdev / -exec rm '{}' ';' 찾기).새로운 루트로 rootfs 덮어쓰기(cd /newmount; mount --move ./; chroot .), stdin/stdout/stderr을 새 /dev/console에 연결하고 새 init를 실행합니다.
그리고 switch_root
다음과 같은 일을 합니다:
if (chdir(newroot)) {
warn(_("failed to change directory to %s"), newroot);
return -1;
}
...
if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) {
close(cfd);
warn(_("failed to mount moving %s to /"), newroot);
return -1;
}
...
if (chroot(".")) {
close(cfd);
warn(_("failed to change root"));
return -1;
}
왜 마운트 지점을 다른 곳으로 옮겨야 합니까 /
?
new_root에 대한 chroot만으로는 충분하지 않은 이유는 무엇입니까?
답변1
편집하다: 편집해주신 @timothy-baldwin님께 감사드립니다.
new_root
하이퍼마운팅은 /
마운트된 네임스페이스의 루트 디렉터리를 변경하고, 하이퍼마운팅 없이 루트를 변경하면 /
시스템이 환경에 있게 됩니다 chroot
(루트 디렉터리는 마운트된 네임스페이스의 루트 디렉터리와 일치하지 않습니다).
이로 인해 다음과 같은 몇 가지 문제가 발생할 수 있습니다.
1. chroot 내에서는 사용자 네임스페이스 생성이 허용되지 않습니다.
에 따르면 man 2 unshare
chroot 환경 내에서는 unshare
사용자 네임스페이스를 검색하는 작업이 실패합니다 .EPERM
EPERM (since Linux 3.9) CLONE_NEWUSER was specified in flags and the caller is in a chroot environment (i.e., the caller's root directory does not match the root directory of the mount namespace in which it resides).
$ unshare -U
unshare: unshare failed: Operation not permitted
2. 마운트 네임스페이스를 입력하면 루트 디렉터리가 네임스페이스의 루트 디렉터리로 설정됩니다.
마운트 네임스페이스를 입력하면 프로세스의 루트가 마운트 네임스페이스의 루트로 설정되므로 setns
마운트 네임스페이스에서 작업을 수행하면 루트가 rootfs 디렉터리로 설정됩니다.
$ nsenter -m/proc/self/ns/mnt /bin/sh
$ ls -ld /new_root
new_root
new_root 디렉토리가 내 chroot 외부에 있음을 알 수 있습니다.
위에 마운트해도 /
chroot 탈출이 실제로 방지되지는 않습니다.
루트 사용자는 umount
이 디렉터리에 마운트 네임스페이스( )를 다시 입력 setns
하고 rootfs를 볼 수 있습니다.
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <unistd.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
int main() {
int ns = open("/proc/self/ns/mnt", O_RDONLY);
if (ns == -1) {
perror("open");
goto out;
}
if (umount2("/", MNT_DETACH)) {
perror("umount2");
goto out;
}
if (setns(ns, CLONE_NEWNS)) {
perror("setns");
goto out;
}
char *a[] = { "/bin/sh", NULL };
char *e[] = { NULL };
execve(a[0], a, e);
perror("execve");
out:
return 1;
}
$ gcc -o main main.c
$ unshare -m ./main
/ # ls -d new_root
new_root
/ # mount -t proc proc /proc
/ # cat /proc/mounts
none / rootfs rw 0 0
proc /proc proc rw,relatime 0 0
chroot 이탈을 방지하려면 new_root
over를 설치해야 합니다./
최소한의 initramfs를 생성하고 switch_root
바이너리를 다음 쉘 스크립트로 대체하여 쉘을 얻었습니다.
#!/bin/sh
exec /bin/sh
/bin/sh
또한 initramfs 내부의 정적 링크로 변경되었습니다 busybox
.
다음 코드를 컴파일하고 정적으로 연결합니다.
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int fd = open(".", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
perror("open");
goto out0;
}
if (chroot("tmp")) {
perror("chroot");
goto out1;
}
if (fchdir(fd)) {
perror("fchdir");
goto out1;
}
if (chdir("..")) {
perror("chdir");
goto out1;
}
char *const argvp[] = { "sh", NULL };
char *const envp[] = { NULL };
execve("bin/sh", argvp, envp);
perror("execve");
out1:
close(fd);
out0:
return 1;
}
내 실제 루트 파일 시스템의 루트 디렉터리에 /escape
.
switch_root
이런 일이 발생하기 전에 재부팅하고 쉘을 얻으십시오.
루트를 과도하게 설치할 필요가 없습니다.
$ mount --move proc new_root/proc
$ mount --move dev new_root/dev
$ mount --move sys new_root/sys
$ mount --move run new_root/run
$ exec chroot new_root
$ ./escape
$ ls -d new_root
new_root
나는 chroot에서 탈출했다.
과부하 루트 포함
$ mount --move proc new_root/proc
$ mount --move dev new_root/dev
$ mount --move sys new_root/sys
$ mount --move run new_root/run
$ cd new_root
$ mount --move . /
$ exec chroot .
$ ./escape
$ ls -d new_root
ls: cannot access 'new_root': No such file or directory
나는 chroot에서 벗어날 수 없습니다.
답변2
rootfs를 오버로드하지 않으면 사용자 및 마운트 네임스페이스가 손상됩니다.
setns
호출자 루트를 마운트된 네임스페이스의 루트로 설정하면 시스템 호출이 취소됩니다chroot
.- 프로세스 루트가 마운트된 네임스페이스의 루트가 아닌 경우 권한이 없는 프로세스는 사용자 네임스페이스를 생성할 수 없습니다.