新钛云服已为您服务1250天
因为一个关于Docker容器安全的事件,把曾一度以稳定性和安全性著称的Docker,演绎成了拥有特权漏洞的容器引擎,使其能够直接访问底层宿主机,就好比CVE-2020-27352安全漏洞导致代码在主机上执行,一夜之间,Docker容器的安全性形同虚设:
Docker在一夜之间更改了cgroup,这使我们能够提升权限并获得主机的root访问权限。我们能够利用cgroups在主机上运行反向Shell并获得代码执行权限。
此问题是由于Canonical的Snap中的配置错误而导致的,并且影响了许多产品。它被指定为CVE-2020-27352
CVE-2020-27352
在为docker生成systemd服务单元时,snapd未指定"Delegate=yes",结果systemd会将进程从这些容器中移入主守护程序的cgroup中。重新加载系统单元时会自动对齐。这可能会向快照中的容器授予原本不希望的其他特权。
一、Linux容器–命名空间和cgroup
Docker是利用cgroup和namespaces来创建安全,可靠和强大的隔离框架。为了构建轻量级容器,需要创建有效的资源管理和隔离,为了使我们能够虚拟化系统环境,Linux内核以namespace和cgroups来提供低级隔离机制。
从根本上说,namespace是限制Linux进程树对各种系统实体(例如网络接口,进程树,用户ID和文件系统安装)的访问和可见性的机制。
另一方面,Linux cgroups功能不仅提供了一种限制机制,而且还可以管理和说明一组进程的资源使用情况。它限制并监视系统资源,例如CPU时间,系统内存,磁盘带宽,网络带宽等。
这样看上去cgroup看起来非常安全。是这样吗?如果有人在Docker容器上错误配置了cgroups那该怎么办?
让我们看看Docker在其“安全”网页上对cgroup的评价:
起到阻止一个容器访问或影响另一个容器的数据和进程的作用,它们对于抵御某些拒绝服务攻击非常重要。它们在公共和私有PaaS之类的多租户平台上尤其重要,即使在某些应用程序出现异常时,也要保证一致的正常runtime。
这就是说,如果Docker容器的cgroup配置错误的话,我们面临的最糟糕的情况就是拒绝服务。
二、Devices cgroup的特殊案例
尽管cgroup被描述为实现资源核算和限制的机制,但“内核” cgroups文档中的“Devices” cgroup(也称为“设备白名单控制器”)比较特殊。因此,Devices cgroup的作用被描述为:
实施cgroup来跟踪并强制执行对设备文件的打开和mknod限制…”
红帽Linux指南对此不透明的定义提供了一些启示:
设备子系统允许或拒绝cgroup中的任务访问设备。
回到内核cgroups文档,可以清晰的看到:
访问权限是r,w和m的组合。
确切地说,从我们的安全角度出发,无论是创建,读取还是写入,都要对 Linux内核的设备禁止各种访问。
受此白名单机制控制的设备可以是内核使用的任何设备。也包括安全的设备,例如/dev/null和/dev/zero,还包括USB设备(例如/dev/uhid),cdroms(/dev/ cdrom),甚至内核的硬盘(例如/dev/sda设备)。
总结来说:Devices cgroup是cgroup子系统中的一个特殊的组成部分,因为它不仅一种“资源核算和限制”机制,而且还是一个内核设备白名单控制器,与系统的资源耗尽相比,它可能造成更大的破坏。
三、从Docker默认容器到主机上的RCE
Systemd是linux下的一种初始化软件,为系统提供了很多系统组件。它旨在统一不同Linux之间的服务配置,并被大多数Linux发行版广泛采用。
Systemd的主要组件之一是服务管理器,它用于初始化系统,并且引导系统用户空间和管理用户进程。
作为其操作的一部分,systemd创建并管理监视各种cgroup。Systemd的cgroup管理理念基于一些设计思想,包括systemd官方网站引用的“单写者规则”:
单写者规则:这意味着每个cgroup中只有一个单写者,即一个进程管理它。只有一个进程应该拥有一个特定的cgroup,并且当它拥有该cgroup时,它是排他性的,没有其他东西可以同时对其进行操作。
该规则对docker系统有深远的影响。
如果容器管理器在系统cgroup中创建和管理cgroup,会违反规则,因为cgroup由systemd管理,因此对其他所有人都没有限制。
在systemd的控制下,用于管理系统cgroup层次结构中的cgroup的容器runtime违反了此规则,这可能会干扰systemd对cgroup的管理。你可能已经猜到,配置错误的systemd服务可能假装管理自己创建的cgroup,实际上,systemd从上方监督所有事务:管理,创建和删除服务之下的cgroup ,但是上层根本没有注意到。
这是容器转型的核心。
当systemd重新加载一个单元时,它首先清理混乱的cgroup,将子cgroup中产生的所有进程移到较高的进程。特别是,如果systemd管理dockerd服务,它将在重新加载时清除所有docker容器的cgroup,从而将容器进程保留在较高的cgroup子系统层次结构中。
为什么systemd会突然重新加载?
系统重装比人们想象的要复杂得多。在启用服务,禁用一项服务,添加服务依赖项等之后,它将重新加载其任何服务的配置文件。这意味着,如果某些事情导致系统服务发生更改,那么即使是不太活跃的服务也可能容易受到影响。
Debian的“无人值守升级就是此类事物的一个著名例子。
无人值守升级是Debian软件包管理系统之一,其主要目的是“通过最新的具有安全性(及其他)更新自动使计算机保持最新状态。”
无人值守升级是一项定期任务,它在预配置的时间内运行一次。它会自动下载并安装安全更新,并且默认情况下会在包括Ubuntu桌面系统和服务器系统在内的各种系统上启用。升级某些服务时,它们的systemd单元配置会更改,这导致systemd重新加载整个系统,如下:
$