2018年9月26日,开源Closure库(最初由谷歌创建并用于谷歌搜索)的一名开发人员创建了一个提交,删除了部分输入过滤。据推测,这是因为开发人员在用户界面设计方面出现了问题。但此次提交的开发人员以及审核人员并未发现这一举动将会导致XSS。
2019年2月,安全研究员Masato Kinugawa发现了这个漏洞,并将其报告给了Google。Google立即做出反应,并在2019年2月22日修复了漏洞,撤销了之前9月份做的修改。另一位安全专家LiveOverflow详细描述了如何导致XSS。
Closure库中的漏洞非常难以检测。它依赖于一种很少使用的称为突变XSS的技术。变异XSS漏洞是由浏览器解释HTML标准的方式不同引起的。
由于浏览器的不同,很难清理服务器上的用户输入。服务器需要考虑不仅浏览器之间以及它们的版本之间的所有差异。对XSS进行清理输入的最有效方法是通过让浏览器解释输入而不实际执行它来实现。
有一个很好的客户端库用于XSS清理:DOMPurify。Closure也使用这个库。但是,DOMPurify并非万无一失。在极少数情况下,需要额外的消毒。确切地说,2018年9月随着Closure的更新而删除了额外的消毒。
DOMPurify使用该template
元素清理用户输入。浏览器以不同方式处理元素的innerHtml
属性和div
元素的相同属性template
。在div
元素的情况下,在innerHtml
分配值之后立即执行。对于template
元素,您可以在执行前应用清理。
DOMPurify背后的想法是获取用户输入,将其分配给元素的innerHtml
属性template
,让浏览器解释它(但不执行它),然后对潜在的XSS进行清理。然而,正是这种解释背后的逻辑是XSS突变的根本原因。
要了解浏览器如何解释无效HTML,请创建仅包含以下内容的HTML文档:
<div><script title="</div>">
当您在浏览器中打开它时,您将看到代码解释如下:
<html><head></head><body><div><script title="</div>"></script></div></body></html>
现在,尝试使用以下内容创建HTML文档:
<script><div title="</script>">
浏览器以不同方式解释此代码:
<html><head><script><div title="</script></head><body>"></body></html>
这种差异是由于当浏览器遇到<script>
标记时,它会从HTML解析器切换到JavaScript解析器,直到找到结束标记。
在大多数情况下,浏览器将始终以与环境无关的相同方式解释同一文档。但是,有一种情况是由于某些客户端情况:noscript
标记,此行为可能会有所不同。
HTML规范声明noscript
必须根据浏览器中是否启用JavaScript 来对标记进行不同的解释。浏览器行为的这种差异正是Masato Kinugawa用于他的XSS概念验证攻击的原因。事实证明,无效的HTML代码在分配给元素的innerHtml
属性时template
(如果禁用了JavaScript)的解释方式不同,并且在分配给元素的innerHtml
属性时div
(就像启用了JavaScript一样)也有所不同。
Masato Kinugawa使用以下有效负载来执行概念验证攻击:
<noscript><p title="</noscript><img src=x onerror=alert(1)>">
如果禁用JavaScript(对于template
DOMPurify用于XSS清理的元素),浏览器将按以下方式解释有效内容:
<noscript><p title="</noscript><img src=x onerror=alert(1)>"></p></noscript>
这"</noscript><img src=x onerror=alert(1)>"
是title
参数的值。因此,DOMPurify不会清理输入内容,因为没有JavaScript,因此没有XSS风险。
但是,如果启用了JavaScript(对于div
浏览器使用的元素),浏览器将按以下方式解释有效内容:
<noscript><p title="</noscript>
<img src="x" onerror="alert(1)">
"">
"
该noscript
元件端部的早期和img
元件被完全解释,包括的JavaScript内容onerror
属性。
如果之前有人发现此漏洞并且是否将其用于任何恶意目的,则无法说明。由于Closure库也用于其他Google产品,因此此漏洞可能会影响Gmail,地图,文档和其他服务。