- Cloud Security Club
- Posts
- Solving Thunder-CTF: Level 04
Solving Thunder-CTF: Level 04
Have you checked my writeup for previous levels? If not, here are the links:
Let’s get started with level 4:
python3 thunder.py create thunder/a4error
gcloud auth activate-service-account --key-file=start/a4-access.json
We will first look into the permissions of the service account related to the level-4 challenge.
python scripts/test-permissions.py start/a4-access.json
The service account has diverse permissions for the cloud logging, cloud functions, and compute instances.
We’ll explore them one by one.
Firstly, we’ll find the running compute instance.
gcloud compute instances list
Next, we’ll describe this instance as the user has “compute.instances.get” permission, which will help us analyze it better.
gcloud compute instances describe a4-instance --zone-us-west1-b
We can’t find any potential leads from this information, but we will try to find the clues to get into the compute instance
Next, we’ll explore cloud functions:
gcloud functions list
We can see a cloud function running, but the service account doesn’t have permission to describe this cloud function (as you can see, cloudfunctions.functions.get
permission is missing)
However, one thing to note is that it has an HTTP trigger, so we can invoke this cloud function using HTTP requests.
We must form the HTTP URL using the following URL format to invoke the cloud function.
https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
The permissions of our service account a4-access didn’t have the permission cloudfunctions.functions.invoke
. So, it can’t invoke all cloud functions in the account. However, there’s a slight possibility that this service account was explicitly granted the invoke permission from the function level (with the help of cloud function level IAM Policy Bindings).
Let’s try to invoke the cloud function by passing our service account’s identity token. (We can generate it using the command: gcloud auth print-identity-token
)
I am passing this newly generated identity token to ensure authentication.
It worked. The cloud function has explicitly granted invoke access to our service account. Since this function takes another argument, the file, we’ll try to provide a dummy value for the argument and curl the URL again.
https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME?file=hello2.txt
It gave the error because there was no file like hello2.txt
in the cloud function.
NOTE: The cloud function has an underlying logging functionality that notes all the errors and log entries by default. We will explore this to get more information about the error and check if it leads to another clue to proceed further.
Also, as said, this specific user has a lot of permissions associated with the cloud logging service. We will explore them now.
We see a bearer token is present in the logs. This token differs from the identity token we passed to the cloud function.
Let’s try to find out how much access this hardcoded token has.
We will again run the test_permission.py
script with the hardcoded token from logs to get the permissions associated with this access token.
python scripts/test-permissions.py <bearer_token>
We can see that this access token has the permission to compute.instances.setMetadata
. Our main aim was to get access to the compute instance running so that we could search for the secret inside the instance.
We will update the compute instance metadata to get into the VM. We can set the ssh_key parameter in the metadata of the compute instance so that we can try to access it by logging into it using SSH
Firstly, we will generate temporary SSH keys using the command:
ssh-keygen -t rsa -b 4096
Then set the key name to ssh_key_level4
It will generate private and public keys, respectively
We can see public and private keys got created:
Now, we will add the public key of this generated SSH key to the metadata of the compute instance
To add the custom metadata to the compute instance, we need to input the fingerprint value of the instance also (which we can refer to the compute instance details when we describe it).
NOTE: The fingerprint value is also short-lived. If it’s giving an error, check the fresh fingerprint value by rerunning the same command for gcloud compute instances.
We will set the metadata by sending a POST request to the setMetadata
URL. The following post body contains the public SSH key and the corresponding fingerprint value.
NOTE: We pass the access token we got from the logs in the authorization header.
Now, let’s make the POST request with the authorization token.
https://www.googleapis.com/compute/v1/projects/<your_project_id>/zones/<enter_the_zone>/instances/<name_of_instance>/setMetadata
It gives a 200 OK response, which indicates that the SSH public key we passed in the URL is successfully added to the metadata – granting us SSH access.
NOTE: One thing to note is that the authentication access token we get from the log is short-lived. So, if you cannot use the access token or get an error with it, generate it again by using the curl command on the cloud function URL (by providing a wrong/non-existent filename) and use the newly generated authentication token.
Now, we can log in to the compute instance over SSH using the private SSH key.
ssh -i <key> username@<external_ip_address>
Once logged in to the compute instance, we will navigate through the instance and get the secret value.
We found the secret value.
This marks the end of Thunder-CTF’s level-4.
If you want to read more about GCP misconfigurations, check out Thunder CTF Level 5 write-up!
Reply