Portuguese English German

Preventing Docker Escaping Attacks

Disclaimer: Docker is not super secure for isolation. This post aims to show not-so-well-known Docker features and how they could be used in other contexts.

In my last post "Docker for Automating Honeypots or Malware Sandboxes", some people were concerned about Docker not being safe enough to use as a malware sandbox, given articles like From webshell to Docker container escape with DVWA and dirtyc0w. The article speaks for itself, and containers are not replacements for virtual machines, which provide better isolation. However, I'd like to discuss two protection mechanisms that you could apply to protect against attackers who can execute arbitrary code in the container:

1) Run the container as a non-root user

By default, user IDs (UIDs) are mapped directly from the host to the container. This means that a root user in the container has the same capabilities as the host. This becomes noticeable when there is a shared volume:

# First, we create a secret file on the host
# And change its permission to be readable only by root
$ sudo su
root$ mkdir -p /home/anderson/deleteme
root$ echo 'secret!' > /home/anderson/deleteme/secret_file
root$ chmod 600 /home/anderson/deleteme/secret_file
# [ Back to normal user ]

# Then, we start a container sharing that directory (deleteme)
$ docker run --detach --name hackme -v $(pwd)/deleteme:/deleteme ubuntu sleep 2000

# And we try to access the secret from inside the container
$ docker exec -it hackme bash
root@883384cec818:/$ cd /deleteme/
root@883384cec818:/deleteme$ ls -lah
total 20
drwxr-xr-x  2 1000 1000 4096 Jan 23 11:48 .
drwxr-xr-x 33 root root 4096 Jan 23 11:49 ..
-rw-------  1 root root    8 Jan 23 11:48 secret_file
root@883384cec818:/deleteme$ cat secret_file 

# If we create another user, we can avoid this
# Let's try using a 'normal user'
root@883384cec818:/deleteme$ adduser normal_user
root@883384cec818:/deleteme$ su - normal_user
normal_user@883384cec818:~$ cd /deleteme/
normal_user@883384cec818:/deleteme$ cat secret_file
cat: secret_file: Permission denied

You should also disable sudo. For Alpine images for example, it comes without sudo by default.

2) Use a read-only file system

It's common for attackers to create files after successfully exploiting systems. One control for that is to make the entire system read-only. You can apply the read-only (ro) flag to individual volumes as well, e.g., docker run (...) -v volumeOnHost:volumeOnContainer:ro (...).

# Create a new container, but this time read-only
$ docker run --detach --name hackme --read-only=true ubuntu sleep 2000

# Connect to this container and try to create a file
# Note: changing {content, permissions}, or removing existing files won't work either
$ docker exec -it hackme bash
root@61ee2c744c4a:/$ touch some_file
touch: cannot touch 'some_file': Read-only file system

The drawback is that some applications still need to write temporary files. For that, you can use a temporary file system without execution or suid files, as pointed out in this article:

# Start the container as read-only, but with /etc read-write, with no exec
$ docker run --detach --name hackme --read-only=true --tmpfs /etc:rw,noexec,nosuid,size=2g ubuntu sleep 2000

# Try creating and executing a shell script from /etc
$ docker exec -it hackme bash
bash-4.3$ cd /etc
bash-4.3$ touch test
bash-4.3$ echo '#!/bin/bash' >> test
bash-4.3$ echo 'echo 1111111' >> test
bash-4.3$ chmod +x test
bash-4.3$ ls -lah test
-rwxr-xr-x 1 0 0 25 Jan 23 12:34 test
bash-4.3$ ./test
bash: ./test: Permission denied

That's all for today.

Thank you.

Share on Twitter Share on Facebook Share on LinkedIn Share on Hacker News

Popular Posts