原文地址:?https://mp.weixin.qq.com/s/nZu4p5ZSmyzOVhQleIXQpg
1. 前言
?1.1 背景
隨著云原生技術(shù)的普及,其暴露出來的攻擊面也被黑客們念念不忘,相關(guān)的攻擊技術(shù)也跟著被“普及”,自動(dòng)化漏洞利用攻擊工具更是如雨后春筍般出現(xiàn)在GitHub開源平臺(tái),其中比較有代表性的如cdk-team/CDK。其是一款為容器環(huán)境定制的滲透測(cè)試工具,在已攻陷的容器內(nèi)部提供零依賴的常用命令及PoC/EXP。集成Docker/K8s場(chǎng)景特有的 逃逸、橫向移動(dòng)、持久化利用方式,插件化管理。
在漏洞利用門檻如此低廉的今天,作為企業(yè)安全的建設(shè)者(搬磚人),除了考慮部署容器層面運(yùn)行時(shí)檢測(cè)平臺(tái),在k8s api-server層面,啟用日志審計(jì)功能,也是一個(gè)成本低廉又高效發(fā)現(xiàn)入侵攻擊的途徑。
通過對(duì)api-server的日志進(jìn)行審計(jì)分析,對(duì)于攻擊者的信息收集行為,部署k8s cronjob后門、利用rbac做權(quán)限提升等持久化攻擊行為都能及時(shí)的發(fā)現(xiàn)并輸出告警。
1.2 kubernetes日志審計(jì)介紹
Kubernetes 審計(jì)功能提供了與安全相關(guān)的按時(shí)間順序排列的記錄集,記錄單個(gè)用戶、管理員或系統(tǒng)其他組件影響系統(tǒng)的活動(dòng)順序。 它能幫助集群管理員處理以下問題:
審計(jì)日志示例(圖片來自參考[5]

如何開啟日志審計(jì)
1、api-server命令行啟動(dòng)時(shí),添加如下參數(shù)
2、kubeadm啟動(dòng)時(shí)
修改api-server配置文件/etc/kubernetes/manifests/kube-apiserver.yaml,增加如下內(nèi)容
2. 攻擊行為檢測(cè)
2.1 分析流程
若攻擊者通過各種手段拿到一個(gè)容器shell環(huán)境后,下一步必然是利用這個(gè)容器進(jìn)行信息收集,獲取更多敏感信息或機(jī)器資源,此時(shí),CDK作為一款自動(dòng)化的攻擊工具,就讓整個(gè)攻擊過程如虎添翼,大大提高攻擊效率。
作為甲方安全的守護(hù)者,從安全建設(shè)的角度,如何有效及時(shí)的發(fā)現(xiàn)攻擊者的入侵行為,是一個(gè)無法避開的問題。本文通過觀察cdk的攻擊行為,從k8s日志審計(jì)的角度羅列一些入侵檢測(cè)的常見規(guī)則。
目前,針對(duì)于日志的審計(jì)分析,我們落地方案的整個(gè)流程為:

注:下列場(chǎng)景,僅限于原生的CDK行為檢測(cè),如果攻擊者具備開發(fā)能力,修改對(duì)應(yīng)特征,可以繞過對(duì)應(yīng)的檢測(cè)規(guī)則。
2.2 信息收集
CDK在容器內(nèi)主要收集以下信息:

工具在鏡像內(nèi)運(yùn)行,其信息收集的結(jié)果執(zhí)如下:

從圖上可知,信息收集的整個(gè)過程,存在與api-server交互的僅有網(wǎng)絡(luò)探測(cè)部分,總共會(huì)向api-server發(fā)送三條請(qǐng)求,分別為API-server、namespace、api信息探測(cè)。
2.2.1 探測(cè)API-server
audit.log
{
? ?"kind": "Event",
? ?"apiVersion": "audit.k8s.io/v1",
? ?"level": "Metadata",
? ?"stage": "ResponseComplete",
? ?"requestURI": "/",
? ?"verb": "get",
? ?"user": {
? ? ? ?"username": "system:anonymous",
? ? ? ?"groups": [
? ? ? ? ? ?"system:unauthenticated"
? ? ? ?]
? ?},
? ?"sourceIPs": [
? ? ? ?"172.18.0.2"
? ?],
? ?"userAgent": "Go-http-client/1.1",
? ?"responseStatus": {
? ? ? ?"metadata": {},
? ? ? ?"status": "Failure",
? ? ? ?"reason": "Forbidden",
? ? ? ?"code": 403
? ?},
? ?"requestReceivedTimestamp": "2023-02-02T08:29:12.189459Z",
? ?"stageTimestamp": "2023-02-02T08:29:12.189553Z",
? ?"annotations": {
? ? ? ?"authorization.k8s.io/decision": "forbid",
? ? ? ?"authorization.k8s.io/reason": ""
? ?}
}
2.2.2 列舉namespace
audit.log
{
? ?"kind": "Event",
? ?"apiVersion": "audit.k8s.io/v1",
? ?"level": "Metadata",
? ?"auditID": "41770e7a-1827-4a14-860f-a812d3db1647",
? ?"stage": "ResponseComplete",
? ?"requestURI": "/api/v1/namespaces",
? ?"verb": "list",
? ?"user": {
? ? ? ?"username": "system:serviceaccount:test:default",
? ? ? ?"uid": "63b8dd88-88dd-4426-bdd1-7966906dc0d5",
? ? ? ?"groups": [
? ? ? ? ? ?"system:serviceaccounts",
? ? ? ? ? ?"system:serviceaccounts:test",
? ? ? ? ? ?"system:authenticated"
? ? ? ?]
? ?},
? ?"sourceIPs": [
? ? ? ?"172.18.0.2"
? ?],
? ?"userAgent": "Go-http-client/1.1",
? ?"objectRef": {
? ? ? ?"resource": "namespaces",
? ? ? ?"apiVersion": "v1"
? ?},
? ?"responseStatus": {
? ? ? ?"metadata": {},
? ? ? ?"status": "Failure",
? ? ? ?"reason": "Forbidden",
? ? ? ?"code": 403
? ?},
? ?"requestReceivedTimestamp": "2023-02-02T08:29:12.205422Z",
? ?"stageTimestamp": "2023-02-02T08:29:12.205485Z",
? ?"annotations": {
? ? ? ?"authorization.k8s.io/decision": "forbid",
? ? ? ?"authorization.k8s.io/reason": ""
? ?}
}
2.2.3 探測(cè)可訪問的api
跟namespace類似,只不過requestURI為/apis
2.2.4 總結(jié)
綜上, 上述日志中可概述為,當(dāng)cdk在做信息收集時(shí),會(huì)向api-server請(qǐng)求3次,分別訪問/,/apis,/api/v1/namespaces,可根據(jù)這些特征做告警規(guī)則
1、userAgent: Go-http-client/1.1 ? ? ?# CDK特定的userAgent,此時(shí),該字段為主要特征
2、responseStatus.code: 403 ? ? ? ? ? #默認(rèn)serviceaccount無權(quán)限時(shí) api-server返回的狀態(tài)碼
3、requestURI: / ? ? ? ? ? ? ? ? ? ? ?# 訪問根目錄
2.3 漏洞利用
Exploit模塊包含的功能:

2.3.1 容器逃逸
其中,容器逃逸層面,一般是不需要與api-server做交互的,也就不會(huì)留下日志。但部分容器逃逸手段是利用錯(cuò)誤配置的pod在創(chuàng)建時(shí)實(shí)施攻擊。因此,容器逃逸的檢測(cè)必須前置到容器創(chuàng)建時(shí),記錄下應(yīng)用請(qǐng)求的權(quán)限,然后結(jié)合運(yùn)行時(shí)入侵檢測(cè)做進(jìn)一步監(jiān)控。
當(dāng)一個(gè)擁有privilege、sys_admin、network、ipc等特殊權(quán)限的pod創(chuàng)建時(shí),它的日志記錄是這樣的。
audit.json
{
? ?"kind": "Event",
? ?"apiVersion": "audit.k8s.io/v1",
? ?"level": "RequestResponse",
? ?"stage": "ResponseComplete",
? ?"requestURI": "/api/v1/namespaces/testpods/pods",
? ?"verb": "create",
? ?"user": {
? ? ? ?"username": "kubernetes-admin",
? ? ? ?"groups": [
? ? ? ? ? ?"system:masters",
? ? ? ? ? ?"system:authenticated"
? ? ? ?]
? ?},
? ?"sourceIPs": [
? ? ? ?"172.18.0.1"
? ?],
? ?"userAgent": "kubectl1.16.15/v1.16.15 (darwin/amd64) kubernetes/2adc8d7",
? ?"objectRef": {
? ? ? ?"resource": "pods",
? ? ? ?"namespace": "testpods",
? ? ? ?"name": "testpod",
? ? ? ?"apiVersion": "v1"
? ?},
? ?"responseStatus": {
? ? ? ?"metadata": {},
? ? ? ?"code": 201
? ?},
? ?"responseObject": {
? ? ? ?"kind": "Pod",
? ? ? ?"apiVersion": "v1",
? ? ? ?"metadata": {
? ? ? ? ? ?"name": "testpod",
? ? ? ? ? ?"namespace": "testpods",
? ? ? ? ? ?"selfLink": "/api/v1/namespaces/testpods/pods/testpod",
? ? ? ? ? ?"uid": "e717d204-7e6d-4608-998b-648a8667e8e1",
? ? ? ? ? ?"resourceVersion": "13517",
? ? ? ? ? ?"creationTimestamp": "2023-02-02T09:51:10Z",
? ? ? ? ? ?"labels": {
? ? ? ? ? ? ? ?"creator": "zhiye",
? ? ? ? ? ? ? ?"team": "teamf"
? ? ? ? ? ?}
? ? ? ?},
? ? ? ?"spec": {
? ? ? ? ? ?"volumes": [
? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ?"name": "rootfs",
? ? ? ? ? ? ? ? ? ?"hostPath": {
? ? ? ? ? ? ? ? ? ? ? ?"path": "/",
? ? ? ? ? ? ? ? ? ? ? ?"type": ""
? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?}
? ? ? ? ? ?],
? ? ? ? ? ?"containers": [
? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ?"name": "trpe",
? ? ? ? ? ? ? ? ? ?"image": "alpine",
? ? ? ? ? ? ? ? ? ?"command": [
? ? ? ? ? ? ? ? ? ? ? ?"/bin/sh",
? ? ? ? ? ? ? ? ? ? ? ?"-c",
? ? ? ? ? ? ? ? ? ? ? ?"tail -f /dev/null"
? ? ? ? ? ? ? ? ? ?],
? ? ? ? ? ? ? ? ? ?"resources": {},
? ? ? ? ? ? ? ? ? ?"volumeMounts": [
? ? ? ? ? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? ? ? ? ?"name": "default-token-mm6s8",
? ? ? ? ? ? ? ? ? ? ? ? ? ?"readOnly": true,
? ? ? ? ? ? ? ? ? ? ? ? ? ?"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ?],
? ? ? ? ? ? ? ? ? ?"terminationMessagePath": "/dev/termination-log",
? ? ? ? ? ? ? ? ? ?"terminationMessagePolicy": "File",
? ? ? ? ? ? ? ? ? ?"imagePullPolicy": "Always",
? ? ? ? ? ? ? ? ? ?"securityContext": {
? ? ? ? ? ? ? ? ? ? ? ?"capabilities": {
? ? ? ? ? ? ? ? ? ? ? ? ? ?"add": [
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"SYS_ADMIN"
? ? ? ? ? ? ? ? ? ? ? ? ? ?]
? ? ? ? ? ? ? ? ? ? ? ?},
? ? ? ? ? ? ? ? ? ? ? ?"privileged": true
? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?}
? ? ? ? ? ?],
? ? ? ? ? ?"restartPolicy": "Always",
? ? ? ? ? ?"terminationGracePeriodSeconds": 30,
? ? ? ? ? ?"dnsPolicy": "ClusterFirst",
? ? ? ? ? ?"serviceAccountName": "default",
? ? ? ? ? ?"serviceAccount": "default",
? ? ? ? ? ?"hostNetwork": true,
? ? ? ? ? ?"hostPID": true,
? ? ? ? ? ?"hostIPC": true,
? ? ? ? ? ?"securityContext": {},
? ? ? ? ? ?"schedulerName": "default-scheduler",
? ? ? ? ? ?"tolerations": [
? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ?"key": "node.kubernetes.io/not-ready",
? ? ? ? ? ? ? ? ? ?"operator": "Exists",
? ? ? ? ? ? ? ? ? ?"effect": "NoExecute",
? ? ? ? ? ? ? ? ? ?"tolerationSeconds": 300
? ? ? ? ? ? ? ?},
? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ?"key": "node.kubernetes.io/unreachable",
? ? ? ? ? ? ? ? ? ?"operator": "Exists",
? ? ? ? ? ? ? ? ? ?"effect": "NoExecute",
? ? ? ? ? ? ? ? ? ?"tolerationSeconds": 300
? ? ? ? ? ? ? ?}
? ? ? ? ? ?],
? ? ? ? ? ?"priority": 0,
? ? ? ? ? ?"enableServiceLinks": true
? ? ? ?},
? ? ? ?"status": {
? ? ? ? ? ?"phase": "Pending",
? ? ? ? ? ?"qosClass": "BestEffort"
? ? ? ?}
? ?},
? ?"requestReceivedTimestamp": "2023-02-02T09:51:10.632436Z",
? ?"stageTimestamp": "2023-02-02T09:51:10.660958Z",
? ?"annotations": {
? ? ? ?"authorization.k8s.io/decision": "allow",
? ? ? ?"authorization.k8s.io/reason": ""
? ?}
}
從日志上可以看出,針對(duì)于錯(cuò)誤配置導(dǎo)致的逃逸,我們可以關(guān)注以下幾個(gè)日志字段,制定告警規(guī)則。
responseObject.spec.volumes ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # 檢測(cè)敏感卷,是否掛載docker.sock等
responseObject.spec.containers.volumeMounts ? ? ? ? ? ? ? ? ? ? ? ? # 檢測(cè)敏感掛載,是否掛載docker.sock等
responseObject.spec.containers.securityContext.capabilities.add ? ? # 是否使用SYS_ADMIN權(quán)限,(字段嵌套這么多層,真的得吐槽
responseObject.spec.containers.securityContext.privileged ? ? ? ? ? # 檢測(cè)是否為特權(quán)pod容器
responseObject.spec.hostNetwork ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # 是否使用宿主機(jī)網(wǎng)絡(luò)
responseObject.spec.hostPID ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # 是否使用宿主機(jī)hostPID
responseObject.spec.hostIPC ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # 是否共享宿主機(jī)內(nèi)存
responseObject.spec.serviceAccount ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?# 是否使用特殊的serviceaccount ?默認(rèn)為default
2.3.2 網(wǎng)絡(luò)探測(cè)
此功能為端口掃描+指紋識(shí)別,不涉及與API交互,未產(chǎn)生日志。
2.3.3 信息竊取
與api-server交互的為secrets、config和psp,如果是自動(dòng)獲取的話,cdk會(huì)發(fā)送兩次請(qǐng)求,分別使用匿名賬戶和當(dāng)前serviceaccout做list動(dòng)作。
下邊列舉些主要特征
requestURI: /api/v1/secrets,
requestURI: /api/v1/configmaps
requestURI: /apis/policy/v1beta1/podsecuritypolicies
userAgent: Go-http-client/1.1
user.username: "system:anonymous"
responseStatus.code: 403
"verb": "list"
2.3.4 權(quán)限提升
RBAC權(quán)限繞過
日志如下
{
? ?"kind": "Event",
? ?"apiVersion": "audit.k8s.io/v1",
? ?"level": "RequestResponse",
? ?"auditID": "bfc643d6-8337-434e-9dec-ba41dd36bfa7",
? ?"stage": "ResponseComplete",
? ?"requestURI": "/api/v1/namespaces/kube-system/pods",
? ?"verb": "create",
? ?"user": {
? ? ? ?"username": "system:serviceaccount:test:default",
? ? ? ?"uid": "63b8dd88-88dd-4426-bdd1-7966906dc0d5",
? ? ? ?"groups": [
? ? ? ? ? ?"system:serviceaccounts",
? ? ? ? ? ?"system:serviceaccounts:test",
? ? ? ? ? ?"system:authenticated"
? ? ? ?]
? ?},
? ?"sourceIPs": [
? ? ? ?"172.18.0.3"
? ?],
? ?"userAgent": "Go-http-client/1.1",
? ?"objectRef": {
? ? ? ?"resource": "pods",
? ? ? ?"namespace": "kube-system",
? ? ? ?"apiVersion": "v1"
? ?},
? ?"responseStatus": {
? ? ? ?"metadata": {},
? ? ? ?"status": "Failure",
? ? ? ?"reason": "Forbidden",
? ? ? ?"code": 403
? ?},
? ?"responseObject": {
? ? ? ?"kind": "Status",
? ? ? ?"apiVersion": "v1",
? ? ? ?"metadata": {},
? ? ? ?"status": "Failure",
? ? ? ?"message": "pods is forbidden: User \"system:serviceaccount:test:default\" cannot create resource \"pods\" in API group \"\" in the namespace \"kube-system\"",
? ? ? ?"reason": "Forbidden",
? ? ? ?"details": {
? ? ? ? ? ?"kind": "pods"
? ? ? ?},
? ? ? ?"code": 403
? ?},
? ?"requestReceivedTimestamp": "2023-02-03T09:56:24.061825Z",
? ?"stageTimestamp": "2023-02-03T09:56:24.061888Z",
? ?"annotations": {
? ? ? ?"authorization.k8s.io/decision": "forbid",
? ? ? ?"authorization.k8s.io/reason": ""
? ?}
}
由上可發(fā)現(xiàn)如下特征:
Pod內(nèi)serviceaccount無權(quán)限的情況
requestURI: /api/v1/namespaces/kube-system/pods
userAgent: Go-http-client/1.1
responseStatus.code: 403
Pod內(nèi)serviceaccount有權(quán)限的情況
requestURI: /api/v1/namespaces/kube-system/pods
responseObject.metadata.selfLink: /api/v1/namespaces/kube-system/pods/cdk-rbac-bypass-create-pod ?
responseObject.metadata.spec.containers.args: *cat /run/secrets/kubernetes.io/serviceaccount/token*
verb: create
2.3.5 持久化
部署daemonset后門
源碼是這樣定義的(參考[6]):

