Google.org点击劫持DOM XSS

摘要

谷歌有一个项目相信许多人可能都没听过,那就是谷歌危机地图(Google Crisis Map)。

该项目旨在帮助人们可以快速的查找和使用一些关键的应急信息(来源)。

虽然它目前仍在使用,但它似乎使用的人并不多。

由于这是一个较老的项目(创建于2012年)且很长一段时间没有更新,因此可以说这是一个查找漏洞的绝佳目标。

它被托管在google.org域,该域的严重程度虽不如google.com(针对客户端漏洞),但它仍然是Google所拥有的域名。

登录

如果你打开项目的主页(google.org/crisismap),你将被重定向到默认地图“天气和事件”。这对我们来说意义不大,因为我们唯一能做的就是查看地图。

这里有一种可以管理和创建新地图的方法。如果我们在网址末尾添加.maps后缀,如:google.org/crisismap/.maps

打开此页面后,你需要使用自己的Google帐户登录才能继续。现在,你应该能够看到一个带有地图列表的仪表板。每个帐户都有三个默认地图。

出于某种原因,如果你在自己的域上发布其中的一个地图,则所有人都能在仪表板的“已发布地图”字段下看到该地图。

创建地图

如果单击红色的“Create Map”按钮,你会看到一条消息显示,gmail.com域不能用于创建新地图。

这意味着我们需要使用包含我们自定义域的电子邮件进行登录。我们可以通过使用GSuite帐户,或使用gmail.com以外域的电子邮件登录来执行该操作。之后,我们就可以创建一个新地图了。

单击“Continue”按钮后,我们将被重定向到我们可以编辑新创建地图的页面。

查找 XSS

首先,我们将向地图添加一个新图层。

将会弹出用于创建新图层的弹框。

输入任意内容作为“Title”。

现在,如果我们在“Source URL”字段中输入javascript:alert(document.domain),它将显示一个错误:

Invalid URL – please include a protocol (e.g. http:// or https://)

这意味着它会在允许你保存新图层之前检查URL是否有效。验证URL的反混淆javascript代码如下所示:

if (url && !url.toLowerCase().match("^\\s*(http://|https://|docs://|$)")) {
  showError("Invalid URL - please include a protocol (e.g. http:// or https://)");
}

但这只是在将实际保存请求发送到后端之前的客户端验证。

修改请求

我们可以使用像FiddlerBurp Suite这样的Web调试代理,来修改请求并发送修改后的版本。

首先,我们需要将“Source URL”更改为有效的URL,例如https://example.com

我们单击“OK”按钮并单击“Save”以发送保存请求。然后我们将修改请求。请求如下:

POST https://google.org/crisismap/.api/maps/1234
{
  "id": "1234",
  "title": "Untitled map",
  "base_map_type": "GOOGLE_ROADMAP",
  "layers": [{
    "id": "1",
    "title": "Test layer",
    "visibility": "DEFAULT_ON",
    "type": "KML",
    "source": {
      "kml": {
        "url": "https://example.com" }
    }
  }]
}

我们将用javascript:alert(document.domain)替换https://example.com,并发送修改后的请求。

测试 XSS

现在请求已发送并保存,因此让我们重新加载页面。

打开“Layers”,然后单击“Download KML”。

点击下载链接后,XSS将被触发,并弹出带有域的警告框!

如何修复

为什么会这样?URL验证仅发生在前端而不是后端。这意味着可以通过验证后端的URL来解决这个问题。

但谷歌并没有这么做。而是在URL保存到后端时检查URL,显示在DOM中前URL已被验证。

因此,如果URL无效,则不会将其用作链接,并将使用一个无意义的值,如:about:invalid代替。

<a href="about:invalid#zClosurez">Download KML</a>

影响

好的,现在我们有一个包含payload指向javascript: URI的链接。该链接位于用于管理地图的页面上。你必须登录才能获取该页面的访问权限。

显然这是self-XSS,因为只有我们能够执行该XSS。

现在,我们要做的就是如何将self-XSS变成真正的有影响的XSS?

增加严重性

我们创建的每张地图都可以发布供公众查看。如果你通过包含域example.com的电子邮件登录,则可以将地图发布到:http://google.org/crisismap/example.com/test

这样,任何人都可以打开此URL并查看我们创建的地图。想要使XSS正常工作,用户需要打开或导航到此页面,打开“Layers”并单击“Download KML”链接。

这意味着它已不再是self-XSS,但缺点是这需要用户操作的步骤过多。

点击劫持

如果我们查看HTTP响应头,可以看到google.org并未发送X-Frame-Options标头。

X-Frame-Options HTTP 响应头是用来给浏览器指示允许一个页面可否在<frame>, <iframe>, <embed> 或者 <object> 中展现的标记。站点可以通过确保网站没有被嵌入到别人的站点里面,从而避免clickjacking攻击。

google.org上缺少该标头,则意味着我们可以将已发布的地图嵌入到我们自己网站上的iframe中。

<iframe src="https://google.org/crisismap/example.com/test"></iframe>

如下所示。用户现在甚至不需要离开我们的站点。但仍需要用户点击iframe中的两个位置(“layers”>“download kml”)。

iframe在我们的网站上被加载 – 这意味着我们可以使用CSS和JavaScript来操作它。

我想到的第一件事,就是将黑色的DIV放置在我们希望用户点击的位置。然后检测单击事件并将DIV移动到第二个点。

这很有效,但仍需要用户点击两个不同的位置。

但有个更好的方案就是绝对定位iframe,这样用户就不必移动光标了。

以下演示我们将iframe缩放了50倍,并将其移动了到我们希望用户单击的位置。首先转到“Layers”选项卡。点击后,它会在带有payload的链路上移动。

你可以通过在下面的iframe中单击两次来尝试该示例:

总结

不要相信用户输入。在使用它之前一定要验证/转义它,甚至在保存它之前最好检查它是否有效。

通过正确设置X-Frame-Options标头,不允许其他域将你的网站嵌入iframe。

在查找漏洞时,请尝试找到该漏洞可能的最高严重性。

例如,如果你找到一个XSS,请尝试通过查找错误配置的Cookie或端点接管帐户。

寻找仍然适合bug奖励计划范围的旧项目。我在谷歌危机地图上发现了另外两个漏洞,后续我也会发布有关它们的文章。

时间线

2018.12.09:报告漏洞

2018.12.10:优先权改为P1

2018.12.10:验证

2018.12.10:确认

2018.12.11:发放奖励

目前评论:0 条

发表评论