相关程序漏洞导致的容器逃逸

小结

谈及云原生
安全时,不少人还停留在传统安全意识和观念,对 Web 攻防、系统攻防加强防范,密 码暴力破解和反弹 Shell 不会掉以轻心。然而,安全总是具有“短板效应”。有时,一个简单的端口暴露、 未授权访问漏洞没有及时处理,就可能为攻击者供不费吹灰之力长驱直入的机会,固若金汤的城池也 会因此沦陷。此外,云原生自身脆弱性带来的风险,在未来数年内,很可能被攻击者所利用,进而发动针对云原 生系统的攻击,因而云原生面临的威胁和安全风险
不可不察。## 云原生面临的安全威胁和风险
云原生面临的安全威胁和风险

容器化基础设施

的威胁和风险不会更少以容器和容器编排系统为代表的云原生基础设施,成为支撑云原生应用的重要载体,其安全性将直 接影响整个云原生系统的安全性。

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

随着容器技术的普及,容器镜像也成为了软件供应链中非常重要的一部分。然而,业务依赖的基础 镜像无论是存在安全漏洞还是包含恶意代码,这种潜在危害比黑客从外部发起攻击严重得多。下面,我 们将介绍两种容器镜像软件供应链的攻击类型。
镜像漏洞利用
“镜像漏洞利用”指的是镜像本身存在漏洞时,依据镜像创建并运行的容器也通常会存在相同漏洞, 攻击者利用镜像中存在的漏洞去攻击容器,往往具有事半功倍的效果。
备受开发者青睐的 Alpine 镜像曾曝出过一个漏洞,编号为 CVE-2019-5021。3.3 到 3.9 版本的 Alpine 镜像中,root 用户密码被设置为空,攻击者可能在攻入容器后借此升到容器内部 root 权限。 这个漏洞看起来很简单,但是 CVSS 3.0 评分高达 9.8 分 [10] 。
镜像投毒
镜像投毒是一个宽泛的话题。它指的是攻击者通过某些方式⸺如上传镜像到公开仓库、入侵系统 后上传镜像到受害者本地仓库以及修改镜像名称假冒正常镜像等,欺骗、诱导受害者使用攻击者指定的 恶意镜像创建并运行容器,从而实现入侵或利用受害者的主机进行恶意活动的行为。
根据目的不同,常见的镜像投毒有三种类型:投放恶意挖矿镜像、投放恶意后门镜像和投放恶意 exploit 镜像。
投递恶意挖矿镜像 这种投毒行为主要是为了欺骗受害者在机器上部署容器,从而获得经济收益。2018 年 6 月,一份
研究报告指出 [11],一个名为 docker123321 的账号向 Docker Hub 上陆续上传了 17 个包含挖矿代码的 恶意镜像。截至 Docker Hub 官方移除这些镜像时,它们已经累计被下载超过 500 万次。据统计,黑客 借助这一行为获得了时值约 9 万美元的门罗币。
投放恶意后门镜像
这种投毒行为主要是为了实现对容器的控制。通常,受害者在机器上部署容器后,攻击者会收到容 器反弹过来的 Shell。
相比之下,这种类型的投毒可能会少一些⸺因为在隔离有效的情况下,即使攻击者拿到一个 容器内部的 Shell,攻击面仍然有限。在 2017 年 9 月,有用户在 Docker Hub 的反馈页面中反馈前述 docker123321 账号上传的 Tomcat 镜像包含了后门程序 [12]。结合该账号上传的其他恶意镜像的挖矿行 为来看,有理由推测攻击者在连接上述后门后可能会部署挖矿程序,获得经济收益。
投放恶意 exploit 镜像
这种投毒行为是为了在受害者部署容器后尝试寻找宿主机上的各种漏洞来实现容器逃逸等目的,以 实现对受害者机器更强的控制。
从攻防对抗的角度来看,恶意 exploit 镜像只不过是一种攻击载荷投递方式,其特点在于隐蔽性和 可能的巨大影响范围。试想,如果 Docker Hub 上某一热门镜像包含了某个 1day 甚至 0day 漏洞的利用 程序,理论上,攻击者将可能一下子获取上百万台计算机的控制权限。

