iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0

從 Day 12 到 Day 17,我們把 Docker 容器的安全邊界基本介紹過了一遍:搞懂容器到底隔離了什麼,看它是怎麼獲得 host root。今天回頭把這六天整理成一張地圖,看看這些攻擊為什麼成立,以及可以從中學到哪些防禦觀念。

六天回顧:我們拆的是哪條邊界?

Day 主題 跨過/理解的邊界 成因關鍵
Day 12 Docker 隔離了什麼 建立基準:容器=共享 kernel 的程序隔離,邊界由 namespace+capabilities+cgroups 構成 先看清楚「牆是用什麼砌的」
Day 13 拿到 shell 後找什麼 — 落地後先枚舉:我在哪、是誰、碰得到什麼
Day 14 docker.sock Docker daemon 的控制面 socket 可存取+daemon 以 root 執行
Day 15 Bind Mount mount namespace(檔案系統邊界) 掛載範圍過大+可寫+無 user namespace
Day 16 privileged capabilities+device+seccomp 一次全開 一個參數掀掉整疊防線
Day 17 單一 capability 單一能力+PID namespace(--pid=host) 每個 cap 都是獨立攻擊面

可以看出,容器逃逸不是只有一種破法:有的是打控制面,有的是打檔案系統邊界,有的是把 root 整碗端回來,有的則是只多拿一個能力。但它們底下其實共用了幾個成因。

把成因收斂成三件事

1. 容器的 root,常常就是 host 的 root

這是 Day 14、15、16 都成立的隱藏前提。因為多數容器沒有開 user namespace,容器裡的 uid=0 對映到的就是 host 的 uid=0。所以一旦你能寫到 host 的檔案(Bind Mount)、或能叫 daemon 幫你做事(docker.sock),那個「root」是貨真價實的 host root,不是被關在容器裡的假 root。

2. 容器不是虛擬機,大家共用同一個 kernel

Day 12 就講過:容器是共享 kernel 的程序隔離。這代表只要拿到 kernel 層的能力(例如 Day 16 的 privileged、Day 17 的某些 cap),就有機會直接作用到整台主機——因為那個 kernel 本來就是 host 的。VM 有獨立 kernel,容器沒有,這是兩者安全模型最根本的差別。

3. 幾乎每一條,都是「為了方便」開太大

把成因講白了,其實很接近 Day 11 對 Linux 提權的結論——授權範圍過大:

  • 掛載為了方便,直接把 host / 可寫掛進來。
  • 為了讓容器「什麼都能做」,直接 --privileged。
  • 為了監控/除錯,多加了一個 CAP_SYS_PTRACE 又配 --pid=host。
  • 為了讓容器能自己管容器,把 docker.sock 掛進去。

每一個單獨看都「有它的用途」,但只要超出實際需要,就變成攻擊面。

一個重要的提醒:枚舉決定你看不看得到這些

Day 13 其實是這整條線的基礎。上面那些邊界再怎麼脆弱,你得先枚舉到它們才有辦法開始計畫滲透。而且 Day 13 也強調過:沒有任何單一指標能百分之百確定你在容器還是主機(/.dockerenv、/proc/1/cgroup、mount… 都只是線索),一定要多方交叉驗證。

從單點到攻擊鏈:防守最容易漏掉的地方

這六天是一天拆一個單點,但真實攻擊往往是把幾個不算致命的錯誤設定串起來。舉例來說:就算一個容器沒有 privileged、沒有 docker.sock、也沒有多的 cap,只要它可寫掛了一個會被 host root 排程執行的目錄,攻擊者一樣能透過「枚舉→ 可寫掛載→ host root 排程」串成一條通往 host root 的路。

總結一句話:

防守不能只檢查「有沒有 privileged、有沒有掛 docker.sock」這種明顯項,要問的是「這個容器碰得到的每一樣東西,串起來會怎樣」。

防禦總表:怎麼守?

好消息是,上面的成因大多可以用同一組原則收斂掉:

面向 做法
掛載 最小化、盡量 :ro;/、/etc、/root、/var/run 等敏感路徑絕不掛進容器(Day 15)
控制面 不要把 docker.sock 掛進容器;真要給,用受限的代理介面(Day 14)
權限 預設 --cap-drop=ALL,只加回真正需要的那一個;不要 --privileged(Day 16/17)
身分 開 user namespace remap,讓容器 root 對映 host 非 root,斷開「容器 root = host root」
命名空間 避免 --pid=host、--net=host 這類鬆綁(Day 17)
審查/監控 用 docker inspect 檢查 Mounts、CapAdd、Privileged、PidMode,在部署前就擋下來(Day 29 會再展開)

核心原則只有一條:最小權限。給容器它真正需要的,而不是「先全開比較省事」。

小結

這六天從「容器隔離了什麼」一路走到「怎麼從容器逃到 host」,我自己整理下來最受用的三點是:

  1. 容器不是 VM:共享 kernel、容器 root 常等於 host root,這是所有逃逸的前提。
  2. 成因幾乎都是授權過大:掛太多、開 privileged、加不必要的 cap——和 Day 11 的 Linux 提權結論如出一轍。
  3. 單點不可怕,串起來才可怕:防守要用「關聯」的角度看,而不是只擋明顯項。

容器的內容就先到這裡告一段落,接下來這些觀念會延伸到 Kubernetes 。

我們會在明天先介紹一下甚麼是 Kubernetes ? 具體的架構有哪些? 接著再做 Kubernetes 常見的錯誤設定造成的資安風險。所以如果讀完這篇還不懂 Kubernetes 的讀者可以不用擔心!

謝謝大家的收看,我們明天見!


上一篇
Day 17|Linux Capabilities:不用完整 Root,也可能擁有危險能力
下一篇
Day 19|換成攻擊者視角看 Kubernetes:Cluster 裡到底有哪些攻擊面?
系列文
我以前被社會打穿,現在輪到我研究怎麼把系統打穿:資安工程師的 30 天紅隊轉職實驗 共 21 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言