云原生安全:攻防实践与体系构建
上QQ阅读APP看书,第一时间看更新

3.4.3 资源耗尽型攻击

同为虚拟化技术,容器与虚拟机既存在相似之处,也有显著不同。在资源限制方面,无论使用VMware、Virtual Box还是QEMU,我们都需要为即将创建的虚拟机设定明确的CPU、内存及硬盘资源阈值。在虚拟机内部的进程看来,它真的处于一台被设定好的独立计算机之中;然而,容器运行时默认情况下并未对容器内进程在资源使用上做任何限制,以Pod为基本单位的容器编排管理系统在默认情况下同样未对用户创建的Pod做任何CPU、内存使用限制。

缺乏限制使得云原生环境面临资源耗尽型攻击的风险。攻击者可能通过在一个容器内发起拒绝服务来占用大量宿主机资源,从而影响到宿主机自身或宿主机上其他容器的正常运行。注意,这里我们讨论的是默认配置下的资源限制缺失,从而导致容器的隔离性在一定程度上失效(影响到容器外系统或服务的正常运行),而非针对某容器本身的拒绝服务攻击。

常见容易受影响的资源如下:

1)计算资源:CPU、内存等。

2)存储资源:本地硬盘等。

3)软件资源:内核维护的数据结构等。

4)通信资源:网络带宽等。

接下来我们分别讨论这些资源在云原生环境下的耗尽风险[1]。其中,网络带宽与计算机所处的网络环境有关,本书不做讨论。

1.CPU资源耗尽

毫无疑问,CPU资源大量消耗会对计算机的正常运行产生影响。在缺少限制的情况下,一个容器几乎能够使用宿主机上的所有算力。

下面,我们借助压力测试工具stress测试一下,同时使用htop工具来监测宿主机CPU使用情况。

首先,在宿主机上开启htop监控(图3-23下方),然后在宿主机上运行一个容器(图3-23上方左侧终端),接着再测量创建一个容器并执行uname -a命令所需的时间(图3-23上方右侧终端)。可见从执行到命令结束约为0.7s。

图3-23 正常情况下创建容器并执行命令的场景

现在,我们模拟大量占用CPU算力的场景,在第一次创建的容器中使用stress工具运行大量sqrt()计算任务(图3-24上方左侧终端),执行如下命令:


stress -c 1000

图3-24 大量消耗CPU资源时创建容器并执行命令的场景

htop(图3-24下方)显示此时宿主机上两个CPU核心使用率均为100%。这时候,我们再次测量创建一个容器并执行uname -a命令所需的时间(图3-24上方右侧终端),发现命令所需时间已经变成了约1.2s,与上次实验相比增长了约70%。

由此可见,在没有限制的情况下,恶意容器能够通过CPU算力耗尽的方式影响宿主机及其他容器的正常运行。

2.内存资源耗尽

内存耗尽的表现也非常明显:应用交互及时性会降低,服务响应时间会延长。在缺少限制的情况下,一个容器几乎能够占用宿主机上的所有内存。

下面,我们借助压力测试工具stress测试一下,同时使用htop工具来监测宿主机内存使用量。

首先,在宿主机上开启htop监控(图3-25下方),然后在宿主机上运行一个容器(图3-25上方左侧终端),接着再测量创建一个容器并执行uname -a命令所需的时间(图3-25上方右侧终端)。可见从执行到命令结束约为0.9s。

现在,我们模拟内存耗尽的场景,在第一次创建的容器中使用stress工具申请大量内存空间(图3-26上方左侧终端),执行如下命令:


stress --vm-bytes 3300m --vm-keep -m 3

htop(图3-26下方)显示此时宿主机上内存占用已经接近容量,约为7.48G。这时候,我们再次测量创建一个容器并执行uname -a命令所需的时间(图3-26上方右侧终端),发现命令所需时间已经变成了约12s,与上次实验相比增长了约12倍。

图3-25 正常情况下创建容器并执行命令的场景

图3-26 大量消耗内存资源时创建容器并执行命令的场景

由此可见,在没有限制的情况下,一个容器能够通过内存耗尽的方式影响宿主机及其他容器的正常运行。

3.进程表耗尽

事实上,除了硬件资源外,操作系统还会提供很多软件资源,进程表就是其中之一。我们以经典的进程表耗尽案例——Fork炸弹——来分析这类软件资源无限制可能导致的问题。操作系统中一切行为都是以进程方式执行的。为了管理这些进程,操作系统内核维护了一张进程表[2],表空间是有限的,一旦饱和,系统就无法再运行任何新程序,除非表中有进程终止。

Fork炸弹,顾名思义,就是借助fork系统调用不断创建新进程,使进程表饱和,最终系统无法正常运行。能够实现上述功能的代码非常多,其中最经典的还是Bash版本:


:() { :|:& };:

上述代码看起来古怪,其实很简单,就是以无限递归的形式不断创建新进程。尽管只有一行代码,在Bash中执行后,如果没有其他限制,操作系统就会慢慢失去响应。这里我们不再解释代码原理,感兴趣的读者可以参考相关资料[3]。我们关心的是,它能否在容器内部运行?

我们做一个小实验。图3-27展示了三个终端:上方终端中,我们在虚拟机内创建了一个容器,然后执行Fork炸弹代码,下方左侧终端事先列出了虚拟机IP地址,下方右侧终端则在Fork炸弹执行几分钟后,尝试使用SSH远程登录到虚拟机,可以发现,虚拟机已经失去响应了。

图3-27 运行Fork炸弹后尝试使用SSH连接目标主机

在虚拟机失去响应后,我们从图3-28中虚拟机的启动窗口中可以看到内核打印的日志。

图3-28 虚拟机日志

日志显示内核在尝试杀掉进程。这说明,在没有相关限制的情况下,容器内的Fork炸弹能够影响宿主机正常运行。

4.存储资源耗尽

除了运行时的资源外,相对静态的存储资源也可能被耗尽。所有虚拟化技术都必须依托实体,容器也不例外。归根结底,容器内存储的数据、文件的实际存储位置还是在实体机上。如果容器内新增了一个1GB的文件,不考虑NFS等情况,那么宿主机上的磁盘空间也应该相应减少1GB。如果容器没有存储空间限制,容器内的攻击者理论上能够耗尽宿主机的存储资源。

首先,我们在宿主机上使用df命令查看一下可用空间(图3-29上方终端),结果为9.4GB。

图3-29 查看宿主机的存储空间

然后,我们在容器中使用fallocate命令创建一个9.4GB的文件(图3-30下方终端)后,宿主机上的磁盘使用率为100%,可用空间为0。此时,如果宿主机需要增加一个大小为1GB的重要文件(图3-30上方终端),就会因为空间不足而失败。

图3-30 在容器中创建一个超大文件

[1] 本小节的随书代码仓库路径:https://github.com/brant-ruan/cloud-native-security-book/tree/main/code/0304-运行时攻击/03-资源耗尽型攻击。

[2] https://zh.wikipedia.org/zh-cn/ 行程控制表。

[3] https://en.wikipedia.org/wiki/Fork_bomb。