漏洞简介

containerd是行业标准的容器运行时,可作为Linux和Windows的守护程序使用。在版本1.3.9和1.4.3之前的容器中,容器填充的API不正确地暴露给主机网络容器。填充程序的API套接字的访问控制验证了连接过程的有效UID为0,但没有以其他方式限制对抽象Unix域套接字的访问。这将允许在与填充程序相同的网络名称空间中运行的恶意容器(有效UID为0,但特权降低)导致新进程以提升的特权运行。

影响版本

containerd < 1.4.3
containerd < 1.3.9

安全版本

containerd >= 1.4.3
containerd >= 1.3.9

漏洞复现

1、首先安装有漏洞的containerd版本
2、用root用户以共享主机网络的方式启动容器 sudo docker run -itd –network=host ubuntu:latest /bin/bash
3、在容器内执行cat /proc/net/unix | grep 'containerd-shim' | grep '@'可看到抽象命名空间Unix域套接字
4、根据漏洞描述通过图片中的抽象命名空间Unix域套接字可访问dockerd-shim rpc api
null

漏洞应急与自测

为了便于验证用户环境中是否存在该漏洞,我们提供了poc镜像。使用方式如下:
sudo docker run -it --rm -v /:/host/ -v /var/run/docker.sock:/var/run/docker.sock --net=host hub.dosec.cn/library/poc:CVE-2020-15257
null

为了便于用户在集群中验证该漏洞,可使用如下命令查看共享了主机网络的pod
kubectl get pod --all-namespaces -o custom-columns=namespace:.metadata.namespace,CONTAINER:.spec.containers[0].name,NetWork:.spec.hostNetwork,hostname:.spec.nodeName,nodeIP:.status.hostIP | grep true

null

当然也可以使用如下编排文件进行检测

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: cve-2020-15257
  namespace: cvetest
spec:
  selector:
    matchLabels:
      name: cve
  template:
    metadata:
      name: cve-2020-15257
      labels:
        name: cve
    spec:
     # restartPolicy: always
      containers:
      - name: cve-2020-15257
        image: dosecteam/pocs:CVE-2020-15257
        securityContext:
          privileged: true
        env:
        - name: KUBERNETES_NODENAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: spec.nodeName
        volumeMounts:
          - name: host-fs
            mountPath: /host
          - name: socket-fs
            mountPath: /var/run/docker.sock
      hostNetwork: true
      volumes:
        - name: host-fs
          hostPath:
            path: /
        - name: socket-fs
          hostPath:
            path: /var/run/docker.sock

执行如下命令:

kubectl create ns cvetes
kubectl apply -f checkcve.yaml
kubectl get po -n cvetest|awk '{print $1}' | xargs -i kubectl logs -n cvetest  {}

漏洞利用附非完整exp

参考containerd官网源码,我们可以在容器内访问到该socket文件。然后可启动一个新的容器,该容器挂载宿主机根目录到容器内的/host目录,即可实现对宿主机完全读写,达到容器逃逸的目的。

img

下面为非完整exp。请自行脑补。

package main

import (
    "fmt"
    "net"
    "os"
    "regexp")

func getshimunixpath() (string, error) {
    file, err := os.Open("/proc/net/unix")
    if err != nil {
        return "", err
    }
    var b []byte = make([]byte, 0x1fff)
    file.Read(b)
    defer file.Close()
    socklist := string(b)

    regString := "/containerd-shim/moby/[a-f 0-9]{64}/shim.sock"
    reg, _ := regexp.Compile(regString)
    path := reg.FindString(socklist)

    if path == "" {
        err = fmt.Errorf("no sock file found")
        return "", err
    }
    path = "\x00" + path
    return path, err
}

func main() {
    shimunixpath, err := getshimunixpath()
    if err != nil {
        fmt.Println(err)
        return
    }
    conn, err := net.Dial("unix", shimunixpath)
    if err != nil {
        fmt.Println(err)
        return
    }
    //do something with this connection
    //此处省略关键信息,自行脑部
    //此处省略关键信息,自行脑部
    //此处省略关键信息,自行脑部

    defer conn.Close()
}

漏洞修复

  1. 升级 containerd 至最新版本。
  2. 通过添加如 deny unix addr=@**的AppArmor策略禁止访问抽象套接字。

应当注意,应停止并重新启动使用旧版本的容器填充垫片启动的容器,因为即使在升级后,运行中的容器仍将继续受到攻击。如果您没有为不受信任的用户提供在与垫片相同的网络名称空间中启动容器的能力(通常是“主机”网络名称空间,例如docker run –net = host或hostNetwork:在Kubernetes窗格中为true),并且以有效UID为0运行时,您就不会受到此问题的影响。

转载自https://mp.weixin.qq.com/s/8Zel4oPXdctUE1kotti8Yw

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