谷歌有一个项目相信许多人可能都没听过,那就是谷歌危机地图(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”按钮后,我们将被重定向到我们可以编辑新创建地图的页面。
首先,我们将向地图添加一个新图层。
将会弹出用于创建新图层的弹框。
输入任意内容作为“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://)");
}
但这只是在将实际保存请求发送到后端之前的客户端验证。
我们可以使用像Fiddler或Burp 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,并发送修改后的请求。
现在请求已发送并保存,因此让我们重新加载页面。
打开“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:发放奖励
*参考来源:appio,FB小编secist编译,转载请注明来自FreeBuf.COM