# The Embedded YouTube Player Told Me What You Were Watching (and more)

Embedded YouTube player, logged in as victim, let attacker load special playlists (HL/WL/LL/UU) and read contents via getPlaylist(), leaking watch history, likes, and unlisted video IDs.

- Payout: $1,337
- Severity: medium
- Program: Google
- Credited: David Schütz
- Disclosed: 2021-01-18
- Write-up quality: 8/10
- Original: https://bugs.xdavidhu.me/google/2021/01/18/the-embedded-youtube-player-told-me-what-you-were-watching-and-more/
- Library page: https://bugbountylibrary.com/r/blog%3Abugs-xdavidhu-me-google-2021-01-18-the-embedded-youtube-player-told-me-what-you-were-watching-and-more
- Tags: domain:web impact:data-leak impact:pii-leak kind:bounty platform:blog sev:medium tech:rest technique:recon technique:source-review vuln:access-control vuln:info-disclosure

**Target:** YouTube IFrame Player API / embedded player postMessage
**Root cause:** The embedded player, running with the victim's authenticated session, would load special/private playlists (HL, WL, Liked, Uploads) and expose their contents to the embedding page via player.getPlaylist() and videoData postMessages, without restricting cross-origin access to owner-only data.
**Steps:**
1. Embed a YouTube IFrame player on attacker page and load playlist `HL` (watch history) or `WL` (watch later) via `player.loadPlaylist('HL')`
2. Call `player.getPlaylist()` to read the array of video IDs in the loaded playlist
3. For targeted attacks, derive Liked playlist ID by replacing first 3 chars of victim channel ID with `LLD`/`LLB`, and Uploads with `UUD`/`UUB`
4. Load those playlist IDs in the player and call `getPlaylist()` to enumerate liked and unlisted/uploaded video IDs
5. For a known private video ID, embed it and read `player.getVideoData()` to leak title and caption languages
**How found:** Prior exploration of YouTube's special playlists (HL, WL, Liked, Uploads) and separately the embedded player's postMessage API. Realized the embedded player is authenticated as the viewer, so loading an owner-only playlist in it and calling getPlaylist() would return contents the attacker normally cannot see.
**Exploitation:** Attacker hosts a page embedding the YouTube player; victim visits while logged into YouTube. The player authenticates as the victim, loads owner-only playlists, and the attacker's JS reads the video ID list via getPlaylist() postMessage responses, exfiltrating watch history, watch later, likes, and unlisted video IDs (which are the only secret protecting unlisted videos).
**Escalation:** Chained playlist enumeration with channel-ID-derived playlist IDs to target arbitrary victims' liked/uploaded videos; unlisted video IDs then allow watching otherwise-secret videos.
**Insight:** Cross-origin embedded widgets that inherit the user's session can be abused as a confused deputy: if the widget exposes data via postMessage (getPlaylist/getVideoData), any embedding page can read owner-only resources by loading them into the widget. Enumerate special/reserved resource IDs (HL, WL, derived channel IDs) and test them through the embed rather than the main site.