體現(xiàn)到日志中,有以下幾個(gè)點(diǎn):
objectRef.name: cdk-backdoor-daemonset
objectRef.namespace: kube-system
responseObject.metadata.selfLink: /apis/apps/v1/namespaces/kube-system/daemonsets/cdk-backdoor-daemonset
responseObject.spec.template.spec.volumes.hostPath.path: /
responseObject.spec.template.spec.containers.name: cdk-backdoor-pod
responseObject.spec.template.spec.containers.securityContext[capabilities:ptivileged]:如圖上所示
responseObject.spec.template.spec.[hostNetwork|hostPID]: true
部署K8S CronJob
CDK源代碼是這樣定義的(參考[7]):

體現(xiàn)在日志中,特征有以下幾點(diǎn):
requestURI: /apis/batch/v1beta1/namespaces/kube-system/cronjobs
verb: create
objectRef.name: cdk-backdoor-cronjob
responseObject.matadata.name: cdk-backdoor-cronjob
responseObject.matadata.selfLink: /apis/batch/v1beta1/namespaces/kube-system/cronjobs/cdk-backdoor-cronjob
responseObject.spec.jobTemplate.spec.template.spec.containers.name: cdk-backdoor-cronjob-container
部署影子k8s api-server
在pod權(quán)限足夠的情況下,通過創(chuàng)建shadow api-server做權(quán)限維持,詳情見參考[4]
在非二開的情況下,通過k8s日志升級(jí)可檢測(cè)以下幾個(gè)字段
objectRef.name: *-shadow-*
responseObject.metadata.labels.component: kube-apiservershadow
responseObject.spec.containers.command: "--secure-port=9444"
2.3.6 總結(jié)
因?yàn)槁┒蠢貌糠郑瑒?dòng)作都普遍較大,因此可觀測(cè)字段已不僅僅局限于userAgent,其特征均比較明顯,極富有工具本身特色
2.4 API利用
2.4.1 Tool模塊
此處需要關(guān)注的(跟api-server有交互的)為kcurl命令,此命令可借助高權(quán)限serviceaccount賬戶列舉/使用k8s資源。
執(zhí)行./cdk kcurl default get 'https://10.96.0.1:443/api/v1/nodes' ,日志內(nèi)容如下:
audit.log
{
? ?"kind": "Event",
? ?"apiVersion": "audit.k8s.io/v1",
? ?"level": "Metadata",
? ?"auditID": "418cafa5-2c1e-4fbf-b086-3d68e321d2bb",
? ?"stage": "ResponseComplete",
? ?"requestURI": "/api/v1/nodes",
? ?"verb": "list",
? ?"user": {
? ? ? ?"username": "system:serviceaccount:test:default",
? ? ? ?"uid": "63b8dd88-88dd-4426-bdd1-7966906dc0d5",
? ? ? ?"groups": [
? ? ? ? ? ?"system:serviceaccounts",
? ? ? ? ? ?"system:serviceaccounts:test",
? ? ? ? ? ?"system:authenticated"
? ? ? ?]
? ?},
? ?"sourceIPs": [
? ? ? ?"172.18.0.3"
? ?],
? ?"userAgent": "Go-http-client/1.1",
? ?"objectRef": {
? ? ? ?"resource": "nodes",
? ? ? ?"apiVersion": "v1"
? ?},
? ?"responseStatus": {
? ? ? ?"metadata": {},
? ? ? ?"code": 200
? ?},
? ?"requestReceivedTimestamp": "2023-02-06T09:51:51.790260Z",
? ?"stageTimestamp": "2023-02-06T09:51:51.791075Z",
? ?"annotations": {
? ? ? ?"authorization.k8s.io/decision": "allow",
? ? ? ?"authorization.k8s.io/reason": "RBAC: allowed by ClusterRoleBinding \"defaultadmin\" of ClusterRole \"cluster-admin\" to ServiceAccount \"default/test\""
? ?}
}
日志中annotations.authorization.k8s.io/reason給出了允許執(zhí)行的原因。我們可以根據(jù)如下三個(gè)字段制定告警規(guī)則:
serviceaccount有權(quán)限的情況下:
annotations.authorization.k8s.io/reason
annotations.authorization.k8s.io/decision
userAgent: Go-http-client/1.1
responseStatus.code:200
無權(quán)限的情況下:
userAgent: Go-http-client/1.1
responseStatus.code:403
2.4.2 總結(jié)
因?yàn)閠ool模塊需要借助高權(quán)限serviceaccount或token,因此,權(quán)限不足的情況下,api-server返回responseStatus.code: 403 的條目說明了api-server接受到了非預(yù)期請(qǐng)求,結(jié)合userAgent等信息便可輸出可疑告警。
3. 不僅僅是CDK
3.1 CVE-2022-3172 K8S聚合?
API(Aggregation API)SSRF漏洞.
聚合?API?實(shí)際上是在?kube-apiserver?中運(yùn)行的,在新?API?注冊(cè)之前,它并不會(huì)工作。如果要添加新的?API,則需要?jiǎng)?chuàng)建一個(gè)APIService?對(duì)象,用來申請(qǐng)?Kubernetes?中新的?URL?路徑。注冊(cè)成功后,當(dāng)有發(fā)送到此路徑中的請(qǐng)求,則會(huì)被轉(zhuǎn)發(fā)到已經(jīng)注冊(cè)的?APIService?上。
APIService?可以將客戶端的請(qǐng)求轉(zhuǎn)發(fā)到任意的?URL?上,這就有可能會(huì)導(dǎo)致?Client?發(fā)送請(qǐng)求時(shí),所攜帶的一些認(rèn)證信息可能會(huì)被發(fā)送給第三方。
通過日志審計(jì)監(jiān)控responseStatus.code字段來進(jìn)行判斷是否有出現(xiàn)重定向的情況,通過檢測(cè)如下字段:
responseObject.code:302
responseObject.code:301
3.2 使用不合規(guī)鏡像創(chuàng)建pod
除了cdk這種常見的攻擊手法,還有一些常見的異常行為需要我們關(guān)注,比如一般企業(yè)內(nèi),pod創(chuàng)建使用的容器均會(huì)從企業(yè)私有的鏡像倉庫中拉取,此時(shí)如果日志中出現(xiàn)了公共倉庫的鏡像,則可判斷為異常,可關(guān)注以下字段
verb : create
level:RequestResponse
esponseObject.kind:Pod
requestObject.spec.containers.image:鏡像倉庫地址
3.3 pod命令執(zhí)行
對(duì)于kubectl exec命令進(jìn)行監(jiān)控,可對(duì)如下字段進(jìn)行監(jiān)控,注:命令執(zhí)行的后續(xù)無法通過日志審計(jì)來進(jìn)一步監(jiān)控,需結(jié)合運(yùn)行時(shí)檢測(cè)進(jìn)一步分析
objectRef.subresource:exec
objectRef.subresource:attach
userAgent
4. 落地實(shí)踐踩過的坑
1、主要為k8s日志類型比較多,且每種類型的字段名,字段數(shù)量均不一致,導(dǎo)致es在存儲(chǔ)數(shù)據(jù)時(shí)存在索引內(nèi)字段類型不一致無法解析存儲(chǔ)的問題。
解決方案為對(duì)于字段不一致的obj,選擇為不做深層次解析。(或者使用hdfs等存儲(chǔ)方式,查詢時(shí)對(duì)字段進(jìn)行解析
2、日志量過大,導(dǎo)致api-server磁盤讀寫io過高
持續(xù)優(yōu)化audit.yaml中的日志規(guī)則,對(duì)于其中的node/status,pod/status,coordination.k8s.io/leases等不做日志記錄。
5. 總結(jié)
本文從k8s日志審計(jì)的角度,分析當(dāng)使用cdk等自動(dòng)化攻擊時(shí),能夠從日志中獲取到的信息,并給出通過這些信息可監(jiān)控字段的告警示例。因?yàn)檫@部分字段都比較固定,完全可以通過機(jī)器學(xué)習(xí)提升告警準(zhǔn)確率。拋磚引玉,希望后續(xù)可以看到更多日志的分析防御角度。
當(dāng)然,CDK作為開源工具,這些特征都可以做關(guān)鍵字替換。因此,筆者認(rèn)為功夫應(yīng)該用到平時(shí),加強(qiáng)k8s的基線管控,比如避免出現(xiàn)高serviceaccount權(quán)限、通過準(zhǔn)入策略限制使用的docker鏡像,并部署容器運(yùn)行時(shí)入侵檢測(cè)平臺(tái)。讓安全能力覆蓋每個(gè)環(huán)節(jié),才能保證集群的安全穩(wěn)定。
6. 參考
[1] https://github.com/cdk-team/CDK
[2] https://www.cdxy.me/?p=839
[3] https://kubernetes.io/docs/tasks/debug/debug-cluster/audit/
[4] https://discuss.kubernetes.io/t/security-advisory-cve-2022-3172-aggregated-api-server-can-cause-clients-to-be-redirected-ssrf/21322
[5]https://github.com/tencentyun/qcloud-documents/blob/master/product/%E5%AD%98%E5%82%A8%E4%B8%8ECDN/%E6%97%A5%E5%BF%97%E6%9C%8D%E5%8A%A1/%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/TKE%20%E5%AE%A1%E8%AE%A1%E6%97%A5%E5%BF%97%E5%88%86%E6%9E%90.md
[6]?https://github.com/cdk-team/CDK/blob/main/pkg/exploit/k8s_backdoor_daemonset.go#LL35-L87C2
[7]?https://github.com/cdk-team/CDK/blob/main/pkg/exploit/k8s_cronjob.go#LL34-L59C2
以上是今天的分享內(nèi)容,如果你有什么想法或疑問,歡迎大家在留言區(qū)與我們互動(dòng),如果喜歡本期內(nèi)容的話,歡迎點(diǎn)個(gè)“在看”吧!
往期精彩指路
標(biāo)簽: