云原生安全-容器安全

1. 什么是云原生安全

云计算 -> 云原生 -> 云原生安全

了解云原生安全前首先了解一下云计算和云原生

云计算：

在云计算出现之前，软件的用户量和数据量较少，可以直接放置到公司的机房中，但是在现在的大数据环境下，传统的软件架构已经不适合了，软件重构和数据迁移又很麻烦，所以就产生了云计算。

云计算允许用户根据需求来获取资源

云原生：

云原生没有确切的定义，云原生一直在发展变化之中，解释权不归某个人或组织所有

云原生（CloudNative）是一种构建和运行应用程序的方法，是一套技术体系和方法论

符合云原生架构的应用程序应该是：采用开源堆栈（K8S+Docker）进行容器化，基于微服务架构提高灵活性和可维护性，借助敏捷方法、DevOps 支持持续迭代和运维自动化，利用云平台设施实现弹性伸缩，动态调度，优化资源利用率

云原生四要素：

微服务

容器化

DevOps

持续交付

简单理解云原生：软件应用服务在上云的过程中为了适应云的一种技术架构

云原生安全：

对于云原生，依旧存在传统的安全问题，如：DDOS 攻击，web 入侵等

除此之外，云原生还处在以下问题：

API 安全

容器安全

缺乏集中管理

排查困难

2. 什么是容器安全

在云原生生态中有很多不同的容器，下面以 Docker 为例进行分析

2.1 Docker 自身安全漏洞

在 CVE 官方记录中 Docker 的历史版本中已存在 20 + 漏洞，包括代码执行，权限提升，信息泄露等

Docker 源码安全

Docker hub 安全

2.2 Docker 架构缺陷

容器间的局域网攻击

DDOS 攻击耗尽资源

有漏洞的系统调用

共享 root 用户权限

3. 容器安全

3.1 针对容器化开发测试过程中的攻击

背景

docker cp 命令

docker cp 命令用于在 Docker 创建的容器与宿主机文件系统间进行文件或目录复制

符号链接

符号链接（软链接），类似于 Windows 上的快捷键

CVE-2018-15664 - 符号链接替换漏洞

影响版本：Docker 17.06.0-ce～17.12.1-ce:rc2，18.01.0-ce～18.06.1-ce:rc2

漏洞原理：CVE-2018-15664 实际上是一个 TOCTOU (time-of-check to time-of-use) 的问题，属于竞态条件漏洞。

简单来说，程序在对某对象进行安全检查（如：用户执行 docker cp 命令后，Docker 守护进程会对给出的复制路径进行检查）和使用该对象的步骤之间存在间隙，攻击者可以先构造一个能够通过安全检查的对象，然后立即使用恶意对象替换之前的合法对象，这样，目标程序真正使用的是替换后的恶意对象（在用 docker cp 命令的时候，替换符号链接，可导致目录穿越）

漏洞复现：利用 metarget 快速搭建 CVE-2018-15664 环境

CVE-2019-14271 - 加载不受信任的动态链接库

影响版本：Docker 19.03.x before 19.03.1

漏洞原理：docker cp 命令依赖 docker-tar 组件会加载容器内部的 nsswitch 动态链接库，攻击者可以通过劫持容器内部的 nsswitch 来实现代码注入，获得宿主机上的 root 权限的代码执行能力

漏洞利用过程如下：

找出 docker-tar 具体会加载那些容器内的动态链接库

下载对应的动态链接库源码，增加 __attribute__属性的函数 run_at_link (该函数在动态链接库被加载时首先执行)

等待 docker cp 触发漏洞

3.2 针对容器软件供应链的攻击

随着容器技术的普及，容器镜像成为了软件供应链中非常重要的一部分，我们可以从 公共仓库中获取镜像，也可以从 私有仓库中获取镜像。

从公共仓库中获取镜像存在两个脆弱性问题：

镜像中软件的安全漏洞问题

镜像内的挖矿程序、后门程序、病毒等恶意程序

镜像漏洞利用

镜像漏洞利用指的是：镜像本地存在漏洞时，使用镜像创建并运行的容器通常也会存在相同的漏洞

例如：Alpine 是一个轻量化的 Linux 发行版，基于 musl libc 和 busybox 构建而成。由 于其体积较小，因此以 Alpine 为基础镜像构建软件是非常流行的。但 Alpine 镜像曾曝出一个漏洞：CVE-2019-5021。在 3.3 ~ 3.9 版本的 Alpine 镜像中，root 用户密码被设置为空，攻击者可能在攻入容器后借此提升到容器内部 root 权限

