
CVE-2019-5736용 PoC
CVE-2019-5736용 PoC
@singe, @_cablethief, @feexd의 도움으로 제작되었습니다.
Ubuntu 18.04, Debian 9, Arch Linux에서 테스트했습니다. Docker 버전은 18.09.1-ce 및 18.03.1-ce입니다. 이 PoC는 현재 Ubuntu 16.04 및 CentOS에서는 작동하지 않습니다.
취약점을 발견한 Dragon Sector의 익스플로잇 코드는 여기에서 확인하세요.
이것은 Docker용 컨테이너 탈출(container escape)인 CVE-2019-5736의 Go 구현입니다. 이 익스플로잇은 컨테이너 내부에서 호스트 시스템의 runc 바이너리를 덮어쓰고 실행하는 방식으로 동작합니다.
이 익스플로잇에는 2가지 사용 사례가 있습니다. 첫 번째(이 리포지토리가 해당하는 경우)는 본질적으로 함정입니다. 공격자는 컨테이너 내부에서 명령 실행 권한을 얻은 다음 대기(listen)하는 악성 바이너리를 시작해야 합니다. 누군가(공격자 또는 피해자)가 docker exec를 사용하여 컨테이너에 들어가면 익스플로잇이 트리거되어 루트 권한으로 코드 실행이 가능해집니다.

두 번째(이 리포지토리가 아닌 경우)는 악성 Docker 이미지를 만듭니다. 해당 이미지가 실행되면 익스플로잇이 발동합니다. 컨테이너에 exec할 필요가 없습니다. GIF 예시는 이 README의 하단을 참조하세요.
이 취약점을 악용하려면 컨테이너 내부에서 root(uid 0) 권한이 있어야 합니다.
네, runc 구현을 덮어쓰게 되며 시스템에서 더 이상 Docker 컨테이너를 실행할 수 없게 됩니다. /usr/bin/docker-runc 또는 /usr/bin/runc 중 하나를 백업하세요. (어느 것을 가지고 있는지에 따라 다르며 /usr/sbin도 확인하세요.)
원하는 대로 코드를 수정하고 go build main.go로 컴파일하세요. 해당 바이너리를 탈출하려는 컨테이너로 이동하세요. 바이너리를 실행하면 다음에 누군가가 컨테이너에 연결하여 /bin/sh를 호출할 때 페이로드가 발동합니다.
이 PoC는 lxc 프로젝트에 대한 이 커밋의 훌륭한 설명(다른 사람들의 유용한 조언과 함께)을 바탕으로 제작되었습니다.
예를 들어 대상 바이너리가 /bin/bash인 경우, 인터프리터 경로 #!/proc/self/exe를 지정하는 실행 가능한 스크립트로 대체될 수 있습니다(/proc/self/exe는 커널이 모든 프로세스에 대해 생성하는 심볼릭 링크로, 해당 프로세스에서 실행된 바이너리를 가리킵니다). 따라서 컨테이너 내부에서 /bin/bash가 실행되면 /proc/self/exe의 대상이 실행됩니다. 이 대상은 호스트의 runc 바이너리를 가리키게 됩니다.
우리는 컨테이너의 /bin/sh를 #!/proc/self/exe로 덮어써서 이 프로세스(Docker exec)를 시작한 바이너리를 가리키도록 합니다.

그런 다음 공격자는 /proc/self/exe의 대상에 쓰기를 시도하여 호스트의 runc 바이너리를 덮어쓸 수 있습니다. 그러나 일반적으로 runC가 실행되는 동안 커널이 덮어쓰기를 허용하지 않으므로 성공하지 못합니다. 이 문제를 극복하기 위해 공격자는 O_PATH 플래그를 사용하여 /proc/self/exe에 대한 파일 디스크립터를 연 다음 /proc/self/fd/를 통해 바이너리를 O_WRONLY로 다시 열어 별도의 프로세스에서 busy loop로 쓰기를 시도할 수 있습니다.
참고: 이전 섹션의 일부 내용은 완전히 정확하지 않습니다. runcinit의 파일 디스크립터를 얻을 때 O_PATH 플래그를 사용할 필요가 없습니다. 또한 다른 프로세스에서 쓰기 루프를 만들 필요도 없습니다. /proc/PID/exe에 대한 파일 핸들을 얻어 runcinit의 파일 디스크립터를 얻습니다. 그런 다음 해당 핸들을 사용하여 /proc/self/fd/FILEDESCRIPTOR에 대한 파일 핸들을 얻습니다. 이것이 쓰기에 사용할 파일 핸들입니다.

궁극적으로 runC 바이너리가 종료되면 성공합니다. 그 후 runC 바이너리는 손상되며 다른 컨테이너나 호스트 자체를 공격하는 데 사용될 수 있습니다.
해당 파일 핸들에 쓸 수 있다면 호스트의 runc 바이너리를 덮어쓴 것입니다. 루트 권한으로 임의의 명령을 실행할 수 있습니다.
이 리포지토리에는 이 예시가 포함되어 있지 않지만 여기에서 찾을 수 있습니다. 제 생각에는 이것이 훨씬 더 위험한 시나리오입니다. 악성 컨테이너 이미지를 실행하기만 하면 루트 권한으로 코드 실행이 가능합니다. 자세한 설명은 이 글을 참조하세요.