容器逃逸

与其他虚拟化技术类似,逃逸是最为严重的安全风险,直接危害了底层宿主机和整个云计算系统的 安全。
根据层次的不同,容器逃逸的类型可以分为以下三类:
图 3.1 不同层次的容器逃逸问题
上图可以进一步展开为:危险配置导致的容器逃逸;危险挂载导致的容器逃逸;相关程序漏洞导致 的容器逃逸;内核漏洞导致的容器逃逸。
危险配置导致的容器逃逸
在这些年的迭代中,容器社区一直在努力将纵深防御、最小权限等理念和原则落地。然而,无论是 细粒度权限控制还是其他安全机制,用户都可以通过修改容器环境配置或在运行容器时指定参数来缩小 或扩大约束。如果用户为不完全受控的容器供了某些危险的配置参数,就为攻击者供了一定程度的 逃逸可能性。
最初,容器特权模式的出现是为了帮助开发者实现 Docker-in-Docker 特性 [13]。然而,在特权模式下 运行不完全受控容器将给宿主机带来极大安全威胁。例如,攻击者可以直接在容器内部挂载宿主机磁盘, 然后将根目录切换过去,从而实现容器逃逸。
危险挂载导致的容器逃逸
在某些特定场景下,为了实现特定功能或方便操作(例如为了在容器内对容器进行管理将 Docker Socket 挂载到容器内),用户会将外部敏感卷挂载入容器。随着容器技术应用的逐渐深化,挂载操作 变得愈加广泛,由此而来的安全问题也呈现上升趋势。
Docker Socket 是 Docker 守护进程监听的 Unix域套接字,用来与守护进程通信,查询信息或下发
命令。
如果在攻击者可控的容器内挂载了该套接字文件(/var/run/docker.sock),容器逃逸就相当容易了, 除非有进一步的权限限制。一种逃逸方法是:(1)在容器内安装 Docker 命令行客户端;(2)使用该 客户端通过 Docker Socket 与 Docker 守护进程通信,发送命令创建并运行一个新的容器,将宿主机的 根目录挂载到新创建的容器内部;(3)在新容器内执行 chroot 将根目录切换到挂载的宿主机根目录。
相关程序漏洞导致的容器逃逸
所谓相关程序漏洞,指的是那些参与到容器生态中的服务端、客户端程序自身存在的漏洞。
CVE-2019-5736 正是这样一个存在于 runC 的容器逃逸漏洞,它由波兰 CTF 战队 Dragon Sector 在 35C3 CTF 赛后基于赛中一道沙盒逃逸题目获得的启发,对 runC 进行漏洞挖掘,成功发现的一个能够 覆盖宿主机 runC 程序的容器逃逸漏洞。该漏洞于 2019 年 2 月 11 日通过邮件列表披露,具体分析可参 见“容器逃逸成真:从 CTF 解题到 CVE-2019-5736 漏洞挖掘分析”[14]。
内核漏洞导致的容器逃逸 Linux内核漏洞的危害之大、影响范围之广,使得它在各种攻防话题下都占据非常重要的一席。
近年来,Linux系统曝出过无数内核漏洞,其中能够用来权的也不少,脏牛( CVE-2016-5195) 大概是其中最有名气的漏洞之一。事实上,脏牛漏洞也能用来进行容器逃逸,一种利用方法的核心思路 是向 vDSO内写入 shellcode 并劫持正常函数的调用过程。在成功触发漏洞后,攻击者可以获得宿主机 上反弹过来的 Shell,实现容器逃逸。
其他
作为一种轻量级虚拟化技术,传统容器与宿主机共享内核,这意味着系统内核权限升漏洞往往也 可用来实施容器逃逸。为了彻底解决这一问题,在轻量与安全性之间达到较好的平衡,安全容器应运而 生。Kata Containers 是一种安全容器的具体实现,其他主流的安全容器还有 Google 推出的 gVisor 等。
无论是在理论上,还是在实践中,安全容器都具有非常高的安全性。然而,在 2020 年 Black Hat!
北美会议上,Yuval Avrahami 分享了利用多个漏洞成功从 Kata containers 逃逸的议题 [15],安全容器首 次被发现存在逃逸可能性。他使用发现的三个漏洞(CVE-2020-2023、CVE-2020-2025 和 CVE-2020- 2026)组成漏洞利用链先后进行容器逃逸和虚拟机逃逸,成功从容器内部逃逸到宿主机上。

