Hpdoger Blog

Share with Web Security Knowledge


Wiz-K8S-LAN-PARTY题解及思考

Wiz K8S network挑战 TL;DR 此次挑战围绕Kubernetes网络服务展开,一共有5个题目。出题人在题目容器内提供了dnscan工具,dnscan能够针对给出的CIDR地址进行存活性探测,并根据IP地址反查其在集群中的域名地址。注意 ,每道题目都需要使用dnscan来确定本题开放的网络服务。 Challenge1 提示需要找到当前集群中存在的Web Service服务。在K8S集群中,Pod容器的「环境变量」包含了K8S的kube-apiserver通信地址,因此使用dnscan指定此类地址的B段扫描,能够得到集群存在的其他svc服务(如下图),在访问10.100.136.254的HTTP服务后拿到flag。 这里再拓展一点关于网格服务(Service Mesh)的「坑」,但和题目本身无关。当前题目环境所在的K8S集群使用istio作为Service Mesh,用来转发Pod出口、入口的网络流量。在做题过程中发现,在使用nmap对10.100.136.254容器进行端口扫描,单从结果上看,这个IP地址所在容器开放了TCP全端口。 这种错误的结果在Kubernetes Service Mesh场景很常见,也是信息收集过程中的坑点:TCP端口的假阳性。Kubernetes Internal Service Discovery这篇文章中,作者指出造成此现象的原因在与: 某些服务网格(例如 Istio)通过拦截某些 Pod 和服务的流量来工作,以提供功能更丰富的流量路由。在这种情况下,网格组件将为其配置范围内的所有有效端口和所有有效 IP 地址完成 TCP 三向握手,然后仅当存在到 pod 或服务的已配置服务网格路由时,才在后端转发连接。即使在关联的 IP 地址和/或端口上没有实际监听任何内容,这也会导致 TCP 端口看起来是打开的。当发生这种情况时,使用 TCP 握手来确定主机是否处于活动状态或端口是否打开的端口扫描程序将给出非常不准确的结果。在这些情况下,您只能依赖应用程序级别的响应返回,然后才能判断所谓的侦听 TCP 服务器是否确实背后有某些东西。 因此,通过「TCP握手」情况来判断端口状态,并不适用存在K8S Service Mesh的场景中,那么常见的nmap和fscan工具都是不可取的。作者实现了一个简易版本的端口扫描脚本:先建立TCP连接,使用socket发送捏造的TCP探针,根据是否有响应来判断端口的开放情况。当然这个脚本还是有点玩具性质,只对6379(redis)进行了处理而其他端口的探针一律捏造为HTTP请求。 Challenge2 依然使用dnscan发现svc地址:10.100.171.123 -> reporting-service.k8s-lan-party.svc.cluster.local. 同时,容器环境给了cap_net_admin,这允许我们通过tcpdump导出容器的虚拟网卡流量 题目描述告知,当前Pod容器启动时存在Sidecar,且让我们借助Sidecar获取敏感数据。 请求reporting-service后的HTTP结果如下图,表明当前集群使用istio-envoy作为sidecar istio-envoy是一个proxy层面的sidecar。istio envoy的架构参考如下图,简单理解就是envoy作为Pod Proxy,伴随每个Pod启动。Pod中容器的所有出、入口流量都会流经instio-envoy这个proxy sidecar。 当在题目环境中执行netstat -all查看通信情况时,发现当前容器和reporting-service服务建立了大量http连接,那这个reporting-service服务很可能就是容器用到的proxy sidecar。 分析容器和reporting-service通信的流量即可,使用tcpdump抓取发往reporting-service的所有流量后,在某个HTTP POST请求中找到flag值 Chall3 使用mount信息得知当前容器挂载了EFS文件系统,同时capsh信息依然保有cap_net_admin权限 fs-0779524599b7d5e7e.efs.us-west-1.amazonaws.com:/ on /efs type nfs4 (ro,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,noresvport,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.23.121,local_lock=none,addr=192.168.124.98) 通过mount挂载的EFS文件目录中有flag.txt文件,但当前player用户不具备读权限 EFS全称为Amazon Elastic File System,是用来给AWS EC2提供弹性存储空间的,EFS基于NFSv4.1 和 NFSv4.

Read more...

对Kubernetes RBAC授权防御&攻击的部分思考

写在前面 不久前看到一篇对Kubernetes授权管理的文章,笔者而后进行一些实验和思考,因此诞生了这篇学习笔记。本文章思路未必贴合实际应用场景,有概念错误的地方还望多多指正。 首先笔者对于Kubernetes用户的授权分为两个抽象主体:对「基础设施」的权限授予和对「服务」的权限授予。 分类讨论 0x01-基础设施 「基础设施」的权限,理想情况下按照集群结构划分: master节点使用admin用户账户权限,意即管理员权限 node节点使用普通用户权限,普通用户权限仅能对集群中的部分资源控制,或者是对某个namespcace中的资源进行控制 0x02-服务 什么又叫做对「服务」进行划分呢?云计算的初衷是优雅地调配庞大规模的服务群,那么运维人员就需要对A、B、C…这些服务(Service)能够操纵集群权限这个能力进行考量,假如Service A由三台nginx容器构成,可能就不需要什么集群服务的资源。对于B服务而言,其定位是用来监控集群主机健康状态的,所以就需要很强的「CURD」权限,起码是对「Pod」、「Deployment」有管理权限。 在「Kubernetes」中,每个服务容器都会被「kubelet」下发一个默认的「ServiceAccount」账户(后文简称SA),而运维人员可以指定服务容器注入哪个SA账户。这意味着运维人员可以将不同SA账户注入给各类服务容器,来达到各类服务(A、B、C)权限细粒度化。 0x03-归根到底 「基础设施」和「服务」的权限授予有相同之处,但又不完全相同。相同的地方在于概念,都是为了控制对集群资源操纵的能力,这一过程在「Kubernetes」中叫做「授权(Authoriazation)」,可以参考下图;不同的地方在于控制单元,基础设施的权限是以「用户账户(User Account)」的权限为单位的,而服务的权限是以「SA账户(ServiceAccount)」为单位划分的。 那么,有没有办法来优雅地控制对这两种权限授予方式的统一呢?答案很显然是有,而且不止一种。这里摘取一种比较好的做法:通过「ClusterRole」分发权限。 例如管理员事先建立「Pod-read-Rule」、「Pod-Write-Rule」规则,分别用来制约对Pod容器可读、可写这两种「操作」,当然集群的资源可不止有Pod容器,我们可以通过「kubectl api-resources」例举哪些资源可以被「ClusterRole」约束。 而后,通过「RoleBinding」或者「ClusterBinding」的方式将这两个「ClusterRole」分发给不同的「User Account」或者「ServiceAccount」,相当于是把锅碗瓢盆给到不同的角色。 假设一个场景,当「User Account A」需要拥有所有Pod的可读权限,那么管理员(kubernetes admin)就通过「RoleBinding」的形式将「Pod-read-Rule」绑定给「User Account A」;当「User Account A」再想要要所有Pod的可写权限时,以相同的「RoleBinding」方式将「Pod-Write-Rule」绑定给「User Account A」即可。 最终,我们创建一个理想化的场景:把集群内所有可利用资源(api-resources)的「CURD」操作分别建立为不同的「ClusterRole」,在创建「Service Account」与「User Account」时赋予它们最小权限,指定最少范围的「namespace」,最好只作用于default namespcace。而后当「SA」或者「UA」需要对应权限时,管理员再经过审核制「RoleBinding」,分配「ClusterRole」给这些「SA」或者「UA」,而不是在初始化时就把权限塞满。 实践 下面以两个实践为例,理解前文提到的权限分发思路。表格为实验拓扑:一台Centos7.x作为Master节点,剩下两台作为Node节点 主机 IP地址 内核版本 用户账户 k8s-master 192.168.56.80 3.10.0-1160.el7.x86_64 kubernetes-admin k8s-node1 192.168.56.81 3.10.0-1160.el7.x86_64 userA k8s-node2 192.168.56.82 3.10.0-1160.el7.x86_64 userB 实践场景1-使用UserAccount master节点使用kubernetes-admin用户进行管理,其他两个node节点使用userA、userB用户进行管理,且需要满足的条件: kubernetes-admin具有对集群操纵的全部权限 userA用户仅拥有对tenantA命名空间的Pod容器完全操作权限 userB用户仅拥有对tenantB命名空间的Pod容器完全操作权限 分析:由于「master」节点的用户配置文件「/etc/kubernetes/admin.conf」默认为kubernetes-admin权限,所以我们无需关心,只需满足UserA、B的需求。 第一步:建立两个Kubernetes User Account分别为userA、userB,参考文章:k8s创建用户账号——User Account - fat_girl_spring - 博客园 (cnblogs.com)。完整的创建过程如下,以相同的方式创建两个用户即可 [root@k8s-master tenantA]# (umask 077;openssl genrsa -out userA.

