CVE-2020-11022/CVE-2020-11023:jQuery 3.5.0安全修复细节
jQuery 3.5.0于上月发布。此版本包含了我报告的两个被标记为“安全修复”的漏洞。
相关链接:
本文将详细解释这些漏洞的技术细节。
问题概述
具有以下特性的应用程序会受到影响:
- 允许用户编写任意HTML(但经过清理)
- 使用jQuery动态追加清理后的HTML
以下代码是一个示例:
|
|
在这种情况下,如果清理操作执行得当,通常不会发生XSS攻击,因为它只是追加了清理后的安全HTML。然而,实际上,.html()
方法在内部执行了特殊的字符串处理,导致了XSS。这就是本文要解释的问题。
PoC示例
存在多种变体,但我将展示三个基本的PoC。通常,以下HTML不会执行JavaScript:
PoC 1:
|
|
PoC 2:(仅影响jQuery 3.x)
|
|
PoC 3:
|
|
您可能认为存在带有onerror
属性的img
标签,但仔细检查会发现,它实际上位于属性或样式元素内部,因此不会执行。因此,即使这些HTML是由HTML清理器生成的清理后HTML,也完全不显得异常。
然而,在所有情况下,如果通过jQuery的.html()
方法追加HTML,JavaScript会意外执行。
您可以在以下位置测试每个PoC: https://vulnerabledoma.in/jquery_htmlPrefilter_xss.html
接下来,我将解释其原因。
CVE-2020-11023:根本原因(PoC 1和2)
PoC 1和2具有相同的根本原因。在.html()
方法内部,作为参数传递的HTML字符串会被传递给$.htmlPrefilter()
方法。htmlPrefilter
通过以下正则表达式执行处理,将自闭合标签(如<tagname />
)替换为<tagname ></tagname>
:
|
|
如果PoC 1的HTML经过此替换,输出将为:
|
|
黄色部分是替换后的字符串。由于此替换,style
元素内的<style />
被替换为<style ></style>
,结果导致之后的字符串从style
元素中跳出。之后,.html()
将替换后的HTML分配给innerHTML
。此时,<img ...>
字符串成为实际的img
标签,并触发onerror
事件。
顺便提一下,上述正则表达式在jQuery 3.x之前使用。从3.x开始,使用了稍作修改的另一个正则表达式:
|
|
此更改引入了另一个XSS向量,仅通过更基本的元素和属性即可导致XSS。PoC 2的向量就是由此更改引入的,它仅适用于jQuery 3.x。
|
|
在这种情况下,属性值上的<img ...>
字符串被跳出,导致XSS发生。
我解释了PoC 1和2的根本原因。jQuery团队是如何修复这个问题的呢?
修复方案(PoC 1和2)
jQuery团队通过将$.htmlPrefilter()
方法替换为恒等函数来修复此问题。因此,传递的HTML字符串不再被htmlPrefilter
函数修改。
然而,这并未解决所有XSS问题。在.html()
内部,还执行了另一个字符串处理,并引入了另一个问题(PoC 3)。
CVE-2020-11022:根本原因(PoC 3)
在.html()
内部,如果作为参数传递的HTML开头标签是特定标签之一,jQuery会尝试用另一个标签包装它一次,并进行下一步处理。这是因为由于HTML规范或浏览器错误,如果没有包装处理,某些标签会自动被移除。
option
元素就是此类元素之一——在MSIE9中,由于其错误,如果option
元素未用select
元素包装,则在分配给innerHTML
时会自动移除。
为了处理此问题,如果传递的HTML字符串的第一个元素是option
元素,jQuery会尝试用<select multiple='multiple'>
和</select>
包装整个传递的HTML字符串,包括该元素。
包装标签定义在: wrapMap.js
实际包装处理在: buildFragment.js
PoC 3的问题通过此包装处理发生。如果PoC 3的HTML经过此包装处理,HTML将变为:
|
|
当此HTML在jQuery内部代码中分配给innerHTML
时,JavaScript被执行。
脚本执行的原因在于<select>
标签的解析。<select>
不允许在其中放置除option
、optgroup
、script
和template
元素之外的HTML标签。由于此规范,插入的<style>
被忽略,<style>
内的</select>
成为实际select
元素的闭合标签,然后<select>
块在此处关闭。最终,接下来的<img ...>
从<style>
中跳出,onerror
事件触发→XSS。这就是根本原因。
修复方案(PoC 3)
jQuery团队通过仅对MSIE9应用包装处理来修复此问题。
MSIE9不易受此问题影响,因为MSIE9的<select>
解析有些特殊(是的,这是错误的)。因此,仅对MSIE9应用包装处理可以解决此问题。
供您参考,这些问题不仅存在于.html()
中,还存在于.append()
、$('<tag>')
等中。基本上,问题通过内部使用$.htmlPrefilter()
方法或包装处理的API发生。
更新建议
如果您的应用程序通过jQuery函数追加清理后的HTML,您应更新到3.5.0或更高版本。如果由于某种原因更新困难,我建议使用DOMPurify(XSS清理器)清理HTML。DOMPurify具有SAFE_FOR_JQUERY
选项,可以在考虑jQuery行为的情况下进行清理。您可以像这样使用它:
|
|
请注意,DOMPurify最近在SAFE_FOR_JQUERY
中存在绕过问题。请确保您使用2.0.8或更高版本。
结语
我从@PwnFunction的XSS挑战开始调查此问题: https://xss.pwnfunction.com/challenges/ww3/
实际上,其中一些漏洞是已知的,并且是该挑战的预期解决方案。(您可以在DOMPurify的变更日志中找到这一事实。至少早在2014年就已已知,并且DOMPurify自2014年起就具有SAFE_FOR_JQUERY
选项。)
以该挑战为触发点,我开始重新阅读jQuery的源代码,并注意到了另一个未公开提及的向量(PoC 2)。由于此向量仅允许通过简单的元素和属性进行XSS,我认为许多应用程序都存在漏洞。当我实际调查时,立即发现了一些易受攻击的应用程序。我向受影响应用程序的开发人员报告了此问题,同时我认为此问题应由jQuery方面修复,因此决定向jQuery团队报告。jQuery团队迅速解决了这些问题,即使他们必须进行破坏性更改。感谢jQuery团队。同时,感谢@PwnFunction,XSS挑战的创建者,给了我调查此问题的机会。
就是这样。我希望本文有助于保护您的Web应用程序或发现错误。