CVE-2018-5175:Firefox中的通用CSP strict-dynamic绕过
在本篇博客中,我将介绍一个在Firefox 60中修复的CSP strict-dynamic绕过漏洞。
https://www.mozilla.org/en-US/security/advisories/mfsa2018-11/#CVE-2018-5175
该漏洞允许绕过使用"strict-dynamic"脚本源策略的网站的内容安全策略(CSP)保护。如果目标网站存在HTML注入漏洞,攻击者可以注入对Firefox开发者工具中require.js库的引用,然后利用该库的已知技术绕过CSP限制执行注入脚本。
什么是"strict-dynamic"?
也许你应该阅读CSP规范:https://www.w3.org/TR/CSP3/#strict-dynamic-usage
但为了练习英语写作,我将解释strict-dynamic。如果你已经了解strict-dynamic,可以跳过本节。
众所周知的CSP通过白名单域名来限制资源加载。例如,以下CSP设置仅允许从自身源和trusted.example.com加载JavaScript:
|
|
由于此CSP,即使页面存在XSS漏洞,也能防止执行来自内联脚本或evil.example.org的JavaScript文件。这看起来足够安全,但如果trusted.example.com有任何绕过CSP的脚本,仍然可能执行JavaScript。更具体地说,如果trusted.example.com有JSONP端点,可能会这样被绕过:
|
|
如果此端点将传递给callback参数的用户输入直接反射到回调函数名,则可以用作任意脚本:
|
|
此外,已知AngularJS也可用于绕过CSP。如果允许托管许多JavaScript文件的域(如CDN),这种绕过可能性变得更加现实。
这样,在白名单中,有时难以安全地操作CSP。为解决此问题,设计了strict-dynamic。以下是使用示例:
|
|
此CSP意味着将禁用白名单,仅加载具有nonce属性中"secret"字符串的脚本。
|
|
A.js可能希望加载和使用另一个JavaScript。为允许此操作,CSP规范允许在具有正确nonce的js在特定条件下加载另一个js时,无需适当的nonce属性。用规范中的话来说,允许执行非"parser-inserted"脚本元素。
以下是允许执行的JavaScript类型的具体示例:
|
|
使用createElement()加载时,它是非"parser-inserted"脚本元素,允许加载。而使用document.write()加载时,它是"parser-inserted"脚本元素,不会被加载。
到此,我大致解释了strict-dynamic。
顺便说一句,strict-dynamic在某些情况下可被绕过。接下来,我将介绍一个已知的strict-dynamic绕过方法。
已知的strict-dynamic绕过
已知如果目标页面使用特定库,strict-dynamic也可被绕过。
由Google的Sebastian Lekies、Eduardo Vela Nava和Krzysztof Kotowicz研究,受影响库列表在此: https://github.com/google/security-research-pocs/blob/master/script-gadgets/bypasses.md
让我们看看此列表中require.js的strict-dynamic绕过。
假设目标页面使用带strict-dynamic的CSP,加载require.js并有简单XSS。在这种情况下,如果插入以下脚本元素,攻击者可以在没有适当nonce的情况下执行任意JavaScript。
|
|
当require.js找到带有data-main属性的脚本元素时,它会从等效于以下代码加载data-main属性中指定的脚本:
|
|
如前所述,strict-dynamic允许通过createElement()加载JavaScript而无需适当的nonce。
这样,在某些情况下,你可以利用已加载JavaScript代码的行为绕过CSP strict-dynamic。
Firefox的漏洞就是由require.js的这种行为引起的。在下一节中,我将解释该漏洞。
通用strict-dynamic绕过(CVE-2018-5175)
Firefox使用传统扩展实现某些浏览器功能。传统扩展指的是基于XUL/XPCOM的扩展,在Firefox 57中移除,而非WebExtensions。即使在最新的Firefox 60中,浏览器内部仍然使用此机制。
在此绕过中,我们使用浏览器内部使用的传统扩展资源。在WebExtensions中,通过在清单中设置web_accessible_resources键,列出的资源可从任何网页访问。传统扩展具有名为contentaccessible标志的类似选项。在此绕过中,由于浏览器内部资源的require.js因contentaccessible=yes标志可从任何网页访问,因此可用于绕过CSP。
让我们查看清单。如果你在Windows上使用64位Firefox,可以从以下URL查看清单:
|
|
|
|
黄色部分使文件可从任何网站访问。这两行用于创建resource: URI。第一行的resource devtools devtools/modules/devtools/将devtools/modules/devtools/目录(存在于jar:file:///C:/Program%20Files%20(x86)/Mozilla%20Firefox/browser/omni.ja!/chrome/devtools/modules/devtools/)映射到resource://devtools/。
我们现在可以通过在Firefox中打开resource://devtools/来访问该目录下的文件。同样,下一行映射到resource://devtools-client-jsonview/。此URL通过contentaccessible=yes标志变得可 web访问,我们现在可以从任何网页加载此目录下的文件。
此目录有一个require.js,可用于绕过CSP。只需将此require.js加载到使用CSP strict-dynamic的页面,即可绕过strict-dynamic。
实际绕过如下:
https://vulnerabledoma.in/fx_csp_bypass_strict-dynamic.html
|
|
从此代码中,data: URL将作为JavaScript资源加载,并弹出警报对话框。
你可能会想,“嗯,为什么require.js被加载?它应该被CSP阻止,因为脚本元素没有适当的nonce。”
实际上,无论你设置多么严格的CSP规则,扩展的web可访问资源都会忽略CSP加载。此行为在CSP规范中提到:
https://www.w3.orgTR/CSP3/#extensions
对资源强制实施的策略不应干扰用户代理功能(如插件、扩展或书签)的操作。这些功能通常将用户的优先级置于页面作者之上,如[HTML-DESIGN]中所阐述。
Firefox的resource: URI也有此规则。因此,用户可以在设置CSP的页面上按预期使用扩展功能,但另一方面,此特权有时可用于绕过CSP,如此错误的情况。
当然,此问题不仅限于浏览器内部资源。即使在一般浏览器扩展上,如果存在可用于绕过CSP的web可访问资源,也会发生同样的情况。
似乎Firefox团队通过将页面的CSP应用于resource: URI修复了此错误。
文章结尾
我写了关于Firefox的CSP strict-dynamic绕过漏洞。
仅供参考,我在寻找我制作的Cure53 CNY XSS Challenge 2018第三级的另一种解决方案时发现了此问题。在此挑战中,我使用了另一种技巧来绕过strict-dynamic。如果你感兴趣,请查看。
此外,我创建了此XSS挑战的不同版本,仍在等待你的答案:)
最后,我要感谢Google的研究让我注意到此错误。谢谢!
发布者:Masato Kinugawa
时间:上午2:02
标签:CSP, Firefox, 安全, XSS