@云原生之容器安全实践( 二 )


@云原生之容器安全实践
本文插图
图2 容器攻击面(Container Attack Surface)
容器一共有7个攻击面:Linux Kernel、Namespace/Cgroups/Aufs、Seccomp-bpf、Libs、Language VM、User Code、Container(Docker) engine 。
笔者以容器逃逸为风险模型 , 提炼出3个攻击面:

  1. Linux内核漏洞;
  2. 容器自身;
  3. 不安全部署(配置) 。
1. Linux内核漏洞
容器的内核与宿主内核共享 , 使用Namespace与Cgroups这两项技术 , 使容器内的资源与宿主机隔离 , 所以Linux内核产生的漏洞能导致容器逃逸 。
内核提权VS容器逃逸
通用Linux内核提权方法论
  • 信息收集:收集一切对写exploit有帮助的信息 。 如:内核版本 , 需要确定攻击的内核是什么版本?这个内核版本开启了哪些加固配置?还需知道在写shellcode的时候会调用哪些内核函数?这时候就需要查询内核符号表 , 得到函数地址 。 还可从内核中得到一些对编写利用有帮助的地址信息、结构信息等等 。
  • 触发阶段:触发相关漏洞 , 控制RIP , 劫持内核代码路径 , 简而言之 , 获取在内核中任意执行代码的能力 。
  • 布置shellcode:在编写内核exploit代码的时候 , 需要找到一块内存来存放我们的shellcode。 这块内存至少得满足两个条件:
    • 第一:在触发漏洞时 , 我们要劫持代码路径 , 必须保证代码路径可以到达存放shellcode的内存 。
    • 第二:这块内存是可以被执行的 , 换句话说 , 存放shellcode的这块内存具有可执行权限 。
  • 执行阶段
    • 第一:获取高于当前用户的权限 , 一般我们都是直接获取root权限 , 毕竟它是Linux中的最高权限 , 也就是执行我们的shellcode 。
    • 第二:保证内核稳定 , 不能因为我们需要提权而破坏原来内核的代码路径、内核结构、内核数据等等 , 而导致内核崩溃 。 这样的话 , 即使得到root权限也没有太大的意义 。
简而言之 , 收集对编写exploit有帮助的信息 , 然后触发漏洞去执行特权代码 , 达到提权的效果 。
@云原生之容器安全实践
本文插图
图3 容器逃逸简易模型(Container Escape Model)
容器逃逸和内核提权只有细微的差别 , 需要突破namespace的限制 。 将高权限的namespace赋到exploit进程的task_struct中 。 这部分的详细技术细节不在本文讨论范围内 , 笔者未来会抽空再写一篇关于容器逃逸的技术文章 , 详细介绍该相关技术的细节 。
经典的Dirty CoW
笔者以Dirty CoW漏洞来说明Linux漏洞导致的容器逃逸 。 漏洞虽老 , 奈何太过经典 。 写到这 , 笔者不禁想问:多年过去 , 目前国内外各大厂 , Dirty Cow漏洞的存量机器修复率是多少?
在Linux内核的内存子系统处理私有只读内存映射的写时复制(Copy-on-Write , CoW)机制的方式中发现了一个竞争冲突 。 一个没有特权的本地用户 , 可能会利用此漏洞获得对其他情况下只读内存映射的写访问权限 , 从而增加他们在系统上的特权 , 这就是知名的Dirty CoW漏洞 。
Dirty CoW漏洞的逃逸的实现思路和上述的思路不太一样 , 采取Overwrite vDSO技术 。
vDSO(Virtual Dynamic Shared Object)是内核为了减少内核与用户空间频繁切换 , 提高系统调用效率而设计的机制 。 它同时映射在内核空间以及每一个进程的虚拟内存中 , 包括那些以root权限运行的进程 。 通过调用那些不需要上下文切换(context switching)的系统调用可以加快这一步骤(定位vDSO) 。 vDSO在用户空间(userspace)映射为R/X , 而在内核空间(kernelspace)则为R/W 。 这允许我们在内核空间修改它 , 接着在用户空间执行 。 又因为容器与宿主机内核共享 , 所以可以直接使用这项技术逃逸容器 。


推荐阅读