CVE-2019-5736 runc容器逃逸漏洞复现与解析
发布时间:
2020-12-11 10:16
漏洞简介
CVE-2019-5736是2019年2月11日在oss-security邮件列表披露的runc容器逃逸漏洞。在Docker 18.09.2之前的版本中使用了的runc版本小于1.0-rc6,因此允许攻击者重写宿主机上的runc 二进制文件,因此可以root的身份执行命令,导致获得宿主机的root权限。
影响范围
docker version <=18.09.2
RunC version <=1.0-rc6
修复方案
runC github 修复链接:
https://github.com/opencontainers/runc/commit/0a8e4117e7f715d5fbeef398405813ce8e88558b
Docker 新版本18.09.2 中已修复了该漏洞,建议升级到新版本。
https://github.com/docker/docker-ce/releases/tag/v18.09.2
Red Hat 平台修复方案详见以下链接:
https://access.redhat.com/zh_CN/security/vulnerabilities/3907161
AWS 平台修复方案详见以下链接:
https://aws.amazon.com/cn/security/security-bulletins/AWS-2019-002/
Kubernetes修复方案详见以下链接:
https://kubernetes.io/blog/2019/02/11/runc-and-cve-2019-5736/
阿里云修复方案详见以下链接:
https://help.aliyun.com/document_detail/107320.html
背景
runC
在执行类似docker exec的命令时,底层实际上是容器运行时在操作。例如runc,相应地,runc exec命令会被执行。它的最终效果是在容器内部执行用户指定的程序。进一步讲,就是在容器的各种命名空间内,受到各种限制(如cgroups)的情况下,启动一个进程。除此以外,这个操作与宿主机上执行一个程序没有区别。
执行过程大体是这样的:runc启动,加入到容器的命名空间,接着以自身为范本启动一个子进程,最后通过exec系统调用执行用户指定的二进制程序。
PROCFS
/proc 是一个伪文件系统,这个伪文件系统让你可以和内核内部数据结构进行交互,与真正的文件系统不同的是它是存在于内存中而不是真正的硬盘上,linux 下有一个说法一切皆文件,所有在linux上运行的程序都在/proc下有一个自己的目录,目录名字为程序的Pid号,目录里面存储着许多关于进程的信息,列如进程状态status,进程启动时的相关命令cmdline,进程的内存映像maps,进程包含的所有相关的文件描述符fd文件夹等等。
/proc/[PID]/fd/:这个目录下包含了进程打开的所有文件描述符。
/proc/[PID]/exe:它是一种特殊的符号链接,指向进程自身对应的本地程序文件(例如我们执行bash,/proc/[bash-PID]/exe就指向/bin/bash)
原理
设想这样一种情况:在runc exec加入到容器的命名空间之后,容器内进程已经能够通过内部/proc观察到它,此时如果打开/proc/[runc-PID]/exe并写入一些内容,就能够实现将宿主机上的runc二进制程序覆盖。下一次用户调用runc去执行命令时,实际执行的将是攻击者放置的指令。
复现
环境
主机
容器
EXP
https://github.com/Frichetten/CVE-2019-5736-PoC
略过 下载、修改payload、编译过程
payload:反弹shell 目标 192.168.0.1 65534
1.接收机器监听65534
2.容器内运行exp
3.模拟主机在此容器中执行命令
4.此时接收机器收到反弹的shell
分析
利用后果
runC 二进制文件被修改
在恢复之前 每次调用runc 均会执行Payload
另一种获取runC 文件描述符 的方式
execve()
execve()是一个内核系统调用函数,execve() 和fork(),clone()不同,它不需要启动新的进程,它直接替换当前执行的文件为新的文件,为新的可执行文件分配新初始化的堆栈和数据段。替换可执行文件,意味着释放调用execve()文件的IO,但这个过程默认是不释放/proc/pid/fd中的打开的文件描述符。
利用
在容器中监控(如 libseccomp )新启动的进程 获取/proc/self/exe 并设法(如 __attribute__ ((constructor)) )使其调用execve()传递/proc/self/fd中打开的文件描述符