背景
国外安全研究员champtar[1]在日常使用中发现Kubernetes tmpfs挂载存在逃逸现象,研究后发现runC存在条件竞争漏洞,可以导致挂载逃逸[2]。
关于条件竞争TOCTOU和一些linux文件基础知识可见这篇文章《初探文件路径条件竞争 - TOCTOU&CVE-2019-18276》[3]。
CVE-2021-30465在Redteam的研究者视角中比较鸡肋,因为需要K8S批量创建POD的权限。但在产品安全的视角恰恰相反,针对Caas(Container as a service)类产品,用户/租户拥有批量创建POD权限,利用挂载逃逸可打破租户间隔离,同时读取Host层面某些敏感数据,危害性极大。
RunC简介
为了让容器生态更加开放,Linux基金会发起OCI(Open Container Initiative),目标是标准化容器格式和运行时[4],其中一个重要产物就是CRI(Container Runtime Interface),抽象了容器运行时接口,使得上层调控容器更加便捷。containerd和runC都是其中代表产物,从dockerd中再剥离出containerd[5],向上提供rpc接口,再通过containerd去管理runC。containerd在初期也是直接对runC进行管理,但为了解决containerd进行升级等操作时会造成不可用的问题,containerd再拆出containerd-shim,独立对接runC。containerd从Runtime、Distribution、Bundle维度提供容器全生命周期的管理能力[6],runC专注于Runtime。
容器设备挂载相关基础知识
Namespace
Namespace是linux控制系统资源的抽象层,将不同的进程放置入不同的Namespace将获得不同的资源视角,该项技术是容器实现的基础[7]。
linux提供8种不同的Namespace以提供不同维度的隔离能力,分别是:
1. Cgroup
2. IPC
3. Network
4. Mount
5. PID
6. Time
7. User
8. UTS
其中,Cgroup和Mount Namespace是最常接触的,在容器挂载相关能力均通过Mount Namespace进行实现。Namespace的使用主要通过clone和unshare 两个方法实现,其中clone创建新进程时,标志位为CLONE_NEW*将会创建新的Namespace并将子进程放入该Namespace[8],unshare方法将调用进程放入不同的Namespace中[9]。
Mount Namespace
Linux中有一个很核心的思想,那就是一切皆文件。在该思想下,Linux通过挂载对不同设备中的文件进行管理。在Linux中,每一个空目录/文件都可以成为挂载点并设置相应的属性。在Mount Namespace下,处在当前Namespace中的进程只对当前Namespace中的挂载点可见,通过
/proc/[pid]/mounts