이 글을 이해하기 위해 mount namespace이해가 필요합니다. 저의 이전 글을 참고해주세요.
1. 최소 파일을 갖는 컨테이너
컨테이너의 장점은 애플리케이션 실행에 필요한 최소 파일과 라이브러리를 관리합니다.
예를 들어 자바 8을 실행하는 컨테이너는 자바8실행파일과 자바8에 관련된 라이브러리가 있습니다. 자바11을 실행하는 컨테이너도 자바11실행파일과 자바11에 관련된 라이브러리가 있습니다. 서버에서 자바 8과 자바11을 실행한다면, 자바8과 11 실행파일 모두 가지고 있어야되고 관련 라이브러리를 설치해야 합니다. 최악의 경우 라이브러리 충돌로 인해 설치가 안되는 슬픈상황이 발생합니다.
2. 컨테이너가 개별적으로 최소 파일을 가질 수 있는 이유
이전 글에서 설명한 것처럼 컨테이너는mount namespace를 사용하여 자기만의 파일 시스템을 가집니다. 따라서 컨테이너가 설치한 실행파일과 라이브러리는 다른 컨테이너에 영항을 가지 않습니다.
3. 컨테이너는 어떻게 파일을 실행할까?
컨테이너에서 파일 실행하는 방법은 특별하지 않습니다. 운영체제에서 파일을 실행하는 것과 동일합니다.
{실행파일 경로}
4. 실습
4.1 sh쉘 실행 준비
sh 실행파일을 실행하기 위해 아래처럼 sh바이너리와 sh실행하기 위한 라이브러리를 /root/base_dir경로에 준비했습니다.
위 그림처럼 준비하기 위해 디렉터리를 생성합니다.
mkdir /root/bash_dir
mkdir /root/bash_dir/bin
mkdir -p /root/bash_dir/lib/aarch64-linux-gnu
sh실행파일을 생성한 디렉터리로 복사합니다.
cp /bin/sh /root/bash_dir/bin
sh가 참조하는 라이브러리는 ldd명령어로 확인합니다.
ldd /bin/sh
ldd로 확인하는 라이브러리를 이전에 생성한 디렉터리로 복사합니다.
cp /lib/aarch64-linux-gnu/libc.so.6 /root/bash_dir/lib/aarch64-linux-gnu/
cp /lib/ld-linux-aarch64.so.1 /root/bash_dir/lib/
4.2 sh쉘 실행
준비한 sh를 실행해봅시다. mount namespace를 생성하면서 sh쉘을 실행합니다.
unshare -m /root/bash_dir/bin/sh
sh쉘이 잘 실행되었습니다.
4.3 논리적인 오류 확인
sh 쉘이 실행되는 것을 보면 잘 동작하는 것처럼 보입니다. 하지만 sh 실행 파일이 참조하는 라이브러리 경로가 잘못 설정되어 있습니다. 제가 준비한 라이브러리 경로를 사용하지 않고, /lib 경로를 참조하고 있습니다.
strace를 사용하여 sh실행파일이 참조하는 라이브러리를 확인할 수 있습니다.
strace -e trace=openat unshare -m /root/bash_dir/bin/sh -c "exit" 2>&1 \
grep -E "openat.*(lib.*\.so)"
strace명령어 결과를 분석하면, 리눅스 sh실행 파일이 라이브러리를 참조할 때 /lib 또는 /lib64 경로를 사용하고 있다는 것을 알 수 있습니다. 리눅스 실행파일이 아니더라도 개발에 사용하는 SDK 등 /lib, /lib64디렉토리 경로를 참조하는 경우가 많습니다.
4.4 논리적인 오류 수정 chroot
앞서 본 논리적인 오류는 실행파일이 우리가 준비한 라이브러리 경로를 바라보지 않는 것입니다. 이 문제를 해결하기 위해 root디렉터리를 우리가 준비한 디렉터리 경로로 변경하면 됩니다. 이 예제에서는 /root/bash_dir디렉터리가 root디렉터리가 되야 합니다.
현재 root 디렉터리 경로는 부모 프로세스의 root디렉터리입니다. mount namespace를 생성할 때 부모 프로세스 mount point를 복사했기 때문입니다.
root디렉터리를 변경하려면 chroot syscall을 사용하면 됩니다.
man chroot
chroot 사용방법은 아래와 같습니다.
chroot {변경할 root 디렉터리 경로} {실행할 명령어}
이 예제에서 root 디렉터리를 변경하려면 아려처럼 chroot를 사용합니다.
unshare -m chroot /root/bash_dir/ /bin/sh
chroot로 root 디렉터리를 변경하면 ls명령어를 실행하지 못합니다. sh실행파일만 준비했고 ls실행파일은 준비하지 않았기 때문입니다.
4.5 root 디렉터리 확인방법
ls명령어를 사용할 수 없기 때문에 다른 방법으로 root 디렉터리를 확인해야 합니다. root 디렉터리는 /proc에서 확인할 수 있습니다. process id는 unshare로 실행한 /bin/sh쉘에서 확인해야 합니다.
# unshare로 실행한 /bin/sh쉘에서 실행
$ echo $$
19560
# host에서 실행
$ ls -l /proc/19560/root
/proc/19560/root -> /root/bash_dir
/proc에서 root 디렉터리를 확인하면chroot경로에 설정한 /root/bash_dir가 root 디렉터리로 되어 있습니다.
5. chroot의 취약점
chroot로 변경된 root디렉터리는 디렉터리 탐색 공격(Directory Traversal)에 취약합니다. root 디렉터리가 절대경로로 변경된게 아니라 심볼릭 링크로 변경되었기 때문에, “cd ../../”같이 root 디렉터리의 상위 디렉터리로 이동이 가능합니다.
root 디렉터리의 상위 디렉터리로 이동하는 것을 탈옥이라고 부릅니다. chroot의 탈옥 문제를 해결하려면 chroot대신 pivot_root라는 syscall을 사용하면 됩니다.
참고자료
이하공백
'전공영역 공부 기록' 카테고리의 다른 글
Istio ServiceEntry설정할 때 IP만 사용하려면? (0) | 2024.09.04 |
---|---|
AWS security group 체이닝이 불가능한지 확인하는 방법 (0) | 2024.09.04 |
linux의 mount namespace (0) | 2024.09.01 |
맥북에서 vagrant로 vmware fusion 실행시 No such file or directory: vagrant-utility.client.crt 에러 (0) | 2024.08.28 |
EKS 애드온의 버전별로 호환되는 EKS 버전을 확인하는 스크립트 (0) | 2024.08.25 |