- Cloud Security Club
- Posts
- Solving Thunder-CTF: Level 05
Solving Thunder-CTF: Level 05
Have you checked my writeup for previous levels? If not, here are the links:
Starting level-5 of Thunder-CTF:
python3 thunder.py create thunder/a5power
First, we will check the permissions associated with the service account
python scripts/test-permissions.py start/a5-access.json
This service account has many permissions for the cloud function service—listing, describing, updating, etc.
We will now list the cloud functions:
gcloud functions list
Since the cloud function is active, we will describe it
gcloud functions describe a5-func-366435738976
In this description, we can see the URL of the HTTPS trigger. We will try to trigger this cloud function using the given URL:
We can see that it’s giving a 403 forbidden error, saying the client doesn’t have permission. We have to pass the authorization token with the request.
Let’s try hitting the function with our service account’s identity token
gcloud auth print-identity-token
The output is just “Hello World!“. Not of much use. We’ll investigate further.
We have another URL that contains the source code, but since this user doesn’t have the cloudfunctions.functions.sourceCodeGet
, we can’t get the source code. But it has the permission to set the source code cloudfunctions.functions.sourceCodeSet
.
We’ll use that permission to modify the source code of the cloud function.
Cloud functions have a runtime service account attached. This service account’s permissions can differ from those of the identities deploying or invoking the function.
Let’s modify the cloud function’s code to return the access token when invoked. We will add a Python script that returns the cloud function’s access code when invoked from an HTTP URL.
mkdir code_modif
nano code_modif/main.py
nano code_modif/requirements.txt
Using the nano editor, we have added the code to get the access token corresponding to the cloud function
In requirements.txt
, we added the requests library as a dependency and deploy.
Now we will deploy the modified code
gcloud functions deploy a5-func-366435738976 --source=./code_modif/ --trigger-http
We updated our cloud function with custom code. Now, we can send requests to the HTTP URL to invoke the function, which will run the updated code.
Again, call the cloud function using the HTTPS URL by passing the service account’s identity token with it.
We can see the cloud function’s access token. Now, we will check for its permission, which we got as the output.
These are the permissions related to the cloud function, which differs from the service account’s permissions.
Now we can see that this cloud function’s access token has permission for iam.roles.get
, iam.roles.list
, and iam.roles.update
. We will use these permissions to grant our service account access to the private storage bucket. (Remember, the challenge description told us the secret is in a private storage bucket?)
We can get the list of IAM policies by making a POST request to this URL https://cloudresourcemanager.googleapis.com/v1/projects/{resource}:getIamPolicy
, by setting the cloud function’s access token as the bearer token.
https://cloudresourcemanager.googleapis.com/v1/projects/{resource}:getIamPolicy
We can see a list of IAM policies associated with the project cloudsecurityclub-dev
. We’ll check the policies for the given service account and try modifying it to access the storage bucket. (.list
and .get
permission)
The following role is associated with our service account.
We can check the service account name by running this command:
gcloud auth list
We can see the service account is [email protected]
We can bind new roles to a service account with a POST request, but we need a PATCH request to modify an existing role instead.
So, we’ll use the PATCH request to update the roles associated with the service account.
PATCH request on this URL:
https://iam.googleapis.com/v1/projects/cloudsecurityclub-dev/roles/a5_access_role_366435738976?updateMask=includedPermissions
We received the 200 OK status code, so the service account’s permissions are updated.
Now, we will try to list the private buckets
gsutil ls
We can now see the bucket and will list the contents of it
gsutil ls gs://a5-bucket-366435738976/
We will copy the contents of the bucket in the root directory.
gsutil cp -r gs://a5-bucket-366435738976 .
We can see secrets.txt
there, which is what we wanted to find.
Let’s look at the contents of this file:
cat secret.txt
And we finally got our secret value.
This marks the end of Thunder-CTF’s level-5.
If you want to read more about GCP misconfigurations, check out Thunder CTF Level 6 write-up!
Reply