原文:
https://bugs.xdavidhu.me/google/2021/04/05/i-built-a-tv-that-plays-all-of-your-private-youtube-videos/
原文太长了,有删减
很久以前我就对YouTube for Android TV App 感兴趣,现在我有了一个LG智能电视,于是我下载了,准备研究一番。你觉得他是一个复杂的app嘛,不是的,其实他就是一个网页。就是个Webviwe加载了https://www.youtube.com/tv,这个webview叫做Cobalt。我们可以在浏览器里打开这个网页

但是我不想被重定向。我只想看Youtube TV。
我测试了一波,把UA从firefox修改为Cobalt就不跳了。
// change ‘Firefox’ to ‘Cobalt’ in the User-Agent
“User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0”
->
“User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Cobalt/87.0”

我想测试一下“remote-control”是如何工作的。通过这个功能,你可以通过浏览器Youtube来控制你的电视。要连接到电视,你需要在电视上先生成一个TV code,然后在应用里添加这个TV code。我们先生成这个TV code

然后通过浏览器在https://youtube.com/pair添加这个TV code

链接完成后,如果你播放一个视频,右下角就会出现一个”Play on TV”的小图标。点击的话,视频就会在电视上播放。

即使是私人视频也可以播放。
这个内部的提供远程控制的API称之为Lounge API。整个配对流程
1.电视从/pairing/generate_screen_id请求一个screen_id
2.使用screen_id,电视从/pairing/get_lounge_token_batch请求lounge_token
3.使用lounge_token,电视从/pairing/get_pairing_code请求配对PIN.
4.电视展示配对PIN。
此后,用户必须在其设备上输入配对PIN。使用PIN,用户的设备将调用/pairing/get_screen,如果用户输入了正确的PIN,则Lounge APIlounge_token也会为用户返回。此后,配对结束。用户现在可以使用lounge_token刚刚获得的电视来控制电视。
在开始配对过程之后,电视会切换到“轮询”模式,这在Google来说是很常见的事情。Google通常使用这些bind请求,而不是WebSocket,这些请求基本上是HTTP请求,如果没有新事件,则等待很长时间,但如果有新事件,则立即返回。电视一遍又一遍地调用/bind接口。
即使您是Web开发人员,此HTTP轮询对您来说似乎也很奇怪。这是一个例子:

为了播放私有视频,它使用了一个额外的token 叫做ctt。
用户请求播放私人视频时,电视从/bind端点接收的事件在ctt旁边包括一个额外的参数videoId。播放视频时,电视随后会从/get_video_info端点请求原始视频URL,并将ctt令牌作为GET命名参数包含vtt(出于某种原因)。没有ctt令牌,电视将无法观看私人视频。
现在你明白了,远程控制是如何工作的。让我们看看漏洞到底在哪里。
在检查这个API的时候,我发现向/bind接口post了大量的参数,而且90%的参数都和播放视频没有什么关系。在尝试弄懂这些参数的时候,我发现参数里好像少了csrf_token….
然后我就继续删除不必要的参数,到最后发现只有lounge_token和私人视频时相关联的。
然后我就写了一个小demo,点击提交按钮,私人视频在我的电视上播放了。/bind接口真的没有csrf保护。
这意味着如果受害者访问我们的网站,我们可以以受害者的名义播放视频!我们只需要在POST请求中指定lounge_token以及要播放的视频的ID,然后以受害者的名义从我们的恶意网站发送请求即可。
由于我们以受害者的名义提出播放请求,因此,如果我们指定要播放受害者的私有视频,则目标的电视lounge_token将ctt为其获得令牌,这将使电视可以访问私有视频…
下面是一个视频播放的请求:

如果你觉得你不知道其中的一些参数,别担心,我也不知道。
可是我们如何知道受害者以前使用过哪些电视?我们如何获得lounge_token?你可能会想这没有办法被利用。
事实上,可以被利用。因为我们可以自己制造电视。
想象一下攻击场景:
我们自己制造一台电视,获取lounge_token,然后让受害者在我们的电视上播放私人视频。此后,我们用电视轮询/bind接口,等待新的播放事件,当我们获取到它时,我们还将获取ctt受害者的私有视频,以便我们观看!不错!
要利用此漏洞,我们不必使用实际的YouTube TV网站,就足以从中提取要点,并构建一个行为类似于“准系统电视”的脚本。这样它就可以lounge_token自己生成一个,并轮询/bind接口以查找新的播放事件。
如果您正在等待“pin pairing”步骤,则实际上我们可以完全跳过这些步骤,只需使用lounge_token初始/pairing/get_lounge_token_batch请求返回的电视即可。
但是我们如何知道受害者的私人视频ID?
这里的神奇之处在于,我们不仅可以播放视频,而且甚至可以使用此易受攻击的请求在电视上播放播放列表。通过改变videoId到listID的POST要求,我们可以指定一个播放列表到电视上播放,而不是视频:

当在POST请求中指定这样的播放列表时,电视将从Lounge API中获得播放事件,其中包含给定播放列表包含的视频ID列表。
但是,我们应该以受害者的名义播放哪个播放列表?
在我以前的YouTube文章中,我谈到了特殊 Uploads播放列表。这是特殊的,因为如果频道所有者观点,即播放列表,他/她可以看到她所有的Public,Unlisted并Private在它的视频。但是,如果其他用户查看相同的播放列表,则只能看到其中的Public视频。
Uploads对于给定的频道ID,可以轻松找到此特殊播放列表的ID:
// just have to change the second char of the channel ID
// from ‘C’ -> `U` to get the ID of the special “Uploads” playlist
“UCBvX9uEO0a3fZNCK12MAgug” -> ”UUBvX9uEO0a3fZNCK12MAgug”
该频道ID对于每个YouTube频道都是公开的,大多数情况下,只需浏览至YouTube上该频道的页面并检查URL,即可找到该频道ID:
所以,如果使用我们的CSRF脆弱的POST要求,我们扮演受害者的特殊 Uploads播放列表中受害人的名字我们的恶意电视上,我们的恶意电视将得到所有的受害者中Public,Unlisted和Private视频的ID!
而且,我们已经知道,ctt只要知道ID,我们就可以窃取该私有视频。
因此,总而言之,我们可以通过执行以下简单步骤来制作一个POC,从受害者那里窃取几乎所有内容:
- 通过对受害者的频道ID进行硬编码,为受害者专门设置一个恶意页面,并让受害者在浏览器中将其打开
- 通过我们的恶意页面,让受害者Uploads在我们邪恶的电视上播放她的播放列表
- 使用我们的邪恶电视,收听播放事件并记下受害者的视频ID(包括Unlisted&Private视频ID)
- 使用邪恶的电视,告诉我们的恶意页面Private在电视上逐一播放所有视频ID,这样我们就可以ctt从电视获得的播放事件中窃取所有的
- 完美利用。
攻击流程图

译者按:
这个利用有点复杂。我们捋一捋。作者先是分析了浏览器TV和电视的配对过程。然后发现,可以通过浏览器控制电视播放私有视频。然后作者在分析如何播放私有视频的时候,发现没有csrf参数,然后又通过播放视频列表来绕过不知道视频ID的问题。中间还夹杂着很多细节的处理,比如修改UA头绕过重定向,一点点分析private视频和哪个参数有关。太细致了。
本文迁移自知识星球“火线Zone”