镜像投毒

镜像投毒是一个宽泛的话题，指的是：攻击者通过某些方式，如上传恶意镜像到公开仓库、入侵系统后上传镜像到受害者本地仓库，以及修改镜像名称并假冒正常镜像等，欺骗、诱导受害者使用攻击者指定的恶意镜像创建并运行容器，从而实现入侵或利用受害者的主机进行恶意活动的行为

根据目的不同，常见的镜像投毒有三种类型：

投放恶意挖矿镜像

投放恶意后门镜像

投放恶意 exploit 镜像

3.3 针对容器运行时的攻击

不安全配置导致的容器逃逸

这些年的迭代，容器社区一直在努力将纵深防御、最小权限等理念和原则落地。

Docker 已经将容器运行时的 Capabilities 黑名单机制改为如今的默认禁止所有 Capabilities，再以白名单方式赋予容器运行所需的最小权限。Docker 默认赋予容器近 40 项权限中的 14 项：

Copy
func DefaultCapabilities() []string {
return []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
}
}

无论是细粒度权限控制还是其他安全机制，用户都可以通过修改容器环境配置或在运行容器时指定参数来缩小或扩大约束。如果用户为不完全受控的容器提供了某些危险的配置参数，就为攻击者提供了一定程度的逃逸可能性

危险挂载导致的容器逃逸

为了方便宿主机与虚拟机进行数据交换，虚拟化解决方案都会提供挂载宿主机目录到虚拟机的功能。容器同样如此。然而，将宿主机上的敏感文件或目录挂载到容器内部 - 当受控容器存在不安全的挂载时，会造成严重的问题。

3.4 程序漏洞导致的容器逃逸

CVE-2019-5736

影响版本：Docker version <= 18.09.2 & RunC version <= 1.0-rc6

漏洞原理：CVE-2019-5736 是一个能够覆盖宿主机 runc 程序的容器逃逸漏洞。在执行功能类似于 docker exec 等命令时，底层实际上是容器运行时在操作。例如 runC，相应地，runc exec 命令会被执行。它的最终效果是在容器内部执行用户指定的程序。进一步讲，就是在容器的各种命名空间内，受到各种限制（如 Cgroups）的情况下，启动一个进程。除此以外，这个操作与在宿主机上执行一个程序并无二致。

攻击步骤：

将容器内的 /bin/sh 程序覆盖为 #!/proc/self/exe。

持续遍历容器内 /proc 目录，读取每一个/proc/[PID]/cmdline，对 runc 做字符串 匹配，直到找到 runc 进程号。

以只读方式打开 /proc/[runc-PID]/exe，拿到文件描述符 fd。

持续尝试以写方式打开第 3 步中获得的只读 fd (/proc/self/fd/[fd])，一开始总是返回失败，直到 runc 结束占用后写方式打开成功，立即通过该 fd 向宿主机上的 /usr/bin/runc，(名字也可能是 /usr/bin/docker/runc）写入攻击载荷

runc 最后将执行用户通过 docker exec 指定的 /bin/sh，它的内容在第 1 步中已经被替换成 #/proc/self/exe，因此实际上将执行宿主机上的 runc，而 runc 也已经在第 4 步中被覆盖掉了

3.5 内核漏洞导致的容器逃逸

从操作系统层面来看，容器进程只是一种受到各种安全机制约束的进程，因此从攻防两端来看，容器逃逸都遵循传统的权限提升流程。攻击者可以凭借此特点拓展容器逃逸的思路，一旦有新的内核漏洞产生，就可以考虑它是否能够用于容器逃逸；而防守者则能够针对此特征进行防护和检测，如宿主机内核打补丁，或检查该内核漏洞利用有什么特点。

CVE-2016-5195

影响版本：Linux kernel >= 2.6.22（2007 年发行，到 2016 年 10 月 18 日才修复）

漏洞原理：Linux 内核的内存子系统在处理写时拷贝（Copy-on-Write) 时存在条件竞争漏洞，导致可以破坏私有只读内存映射

使用 PoC 来完成容器逃逸。该利用的核心思路是向 vDSO 内写入 shellcode 并劫持正常函数的调用过程

4. 参考

《云原生安全 —— 攻防实践与体系构建》