逆向工程谷歌
谷歌Colaboratory被全世界的数据科学家称为“Colab”,是一个免费的云木星笔记本平台。除了为用户提供运行Python和R笔记本的环境外,Colab还慷慨地允许用户共享对有限数量的gpu和tpu的免费访问。
Colab已经迅速成为实际的环境在木星笔记本中进行编码。然而,要利用Colab的计算能力处理木星笔记本以外的任何东西是非常困难的。机器学习工程师想要生产他们的模型,让他们走出笔记本阶段,这是一个特别相关的问题;笔记本虽然非常适合探索,但与更高级的MLOps工具(将训练过程编纂成正式的管道)不太匹配。
几天前我就处在这个位置上,我决定不围绕Colab的限制改变我的工作流程,而是围绕我的工作流程改变Colab !
因此,今天我们将一窥谷歌Colab的内部结构,并发现我们如何可以稍微改变Colab的规则。需要明确的是,我们在这里所做的一切都不会对Colab或该服务的用户造成任何伤害,我们只是在幕后探索。
在窗帘后面
Colab的秘密秘诀在于它的后端:谷歌服务器提供基础设施,让您只需打个响指,按一下按钮就可以运行代码。因此,我们的第一步是分析后端API。要做到这一点,最简单的方法是检查Colab在正常运行期间进行的API调用。为此,我们启动Chrome的开发者工具,找到网络选项卡,然后尝试运行一段代码。DevTools开始记录Colab的每个请求,我们几乎立刻就发现了一些有趣的东西。


