# I Built a TV That Plays All of Your Private YouTube Videos

YouTube TV Lounge API /bind endpoint lacked CSRF protection, letting a malicious page play a victim's private Uploads playlist on an attacker-controlled TV and leak all private/unlisted video IDs and ctt tokens.

- Payout: $6,000
- Severity: high
- Program: Google
- Credited: David Schütz
- Disclosed: 2021-04-05
- Write-up quality: 9/10
- Original: https://bugs.xdavidhu.me/google/2021/04/05/i-built-a-tv-that-plays-all-of-your-private-youtube-videos/
- Library page: https://bugbountylibrary.com/r/blog%3Abugs-xdavidhu-me-google-2021-04-05-i-built-a-tv-that-plays-all-of-your-private-youtube-videos
- Tags: domain:web impact:data-leak impact:pii-leak kind:bounty platform:blog sev:high tech:rest technique:chained technique:source-review vuln:access-control vuln:csrf vuln:info-disclosure

**Target:** YouTube TV Lounge API (/bind, /pairing endpoints)
**Root cause:** The Lounge API /bind endpoint authenticated solely via cookies with no CSRF token, so cross-site POSTs could issue play commands as the victim. The TV then received a ctt token granting access to the specific private video.
**Steps:**
1. Set User-Agent to contain `Cobalt` to load https://www.youtube.com/tv and act as a TV
2. Request screen_id from `/pairing/generate_screen_id`, then lounge_token from `/pairing/get_lounge_token_batch` (skip PIN pairing)
3. Poll `/bind` with the lounge_token to receive play events
4. Craft malicious HTML form POSTing to `/bind` with lounge_token and `listId` set to victim's Uploads playlist (channel ID with second char C->U)
5. Victim visits page; CSRF fires; attacker TV's /bind receives play event listing all video IDs including private/unlisted
6. For each private videoId, repeat CSRF play with `videoId`, capture ctt from play event, then GET `/get_video_info?...&vtt=<ctt>` to retrieve title and direct video URL
**How found:** Recalled a years-old observation that a TV could play a private video without login; reverse-engineered the Android TV APK to find it loads youtube.com/tv (Cobalt WebView); spoofed User-Agent to become a TV; inspected Burp logs to isolate the single /bind POST that triggered playback; noticed no CSRF token among the many parameters and progressively stripped params until only lounge_token remained, then confirmed with an HTML form CSRF PoC.
**Exploitation:** Attacker hosts a page hardcoding victim's channel ID; victim's browser sends CSRF POST to /bind playing the victim's Uploads playlist on attacker's fake TV. Attacker's TV polls /bind, harvests all video IDs, filters private/unlisted via Data API, then iteratively CSRF-plays each private video to steal its ctt token and fetch the raw video URL.
**Escalation:** Chained CSRF with the special Uploads playlist trick (C->U channel ID) to enumerate private video IDs, then per-video ctt theft to bypass the per-video token limitation, enabling mass exfiltration of all private/unlisted videos.
**Insight:** When a feature uses long-lived bearer-like tokens (lounge_token) in request bodies rather than headers, test whether the endpoint accepts cookie-only auth with no CSRF token; also look for special owner-only playlist IDs (Uploads, C->U) that enumerate private content when played as the owner.
