Docker逃逸的一些基础手法...

【Docker】Docker逃逸小结

前言

Docker自2013年发行开始,一直受到业界广泛的关注。

这篇文章将探讨Docker安全性相关的问题。

什么是容器?

容器的本质就是把系统中同一个业务目标服务的相关进程组合在一起,放在一个namespace中。

同一个namespace中的进程能够互相通信,同时看不见其他namespace中的进程。

每个namespace拥有自己独立的主机名、进程ID、IPC、网络、文件系统、用户等资源

此外,为了限制namespace对物理资源的使用,要对进程能够使用的CPU、内存资源进行一定的限制,而使用Cgroup技术就可以解决。

我们常说的4c4g容器其实就是限制这个容器最多能使用4核计算资源和4G的内存空间

简而言之,Linux内核提供namespace完成隔离,Cgroup完成资源限制。namespace+Cgroup构成了容器的底层技术

容器的实现没有使用任何虚拟化的技术,而是一种隔离技术。可以简单的把docker理解成一种高级的chroot

Docker的安全机制

上文提到了,Docker使用的是隔离技术,所以我们在容器内部使用的仍然是宿主机的内核、cpu与内存。这就可能导致一些安全问题的出现。

在讲述Docker可能存在的安全问题之前,先来探讨一下Docker是如何在最大程度上保证容器的安全性。

Docker使用到的安全机制有很多:

  • Linux Capability
  • AppArmor
  • SELinux
  • Seccomp

这里就简单的提一提Linux Capability

因为Docker默认会对User namespace不进行隔离,所以在docker内部cat /etc/passwd的时候会查看到uid为0,也就是说Docker容器内部的root其实就是宿主机的root。

这时候就会有疑问了,既然是宿主机的root,应该是无所不能的,为什么很多服务都无法使用?并且有些命令会提示Permission denied

这个其实就是Linux Capability实现的

自Linux 2.1开始,引入了capability的概念,它打破了操作系统中超级用户和普通用户的概念,普通用户通过设置capability也可以做到超级用户才能干的事情

Linux Capability一共有38钟,docker默认开启了14种,这样就有效的避免了很多安全问题

如果开启docker使用了--privileged=true,那么其实就是开启了所有的capability

Docker逃逸

docker逃逸的类型无非下面三种:

  1. 配置不当
  2. Docker软件的漏洞
  3. 内核漏洞

1.特权模式

我们在上面讲到,特权模式将capability全开了,那么就存在挂载权限。

可以通过mount的方式将宿主机的根目录挂载到容器中,这样就可以通过容器对宿主机的文件进行读写。

这里用一个ubuntu20.04的容器来进行说明

  1. 以特权模式开启ubuntu20.04容器,执行/bin/bash
  2. 执行fdisk -l 查看挂载情况
  3. cd进入/dev 查看挂载情况
  4. 在/dev创建一个文件夹 mkdir test114
  5. 将/dev/sdc挂载到刚刚创建的test114上
  6. 可以查看宿主机的文件,同时可以修改
  7. 创建一个test114文件
  8. 回到宿主机查看,发现确实有这个新增的文件,并且文件所有者就是root

image-20230328140318869

image-20230328140337282

image-20230328141055405

image-20230328141155711

image-20230328141256991

2.--cap-add=SYS_ADMIN

使用--cap-add=SYS_ADMIN的参数,可以让docker容器内部使用mount和unmount,可以实现上述一样的效果

3.挂载了Docker.sock

Docker使用的架构是C/S架构,我们使用的docker指令,docker就是client,而server端是由docker daemon完成的,两者之间的通信方式有如下三种:

  1. unix:///var/run/docker.sock
  2. tcp://host:port
  3. fd://socketfd

docker默认使用docker.sock进行通新,当容器中进程与Docker守护进程进行通信的时候,容器本身需要挂载unix://var/run/docker.sock文件

本质来说,如果能够访问docker socket或者连接https的api,那么就可以执行docker服务能够执行的任何命令。

所以如果容器中有能够访问docker服务端的能力,就可以完成docker逃逸,具体操作就是:

  • container内部安装docker.io(客户端),通过容器内部存在的docker.sock或者api与宿主机的server进行交互
  • 让宿主机server以特权模式开一个容器,进入该容器,执行任意命令(或者直接使用-v参数进行挂载根目录的操作)

这里就拿一道最近的比塞题来讲解吧,2022年的Real World体验赛的Be-a-Docker-Escaper

题目给出的Dockerfile中存在如下一句话:

docker run -i -m 128m -v /var/run/docker.sock:/s ubuntu # You are here!

非常明显将/var/run/docker.sock挂载到了容器的/s

执行如下操作即可访问宿主机中的flag:

apt update
apt install docker.io
mkdir /test114
docker -H unix:///s/docker.sock run -v /:/test114 -i ubuntu /bin/bash

cd /test114
cat flag

4.脏牛漏洞(内核漏洞)

Dirty Cow(CVE-2016-5195)是Linux内核中的权限提升漏洞,源于Linux内核的内存子系统在处理写入时拷贝(copy-on-write, Cow)存在竞争条件(race condition),允许恶意用户提权获取其他只读内存映射的写访问权限。

已经有人将环境打包成了一个docker镜像,可以直接复现

gebl/dirtycow-docker-vdso (github.com)

5.runc (cve-2019-5736)

poc地址 Frichetten/CVE-2019-5736-PoC: PoC for CVE-2019-5736 (github.com)

6. cp (cve-2019-1427)

CVE-2019-14271:Docker cp命令漏洞分析-安全客 - 安全资讯平台 (anquanke.com)