它看起来像这样的URL (/桶/ m / < id > / socket . io
)是运行在远程机器上的Jupyter套接字的代理。
如果启动Files窗格(该窗格显示/内容
从Colab UI的左窗格,我们得到了另一个有趣的请求:


这一次,响应体是JSON,枚举远程主机上的文件。看来URL (/桶/ m / < id > / api /内容/
)指向提供文件元数据的服务。

双击Files窗格中的文件,Colab将下载并显示该文件。如果我们试着点击/内容/ sample_data / README.md
,我们注意到一个请求/桶/ m / < id > /文件/
它返回该文件的内容。


很明显,https://colab.research.google.com/tun/m/ < id > /
是运行Colab实例的服务器的反向代理,它提供了/ socket . io
,/文件
,/ api /内容
端点。
让我们试着看看这些服务是否在Colab容器实例本身中运行。的lsof
程序安装在Colab内部,所以我们运行lsof -iTCP stcp:听
列出正在侦听TCP端口上的网络请求的所有进程。

啊哈!colab-fileshim
,节点
,jupyter-notebook
这些表面看起来都很值得探索。最佳电子竞技即时竞猜平台。因为我们已经使用了Files窗格,所以让我们看看colab-fileshim
第一。它有PID 28,所以我们检查/ proc
文件系统查看进程的完整命令行:

下一步是调查/usr/local/bin/colab-fileshim.py
.具有讽刺意味的是,我们可以通过在“文件”窗格本身中浏览它来做到这一点。该程序看起来就像一个无趣的文件服务器,除了服务器本身的响应外,我们从它中学不到太多东西localhost: 3453 /文件
(包含实际的文件内容)和localhost: 3453 / api /内容
(JSON元数据)。这意味着Colab将这些请求从隧道URL转发到实例本身的3453端口。
在Chrome DevTools的Network选项卡中,我们可以右键单击一个请求来复制cURL命令来重新生成它。例如,下面是查看README的cURL命令。md文件:
$卷发' https://colab.research.google.com/tun/m/m-s-3oy94z70yrj59/files/content/sample_data/README.md?authuser=0 ' \ - h的权威:colab.research.google.com \ - h”x-colab-tunnel:谷歌“\ - h”接受 : */*' \ - H的dnt: 1 \ - H的接收语言:en - us, en; q = 0.9 \ - H的sec-fetch-site:同源\ - H ' sec-fetch-mode:歌珥\ - H的sec-fetch-dest:空\ - H的推荐人:https://colab.research.google.com/ \ - H的饼干:< < READACTED > >“\ - H的范围:字节= 0 - 930 \——压缩
如果在本地计算机的终端上运行此命令,则会将该README文件的内容打印到我们的终端。通过一些尝试和错误,我们看到我们可以删减大部分的头文件,只留下
$ curl 'https://colab.research.google.com/tun/m/m-s-3oy94z70yrj59/files/content/sample_data/README.md?authuser=0' \ -H 'x-colab-tunnel:谷歌' \ -H 'cookie: << readwritten >>'
的x-colab-tunnel
头的存在是为了防止我们(或邪恶的攻击者)从普通的浏览器选项卡发出这些请求,表面上是为了阻止XSS攻击。的饼干
头向谷歌提供身份验证,以证明我们已经访问了notebook实例。因为cookie很长,处理起来很麻烦,所以我们将把它存储到shell变量中COLAB_COOKIE美元
本文的其余部分。
$ COLAB_COOKIE="<<先前修订的值>>" #使用:$ curl…- h“饼干:$ COLAB_COOKIE”
胜利1:暴露我们自己的服务器
现在我们已经发现了Colab的反向代理,让我们看看是否可以使用它来隧道我们自己的请求!
而不是试图打乱现有的colab-fileshim.py
服务器,我们可以简单地用我们自己的服务器替换这个进程!我们运行pkill - f colab-fileshim
杀死它,这样我们就可以在相同的端口上启动自己的服务器。
作为一个简短而简单的演示,我们将启动python的默认HTTP服务器来提供我们自己的文件localhost: 3453 /文件
.

瞧!我们现在可以更改cURL命令来下载我们自己的文件!
$ curl 'https://colab.research.google.com/tun/m/m-s-3oy94z70yrj59/files/message.txt?authuser=0' \ -H "x-colab-tunnel:谷歌" -H "cookie: $COLAB_COOKIE"嗨!这是我们自己的文件服务器!
$ curl 'https://colab.research.google.com/tun/m/m-s-3oy94z70yrj59/files/shadow?authuser=0' \ -H "x-colab-tunnel:谷歌" -H "cookie: $COLAB_COOKIE" root:*:18585:0:99999:7::: daemon:*:18585:0:99999:7::: bin:*:18585:0:99999:7::: sys:*:18585:0:99999:7:: sync:*:18585:0:99999:7::: #…
注意Colab单元格中的日志行,证明我们的服务器处理了请求:
在0.0.0.0端口3453 (http://0.0.0.0:3453/)上服务HTTP…172.28.0.1 - - [22/Jun/2022 16:43:10] "GET /files/message.txt HTTP/1.1" 200 - 172.28.0.1 - - [22/Jun/2022 16:43:16] "GET /files/shadow HTTP/1.1" 200 -
不幸的是,因为要求x-colab-tunnel:谷歌
头,我们无法从浏览器访问服务器。
进一步侦查
让我们继续研究,这次看看我们之前发现的另一个有趣的过程,节点
.如果我们检查/proc/7/cmdline
,我们看到进程正在运行/ datalab / web / app.js
.
一旦我们跳到那里读代码,我们就能找到/ datalab /网络
包含一个相当标准的NodeJS应用程序。和前面看到的一样/ socketio /
路由,它也暴露了一个/ _proxy /{港口}/
路线。啊哈!这应该能让我们进入任何URL从任何Colab实例上的端口!
让我们启动一个快速服务器并进行测试。

$ curl 'https://colab.research.google.com/tun/m/m-s-3oy94z70yrj59/_proxy/1234/some/path?authuser=0' \ -H "x-colab-tunnel:谷歌" -H "cookie: $COLAB_COOKIE" Colab转发服务器!身体负责人< /名称> < / > < > < h1 >从Colab你好!< / h1 > < h2 >路径= /一些/路径< / h2 > < /身体> < / html > %
如果我们能从浏览器选项卡查看这个HTML页面就好了。不幸的是,科拉布拒绝代理任何请求,除非他们有x-colab-tunnel:谷歌
标题设置。如果我们试图从浏览器访问这些url,我们会遇到一个通用的HTTP 400客户端错误页面:

胜利2:暴露整个网页
幸运的是,我们可以使用一个Chrome扩展插入HTTP头到浏览器请求实时运行.我们设置它发送x-colab-tunnel:谷歌
所有请求的头:

然后我们可以在浏览器中启动隧道url !

来月亮行星Jupyter !
最后,让我们看看第三个也是最后一个有趣的过程,jupyter-notebook
,它在端口上监听9000
.
首先,我们可以尝试使用前面提到的代理和报头技巧从浏览器访问端口/桶/ m / < id > / _proxy / 9000
.不幸的是,我们遇到的是HTTP 500服务器错误页面,而不是理想的Jupyter UI。
奇怪。我们试着通过跑步来诊断! curl - localhost: 9000
从Colab笔记本本身,但我们仍然得到一个错误消息:

的输出lsof
之前的故事给了我们一个提示与其继续听0.0.0.0
/::
(所有接口上的所有IP), Jupyter只监听给Colab实例的私有IP。这大概是为了避免木星的界面暴露给我们。
谷歌当然没有尽力隐藏它,而且有一个快速修复方法。
为了绕过侦听地址限制,我们需要创建一个在所有接口和IP上侦听的进程,并将它收到的所有流量转发到Jupyter正在侦听的特定IP地址。我们可以安装套接字代理工具socat
(“插座猫”)这样做。我们将使用socat
转发来自localhost: 9000
来主机名:9000美元
并返回:

这是一个开始!如果我们在浏览器中重新加载URL,我们会看到Jupyter UI的片段,但它显然已经损坏了。

这是因为Jupyter期望在域的根(URL路径)处被访问/
),但我们的科拉布隧道有一条/桶/ m / < id > / _proxy / 9000
这样一来,CSS和JS文件等资源的绝对路径就会被打乱。
这里没有简单的解决方案——我们需要一个完整的(子)域来将流量转发到我们的木星服务器。
胜利3:揭露木星UI
值得庆幸的是,Colab确实有一个隐藏得很好但官方的端口转发解决方案,它确实提供了整个子域!有趣的是,它隐藏得非常好,我花了比发现内部反向代理更长的时间才找到它!
要了解如何使用Colab的官方端口转发,您需要从左边栏打开Code Snippets选项卡,并找到Output Handling代码段。点击“查看源笔记本”,你就会被带到advanced_outputs.ipynb
,Colab的强大用户片段花园展示了稻草人记录的平台功能.我们需要的具体代码片段可以在标题“浏览到内核上执行的服务器”下找到。
我们可以使用这个代码片段将Jupyter UI公开为子域。

现在,我们可以单击链接(并添加)/树
到URL来安抚Jupyter),并看到完整的工作* Jupyter UI!

嗯,几乎完全工作。谷歌似乎已经将官方代理限制为只允许GET请求,从而允许我们查看但不运行笔记本。
总结
恭喜你,你坚持到了最后!我希望这篇文章对向您展示您所不知道的关于Colab如何工作的事情是有价值的,同时也帮助您学习了一般逆向工程工具的半结构化方法。我也希望这能激励您更深入地了解您每天使用的工具和产品的内部结构!
如果您是一名数据科学家,希望将您的组织的数据和机器学习过程提升到一个新的水平,那么就去看看吧DagsHub.我们的协作平台允许您开发可复制的数据管道,跟踪模型实验,并在整个机器学习生命周期中收集训练数据、注释和训练模型。
DagsHub:人们建立数据科学项目的地方。