资源耗尽型攻击

同为虚拟化技术,容器与虚拟机既存在相似之处,也有显著不同。在资源限制方面,我们需要为即 将创建的虚拟机设定明确的 CPU、内存及硬盘资源阈值,在虚拟机内部的进程看来,它真的处于一台 被设定好的独立计算机之中。
然而,容器运行时默认情况下并未对容器内进程在资源使用上做任何限制,以 Pod 为基本单位的容 器编排管理系统也是类似的,Kubernetes 在默认情况下同样未对用户创建的 Pod 做任何 CPU、内存使 用限制 [16]。
限制的缺失使得云原生环境面临资源耗尽型攻击风险。攻击者可能通过在一个容器内运行恶意程序, 或针对一个容器服务发起拒绝服务攻击来占用大量宿主机资源,从而影响到宿主机或宿主机上其他容器 的正常运行。

微服务架构为云原生应用安全带来新的安全风险

在如今应用的开发、测试、运维均追求敏捷开发的时代,微服务应运而生。但随着应用的微服务化 升级,新的安全风险也不容忽视。
首先,随着微服务的增多,暴露的端口数量也急剧增长,进而扩大了攻击面,且微服务间的网络流 量多为东西向流量,网络安全防护维度发生了改变。
其次,不同于单体应用只需解决用户或外部服务与应用的认证授权,微服务间的相互认证授权机制 更为复杂,人为因素导致认证授权配置错误成为了一大未知风险。
最后,微服务通信依赖于 API,随着业务规模的增大,微服务 API数量激增,恶意的 API操作可能 会引发数据泄漏、越权访问、中间人攻击、注入攻击、拒绝服务等风险。

参考资料

绿盟 云原生安全技术报告

友情链接

GB-T 36630.2-2018 信息安全技术 信息技术产品安全可控评价指标 第2部分:中央处理器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的基于libbpf的容器逃逸监测程序示例: ```C #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <linux/bpf.h> #include <bpf/libbpf.h> #define BPF_PROGRAM "\ int kprobe__do_exit(struct pt_regs *ctx) {\n\ bpf_trace_printk(\"Container escape detected!\\n\");\n\ return 0;\n\ }\n" int main() { struct bpf_insn insn_buf[256]; // 将BPF程序编译成BPF字节码 int prog_len = bpf_asm(BPF_PROGRAM, insn_buf, sizeof(insn_buf)); if (prog_len < 0) { fprintf(stderr, "Failed to compile BPF program: %s\n", strerror(-prog_len)); return EXIT_FAILURE; } // 加载BPF程序 int prog_fd = bpf_load_program(BPF_PROG_TYPE_KPROBE, insn_buf, prog_len, "GPL", 0, NULL, 0); if (prog_fd < 0) { fprintf(stderr, "Failed to load BPF program: %s\n", strerror(errno)); return EXIT_FAILURE; } // 安装BPF程序到目标kprobe int ret = bpf_attach_kprobe(prog_fd, BPF_PROBE_ENTRY, "do_exit"); if (ret < 0) { fprintf(stderr, "Failed to attach BPF program to kprobe: %s\n", strerror(-ret)); return EXIT_FAILURE; } // 等待BPF程序输出 char buf[1024]; ssize_t len = read(ret, buf, sizeof(buf)); if (len < 0) { fprintf(stderr, "Failed to read BPF output: %s\n", strerror(errno)); return EXIT_FAILURE; } // 关闭BPF句柄 close(prog_fd); return EXIT_SUCCESS; } ``` 该程序的作用是在容器逃逸时打印一条警告信息。具体实现方式是使用BPF程序来监测系统中的`do_exit`函数,并在函数调用时输出一条警告信息。BPF程序被编译成BPF字节码后,使用libbpf库提供的函数将其加载到内核中,并使用`bpf_attach_kprobe`函数将其安装到`do_exit`函数上。最后,程序等待BPF程序输出并关闭BPF句柄。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值