- 入口
- Challenge 1 - Recon
- Challenge 2 - Finding Neighbours
- Challenge 3 - Data Leakage
- 后记
- Challenge 4 - Bypass Boundaries
- Challenge 5 - Lateral Movement
- 小结
- Reference
入口
https://www.k8slanparty.com/
Challenge 1 - Recon
这道题的目的是想让你找到隐藏在K8s集群内的其它服务。
大致实验过程如下:
查询当前容器的ip:
发现 是 192.168.11.169/31
,说明主机只可能是192.168.11.168
和192.168.11.169
,既然192.168.11.169
是当前容器的网络地址,那就说明192.168.11.168
是广播地址,说明这个网段没有别的服务。但还是不放心,于是用dnscan
先探查下,果然没结果:
查看是否在kubernetes内,有的话确认下kubernetes API Server的地址:
发现确实是在kubernetes集群中,且kubernetes API Server的地址是:https://10.100.0.1
。
接着继续信息收集,先后查看了/etc/hosts
、/etc/hostname
文件,但内容都是空的。
查看DNS配置文件/etc/resolve.conf
,发现当前kubernetes集群的DNS服务器为:10.100.120.34
。可以看出DNS服务器和Kubernetes API Server位于同一个B段。
所以 ,可以用dnscan
探查下在这个B段里能不能找到其他的服务。
dnscan -subnet 10.100.0.0/16
,成功在k8s集群中找到了另一个服务的IP和域名:
用curl
直接访问该主机的80端口,得到flag:
Challenge 2 - Finding Neighbours
感觉跟Challenge 1很类似,也是发现在k8s集群里其他服务。
大致实验过程如下:
使用命令tcpdump -i ns-313861 dst host 10.100.171.123 and dst port 80 -w reporting-service.pcap
监听网络通信,并将流量捕获到文件reporting-service.pcap
:
使用命令tcpdump -r ./reporting-service.pcap -A
查看数据包内容,可以看到flag在里面:
直接过滤下,tcpdump -r ./reporting-service.pcap -A |grep -i "wiz_k8s_lan_party"
PS:其实用dig -x <IP>
进行DNS反向查找也是可以的,不一定要用它提供的dnscan
命令,如下图 :
先通过netstat -pant
查找网络连接情况,发现IP10.100.171.123
然后使用dig
进行反向查找,发现对应的域名:
Challenge 3 - Data Leakage
这道题的题目说到,目标系统使用了一个过时的支持云的数据存储,但该技术是在仅支持网络级别的访问控制的时代引入的。
换言之,漏洞就在于访问控制,绕过它就能获取flag。
进入环境后,我还是跟前两道题一样,对10.100.0.1/16
网段先做了其他服务发现的操作,但没有收获。
既然是存储,那就先看看有没有挂载到本地,执行df -hT
查看:
发现目标,可以看到将远程AWS EFS存储挂载到了本地/efs
目录,服务的主机域名是:fs-0779524599b7d5e7e.efs.us-west-1.amazonaws.com
,对应的IP是192.168.124.98
查看下/efs
目录,发现了flag文件:
不过我们无法查看flag文件的内容,因为它的权限位为000
。
既然题目说了,访问控制是仅基于网络层面的,就自然而然不会是想着通过本地方式去读取文件。
于是笔者通过nfs+TAB键查看下系统内有没有一些疑似nfs的客户端工具,可以看到有如下这些命令:
显而易见的,nfs-cat
就是我想要的,问题是该如何使用。
nfs-cat
,目标环境没有该命令详细的帮助信息:
先凭自己的理解,尝试读取flag看看。但等了几十秒左右,结果报错如下:
而且在等待的过程中,用netstat -pant
查看网络情况,发现很奇怪,nfs-cat
连接的并不是目标的2049
端口,竟然是111
端口:
因为从GPT给我的信息来看,NFS服务的默认端口是2049
端口。
难道还需要显式指定目标端口?这就有必要看看nfs-cat
的帮助文档了。
既然在该CTF的环境里没有man手册,那就在自己的Ubuntu服务器上安装nfs-cat
看看,如图:
可以看到,nfs-cat
的man手册也很简略,但它指明了代码仓库http://github.com/sahlberg/libnfs
:
详细说明应该在代码仓的帮助文件里有写。
从它的README文档可以了解到,该命令在使用时,如果不显示指定,则用的是NFS v3版本的协议,从CTF的环境里通过nfsstat -m
或 mount |grep -i nfs
,可以看到环境里NFS用的是v4版本:
所以需要在url里加上参数version=4
指定版本,如下图:
可以看到结果不同了,但报错是意料之中,也就是没有权限访问的意思。
继续查看nfs-cat
的README文档,发现竟然可以通过uid
、gid
指定以什么用户身份去访问:
从flag.txt
文件的权限为可知,只有root
用户才可以读。所以我们就在nfs url中指定uid=0
,如下图,成功读取flag文件的内容:
后记
回头看下这道题的题目,一开始我用nfs-cat
查看flag文件失败,总以为是要加上代理,或者是建立隧道才能绕过所谓的基于网络的访问控制策略。因为从题目来看,我以为是有IP白名单,也就是EFS服务设置了IP白名单策略。于是我一直在搜寻某个服务器存在代理端口的可能,又或是通过iptunnel
这种工具在本机和目标EFS服务建立隧道。但经过一番折腾,都行不通。一是根本找不到也无法判断是否存在代理端口,因为存在Istio,所以使用nmap探测端口,都是open
状态;二是iptunnel
建立隧道需要在通信两端都进行设置才行。
从答案再看题目,我才有点理解了这里说的基于网络的访问控制的意思,在url指定uid参数去指定以某种用户角色权限去访问,这也确实算是基于网络的访问控制,就是在协议上加上参数去指定,也没毛病。
Challenge 4 - Bypass Boundaries
View Policy:
从题目的意思,大概是说,服务网格技术(典型的:Istio)对于(恶意的)root用户来说是很有吸引力的。
简单了解下什么是Istio:
从给出的AuthorizationPolicy策略来看,是不允许来自当前空间(k8s-lan-party)内的http get/post流量访问某个受保护的pod的,这个pod名给的是个占位符${flag-pod-name}
,大概率是如果绕过了该策略的防护,访问到该pod提供的服务,就能拿到flag。
用之前的dnscan -subnet 10.100.0.1/16
获取到对应的flag pod的主机域名为istio-protected-pod-service.k8s-lan-party.svc.cluster.local
,用curl访问,结果命中了前面的Istio的策略,无法获取flag:
想不到其他办法,在/etc/passwd
查看到有两个普通用户,一个是istio
,一个是player
,因为这道题跟Istio有关,于是靠感觉,切换到了istio
用户,再次用curl访问 ,bingo!竟然获取到了flag:
从Istio的github issue里找到了原因所在:
https://github.com/istio/istio/issues/4286
其实Istio的wiki里也写到了:
https://github.com/istio/istio/wiki/Understanding-IPTables-snapshot#use-pid-to-get-iptables
总结下就是说,uid=1337,这个是用于区分来自Envoy代理和应用程序的流量的 uid。以 1337 uid/gid 运行的应用程序不会被捕获,因此会绕过代理。
Challenge 5 - Lateral Movement
View Policy:
让GPT帮解读下这道题:
个人理解,意思就是这个Admission Control准入控制策略,会根据准入请求时作出相应的处理,这个处理的策略就是根据题目给出的policy文件制定的。即当发送Admission准入请求去在名为sensitive-ns
的命名空间中创建或更新pod时,Admission Controller准入控制服务器就会往该Pod的容器里注入一个名为FLAG
的环境变量。整个请求的流程如下图如下图:
另外,通过GPT的解读,我可以知道Admission Webhook通常包括MutatingAdmissionWebhook,而/mutate
这个Endpoint就是用来处理Mutating Admission 请求的,可以在pod创建或更新时进行相应处理。
依旧使用dnscan
工具进行内部服务发现:
经测试发现只有 kyverno-svc.kyverno.svc.cluster.local
是开放web端口(443)并且存在/mutate
这个Endpoint的。
问题是请求数据如何构造,k8s官方文档关于 Dynamic Admission Control 并没有写具体怎么构造。
于是问GPT,试了很多它给的json数据,发了请求结果https://kvverno-svc.kyverno.svc.cluster.local/mutate
返回curl: (92) HTTP/2 stream 0 was not closed cleanly: INTERNAL_ERROR (err 2)
,搞的我以为我的解题方向错了,郁闷卡了挺久... 后来还是题目给的提示信息里知道 kube-review 可以实现yaml到Admission request请求数据的转换。
先在本地写好创建pod的yaml文件:
apiVersion: v1
kind: Pod
metadata:name: nginxnamespace: sensitive-ns
spec:containers:- name: nginximage: nginx:latestports:- containerPort: 80
然后使用开源工具 kube-review 创建准入请求所需的json请求数据:
kube-review create pod.yaml > pod.json
然后将pod.json
粘贴到pastebin,生成下载链接,就可以在CTF的shell环境下将pod.json
文件下载下来了:
接着发送/mutate
准入请求,响应数据里的patch,base64解码后可以看到就是策略里注入的FLAG:
综上,从这道题可以看到,Admission Controller可以随便让用户访问不是一件好事。
小结
学到了很多,其实云安全是个很有意思的领域,涉及新知识非常多,很有新鲜感.
另外,学习思路是一样的,你的知识面越广,对研究目标越熟悉,你能看到的攻击面越大。
通关后拿了个证书,当个纪念:
https://www.k8slanparty.com/certificate/lXIko99e
Reference
https://www.wiz.io/blog/k8s-lan-party-challenge
https://thegreycorner.com/2023/12/13/kubernetes-internal-service-discovery.html#kubernetes-dns-to-the-partial-rescue
https://gist.github.com/nirohfeld/c596898673ead369cb8992d97a1c764e
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/
https://github.com/istio/istio/wiki/Understanding-IPTables-snapshot#use-pid-to-get-iptables
https://github.com/istio/istio/issues/4286
https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#request
https://github.com/anderseknert/kube-review