Read more...

justCTF2023-AWS Cognito认证服务的安全隐患

TL;DR justCTF2023 Easy Auth Cloud题目,有关AWS Cognito认证服务可能存在的安全隐患 这道题目和文章 Hacking AWS Cognito Misconfigurations 思路大致相同,利用「Cognito」服务的默认配置和一些错误用法进行权限提升,但是多出几个细节,这里简单归结几点如下: Web应用没有注册和登录功能,前端JavaScript代码简单混淆,能够通过AWS Cognito JavaScript SDK 拿到App Client ID, User Pool ID, Identity Pool ID, and region 信息 攻击者首先需要获得Web应用的认证,在修改用户属性后,「Cognito Identity Pool」会基于ABAC(attribute based access control)的方式提供给用户更高权限的「AWS Credentials」,之后利用「AWS Credentials」获取托管在云上的「lambda」代码并解密出flag值 Cognito认证 「Cognito」是AWS提供的一项全托管的认证、授权和用户管理服务,通过「Cognito」,开发人员可以不用自行编写认证、授权和用户管理的代码,而是通过「Cognito」的API来完成这些操作。「Cognito」提供两种核心服务,分别为「UserPool」和「Identity Pool」 UserPool:UserPool表示一个用户池,用于管理用户的注册、登录、身份验证和密码重置等操作。使用UserPool可以方便地创建和管理用户帐户,用以Web、Mobile APP的身份管理。此外,UserPool还支持社交身份验证,例如Facebook、Google等。 Identity Pool:Identity Pool表示身份池,用于管理访问AWS服务的用户身份认证和授权。它与UserPool不同,它可以提供跨不同平台(不同应用程序和设备)的单一登录。身份池为应用程序用户提供了一组临时安全证书,这些证书可用于访问AWS服务。 通过这两种服务,开发人员可以方便地创建、管理和验证用户帐户,并管理应用程序的访问权限和安全性。 题目复现 开局给了个登陆页面,经过简单阅读前端JS代码后发现有对「Cognito SDK」的使用,调出前端控制台debug偏移就能够拿到「Cognito」信息 Client ID User Pool ID Identity Pool ID region g1l1udtdgp1cu30fogbucvh4d eu-west-1_sEBJdM3TJ eu-west-1:a4b696bc-7cc2-4818-a045-2ff49b601cbc eu-west-1 后续的所有步骤都需要借助「aws-cli」,首先根据「clientID」向「Cognito」服务注册账号 aws cognito-idp sign-up --client-id "g1l1udtdgp1cu30fogbucvh4d" --region "eu-west-1" --username "hpdoger1" --password "*()Hpdoger123" 紧接着使用注册的账号登陆「User Pool」,作者只配置了「USER_SRP_AUTH」这种认证方式,我选择用「SDK」进行登陆模拟,运行如下登录脚本会打印用户登陆后的各种「Token」信息

Read more...
1 of 1