# Cookie Tossing to RCE on Google Cloud JupyterLab

Cookie tossing on notebooks.googleusercontent.com plus Tornado's URL _xsrf acceptance bypassed CSRF, letting an attacker install a malicious JupyterLab extension and achieve RCE.

- Payout: $3,134
- Severity: high
- Program: Google
- Credited: s1r1us
- Disclosed: 2020-12-23
- Write-up quality: 8/10
- Original: https://blog.s1r1us.ninja/research/cookie-tossing-to-rce-on-google-cloud-jupyter-notebooks
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-s1r1us-ninja-research-cookie-tossing-to-rce-on-google-cloud-jupyter-notebooks
- Tags: domain:web impact:privesc impact:rce kind:bounty platform:blog sev:high tech:python technique:chained technique:source-review vuln:csrf vuln:rce

**Target:** JupyterLab notebook instance on Google Cloud AI Hub (random-id.notebooks.googleusercontent.com)
**Root cause:** Tornado accepts the _xsrf token from the request URL/body in addition to the X-XSRFToken header, and _xsrf is a cookie that can be tossed from a sibling subdomain, so the CSRF check can be satisfied cross-site.
**Steps:**
1. Edit /opt/conda/share/jupyter/lab/static index.html on own notebook VM to get self-XSS on own subdomain.
2. From attacker notebook subdomain, set `_xsrf=1` cookie scoped to notebooks.googleusercontent.com (cookie tossing).
3. Craft cross-site POST to victim notebook with `_xsrf=1` in the request body/URL instead of the X-XSRFToken header, bypassing preflight.
4. POST a JSON body installing a malicious extension from npm (`@mohansrk/test`).
5. Extension opens a WebSocket to the terminal endpoint and runs arbitrary code in the victim instance.
**How found:** Started from self-XSS by editing JupyterLab static files, then examined cookies and noticed _xsrf; realized cookie tossing could overwrite it, and reading Tornado source revealed _xsrf is also accepted from the request URL/body, removing the header preflight barrier.
**Exploitation:** Attacker hosts a malicious page/notebook that tosses the _xsrf cookie onto the parent domain, then submits a cross-site form POST with matching _xsrf in the body to the victim's notebook to install an npm extension; the extension connects to the terminal WebSocket and executes commands, yielding RCE on a VM with Editor role.
**Escalation:** RCE on the notebook VM grants Editor role access to much of Google Cloud.
**Insight:** When a CSRF token is also accepted from the request body/URL (not just a custom header), cookie tossing from a sibling subdomain can fully satisfy the check; read the framework's token-extraction code, not just the docs.
