除了谷歌地图之外,可能很少有人知道谷歌的在线防灾地图(Google Crisis Map),它创建于2012年,Web架构更新缓慢,网站访问量相对较少。而作者就是通过在这个“老旧”的地图服务中,发现了XSS和依托其服务的google.org点击劫持漏洞。该篇Writeup也算是在“犄角旮旯”角落里发现漏洞的典型,我们一起来看看。
谷歌防灾地图创建于2012年,目的在于帮助人们发现和预警重要的灾害活动,网站访问量较少,它托管于谷歌旗下域名google.org,从客户漏洞角度来说,虽然没有google.com那么引人注目,但好歹也是属于谷歌域名。
我们可以通过https://google.org/crisismap主页进行浏览,其中右上角显示出“天气,灾害,应急准备”,此时,我们能做的仅只是简单浏览这个防灾地图。
经测试,我们可以在其URL后面添加一个.maps来创建自己的地图,也就是https://google.org/crisismap/.maps打开该链接之后,就会以谷歌账号登录进入,其中可以看到三幅默认地图,这三幅地图对任意用户都可见,如下:
点击Published Map下的地图名称即可查看相应地图。
来到左上角,点击 “Create Map” 按钮之后,会跳出以下的提示框告知Gmail账号不能创建地图,只有个人或企业定制域名身份才具备地图创建权限:
也就是说,我们需要以谷歌关联的个人或组织机构邮箱身份才能创建地图,这里,可以通过GSuite账户或其它非gmail.com后缀邮箱登录即可。之后,创建地图开始,点击下图Continue之后就行:
在创建地图的过程中,点击'Add layer'我们可以向其中添加新的图层(layer),之后,会跳出图层对话框,其中包含了图层标题、描述、属性、图例、缩放坐标、来源URL(Source URL)等填写项。
当我们简单填写了图层标题,选择了图层类型,并在来源URL(Source URL)中填入javascript:alert(document.domain)的XSS Payload,提交保存后,它会反应出错提示:
Invalid URL – please include a protocol (e.g. http:// or https://)
这看似是在图层保存之前,后端会检查其Source URL的合法性,其检查逻辑可能如下:
if (url && !url.toLowerCase().match("^\\s*(http://|https://|docs://|$)")) { showError("Invalid URL - please include a protocol (e.g. http:// or https://)");}
更关键的是,该合法性验证貌似是请求提交到后端服务前,对客户端的唯一一项验证措施。
在此,我们可以用BurpSuite代理工具来对请求抓包分析,并对请求进行相应更改提交给后端服务。首先,我们可以把代表Source URL的值进行一个替换,这里我们把它替换成https://example.com,然后点击OK并保存,在此过程中的请求如下:
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" } } }]}
在此,我们把其中代表Source URL的https://example.com替换成之前的XSS Payload - javascript:alert(document.domain),然后转发请求。请求竟然可以成功被提交保存!之后,我们打开图层选项,点击下载按钮“Download KML”,就能完美触发XSS Payload:
这个XSS漏洞原因是什么呢?原来那个Source URL合法性验证只会发生在前端( frontend),而与谷歌防灾地图的数据库进行交互的后端(backend)却没有该URL验证。其漏洞的危害就是,任意用户可以创建地图并公开发布,比如我们以后缀example.com的邮箱进行创建地图并发布,那么该地图的URL就是:
http://google.org/crisismap/example.com/test
那么,任何查看下载该地图的用户,由于其中存在 javascript: URI 的XSS Payload,点击相应的“Download KML”下载按钮之后,就会成功触发XSS Payload,当然,有效的漏洞利用还需要更多深入的构造。
在与后端交互过程中,如果我们查看一下响应中的HTTP消息头,发现google.org并没有要求X-Frame-Options设置。X-Frame-Options 的HTTP 响应头是用来给浏览器指示允许一个页面可否在<frame>、<iframe>、<embed> 或者 <object> 中展现的标记。应用该设置的站点可以通过确保网站没有被嵌入到别人的站点里面,从而避免 clickjacking 攻击。
那么,也就是说,我们可以把上述创建发布的地图以iframe方式嵌入到我们控制的网站中去,又能触发XSS,也能证明Clickjacking,如下:
<iframe src="https://google.org/crisismap/example.com/test"></iframe>
但受害者还需点击“Layers” > “Download KML“才能触发XSS。所以,我又想到用背景为黑色的DIV标签来把iframe伪装成一个点击链接,不好的是,还是需要两次点击才能触发,如下:
这是<点击我>一个POC demo,它把iframe缩放了50倍,并将其移动到我们希望受害者用户单击的位置。两次连续点击之后,会出现我创建发布的地图,大家可以试试。
1、任何用户输入点都值得怀疑。对厂商来说,需要在保存提交数据之前进行一些必要的验证措施; 2、考虑Clickjacking时,可检查X-Frame-Options ; 3、寻找漏洞时,尽量去实现最坏的漏洞危害,或综合利用; 4、尽量在测试范围内的一些“老旧”系统中多做挖掘。
2018.9.12 漏洞上报 2018.10.12 漏洞被分类为P1级别 2018.10.12 谷歌深入调查并给予肯定(Nice Catch) 2018.11.12 得到一笔赏金