1. 疑点 1

在描述当中说到,需要将暴露的cgroup handle作为当前目录启动容器,而漏洞描述当中只提到了设置 /proc/self/fd/7/,那么请问编号7的fd是如何获得的?

image-20240223145439564

解决: 利用lsof 进行查询,找到cgroup对应的句柄序号

image-20240223165524925

这样,找到了序号为6的fd

2. 第一次复现

在找到fd序号之后,将/proc/self/fd/6/设置成工作目录,启动容器

image-20240223165848202

别说是逃逸了,容器起都起不来。

3. 疑点 2

就在第一次复现失败以后,我觉得是fd开启顺序的问题,对fd开启了遍历,同样也没有成功创建容器,就在这时,超桑的系统上,他复现成功了,我们第一时间比对了Container引擎的版本与kernel版本

image-20240223170510106

经过比对,docker版本都是20.10往上,runc版本都在1.1.10以下,那么问题就在kernel版本上了

image-20240223170636177

由于我比较复古,喜欢装Centos7的机器,导致我的测试环境都是3.10的内核,而超酱的是ubuntu,采用的是5.10的内核,不同内核之间会有不同的效果,那么这个问题就抛向了内核所支持的syscall上面了,是否5.10多了或者少了某些syscall导致runc运行不安全起来了呢?

4. 对比源码

那天是一个周末,我约了内裤师傅出去玩,内裤师傅跟我讲,看看源码,源码里面有个syscall,这跟我之前的猜想一样,进而我来github上对比了一下源码

image-20240223171132522

在源码的libcontainer/cgroups/file.go 第89行,新版本修复后加入了一个关闭句柄的操作,相对应的是一个名叫Openat2的syscall,那么就寻找一下openat2的一些特性

在3.10的kernel当中没有找到openat2的描述

image-20240223171458087

而在5.10 kernel当中成功寻找到了文档

image-20240223171546963

经过查询官方文档,发现在5.6才开始有openat2的头文件

image-20240223172134294

这就是为什么3.10 kernel无法复现,而5.10 kernel可以创建的原因

5. 疑点 3

若低版本kernel缺少 openat2 syscall,那么低版本就无法使用runc了啊,低版本是怎么创建容器的呢?

simage-20240223172542157

查看源码发现,openat2在使用后会进行检查,如果抛了什么异常,那么就会使用字符串拼接一个地址,再进行开启,因此,若系统无openat2 syscall,直接使用ProcThreadSelf方法,由于这个方法是用fd/+xxx进行拼接,所以使用ProcThreadSelf的时候,无任何安全问题。