叫你打靶機平台不是叫你把整個平台打下來,記錄針對 vulfocus 的一次滲透
最近接觸到一個對岸的開源靶機平台 vulfocus , vulfocus
整合了 docker
、 docker compose
,讓架設者能夠一鍵部署漏洞題目,並且在 container 中自帶 Flag 。
事情的開端是我在這個專案的 issue 中看到了一個 JWT token
leak,並且可以拿到 admin
的權限,欸既然拿到 admin
權限了又是靶機平台,甚至可以自訂 docker-compose.yml
,應該可以馬上 RCE 吧?我一開始是這樣想的,但結果比我想像的還要複雜很多。
有 docker compose 為什麼還是沒辦法 RCE 呢
導入靶機介面提供了三種導入方式
- 添加
- 本地導入
- Compose 編譯
添加功能可以讓你上傳或是提供網址 pull image 進來,本地導入讓管理者可以直接透過本機裡面已經有的 image 一鍵建立漏洞題目,而 Compose 編譯則能自己撰寫靶機的 docker-compose.yml
既然靶機是 docker
管理的,不免讓人馬上聯想到 docker escape 。我都有 docker-compose.yml
了直接寫一個 privileged: true
不就完事了。
恭喜, docker compose
的功能根本就是壞的,因為他把 yaml parse 之後直接塞進去 json.load
,但是 json.loads
只能塞字串
Vulfocus API 的架構
在亂翻功能時,我發現「本地導入」頁面有一個 vulfocus-api
的 container
正常來講這種靶機平台應該是包兩層 docker 避免 challenge 影響到其他的 Container,不過看了一下 source code 發現全部的 challenge container 都是透過 host 的 docker api 直接新增,也就是說這些 challenge 被散落在宿主機上,僅透過 network 隔離。
既然 vulfocus-api
是一個 container 並且可以操作 host 的 docker,那我們有沒有辦法透過「本地導入」把這個 container 加進來,這樣我不就有宿主機的 docker.sock
了嗎?
恭喜,又不能,因為「本地導入」只會複製 container 的 image 和 docker-comopse.yml
的 expose port,其他欄位都會被丟掉,包括被掛進來的 docker.sock
YAML 反序列化漏洞
api 中有許多地方用到了 yaml
來儲存和載入資料,而有些地方用了不安全的 yaml.Loader,雖然如此但這些 yaml.Loader
的內容都是我們(理論上)無法直接控制的,有的是透過 yaml.dump 後 load 進來,有的是透過官網下載官方編排場景 load 進來
1 | raw_data = yaml.dump(req) |
1 | url = "http://vulfocus.io/api/layoutinfodet?layout_id={}".format(id) |
那為什麼是「理論上」呢?因為我只要把他們的官網打下來就變成可控的了
當然不是,作為好駭客(的朋友)我們不能在未經授權的情況下攻擊別人的網站,因此我們將進到本文的重點:如何在不打下官網的狀況下控制 raw_data
題外話:目前 vulfocus.io 這個 domain 已經被越南的壞駭客打下來了
MitM + YAML Deserialization + Docker Escape
Vulfocus 的場景功能
在開始之前,我們需要介紹另一個 Vulfocus framework 的 feature —— 場景功能
簡而言之,場景功能可以將多個 container 和網路環境整合成一個題目,前面提到 challenge 由不同網卡將彼此做隔離,而 layout 功能則是將多個 container 放在同一個網路環境,讓彼此可以互通,因此出題者可以構建比如 SSRF、題目再加一層 WAF 之類的場景。
亂搞網卡
既然可以亂搞網路環境,那我們有沒有可能將某個 container 掛到和 vulfocus-api
的相同物理網路之中呢?
在新增網卡的頁面中,雖然 vulfocus 本身沒有對新增的網卡做任何限制,但是這些資料到了 docker api 就會爆炸,因為沒辦法新增相同名稱或子網重疊的 network
在一番嘗試後,新增場景的網卡邏輯引起了我的注意:
1 | #run_layout |
也就是說,程式會先看 docker network 裡面有沒有該場景的網卡名稱,如果有就會用它把 container 跑起來,沒有的話才會去 NetWorkInfo 中找儲存的網卡,所以,我們可以在新增場景時,直接設定不存在於 vulfocus 但存在於 docker 中的 network,也就是 vulfocus-api
使用的網路環境 vulfocus_vulfocus
至於為什麼是 vulfocus_vulfocus
呢?因為官方提供的 GitHub repo 中,docker-compose.yml
便是 vulfocus
,而 docker network 命名的邏輯為 folderName_networkName
因此如果使用官方的 repo ,網卡名稱便會是 vulfocus_vulfocus
MitM
到目前為止,我們已經成功連接到 vulfocus-api
的物理網路,因此,我們只需要對他做 MitM ,http://vulfocus.io
回傳的結果便是我們可控的了
將 vulfocus-api 與 gateway 做中間人攻擊後,我們便可以偷偷換掉 vulfocus.io 的 dns 結果
觸發更新的 api,此時程式按照預定邏輯到 vulfocus.io 下載更新,但因為 dns 查詢結果已經被我們篡改,因此會下載到錯誤的 yaml
1 | { |
至此,我們成功 compromise 了 vulfocus-api
Docker Escape
拿到 vulfocus-api
的 shell 後,要做 docker escape 就很簡單了,因為 vulfocus-api
已經將宿主的 docker.sock 給 mount 進來了,我們只要用他創一個 privileged container 就可以了:
1 | import docker |
成功拿到宿主機的 shell
後記
這個專案是在 DEVCORE 實習時接觸到的,整個過程其實蠻 tricky 的,也是第一次打 web 打到需要用到 MitM Attack,算是一個特別的經驗,整個過程大概花了兩個禮拜(主要是因為大部分的程式碼都很 spaghetti 的),我覺得最大的收穫就是翻 code 的能力變強了 ( ̄▽ ̄)
本次經驗也同步在期末的實習生發表同步分享,感謝導師 Mico 讓我花兩個禮拜在平台上面亂搞一通。