客户端模板注入
翻译
原文:https://portswigger.net/web-security/cross-site-scripting/contexts/client-side-template-injection
- name: 翻译
desc: 原文:https://portswigger.net/web-security/cross-site-scripting/contexts/client-side-template-injection
bgColor: '#F0DFB1'
textColor: 'green'
2
3
4
# 客户端模板注入
在本节中,我们将介绍客户端模板注入漏洞,以及如何利用它进行 XSS 攻击。这种攻击技术是由我们的研究团队开创的 - 阅读更多没有 HTML 的 XSS:使用 AngularJS 进行客户端模板注入 (opens new window)。尽管客户端模板注入是一个通用问题,但我们将重点关注 AngularJS 框架中的示例,因为这是最常见的。我们将描述如何制作从 AngularJS 沙箱中逃逸的漏洞利用,以及如何使用 AngularJS 的潜在功能绕过内容安全策略(CSP) (opens new window)。
# 1什么是客户端模板注入?
当应用程序使用了客户端模板框架,并在网页中动态嵌入用户输入时,就会出现客户端模板注入漏洞。当渲染一个页面时,框架会扫描页面内容,以查找模板表达式,并执行它遇到的任何表达式。攻击者可以提供恶意的模板表达式,从而发起跨站脚本(XSS) (opens new window)攻击。
# 2什么是AngularJS沙箱?
AngularJS 沙箱是一种机制,可以防止 AngularJS 模板表达式访问潜在的危险对象,例如window
或document
。它还可以防止 访问潜在的危险属性,例如__proto__
。尽管 AngularJS 团队不认为它是一个安全隐患,但更广泛的开发者社区通常不这么认为。虽然 “沙箱绕过” 最初具有挑战性,但安全研究人员已经发现了许多绕过方法。因此,该特性最终在 AngularJS 1.6 版本中被删除。但是,许多遗留应用程序仍然使用旧版本的 AngularJS,因此可能容易受到攻击。
# 3AngularJS沙箱是如何工作的?
沙箱的工作原理是解析表达式,重写 JavaScript,然后使用各种函数来测试重写的代码 是否包含任何危险对象。例如,ensureSafeObject()
函数检查给定对象是否引用自身,这是检测window
对象的一种方法。构造函数Function
的检测方式大致相同,即检查构造函数属性是否引用自身。
ensureSafeMemberName()
函数检查对象的每个属性访问,如果它包含危险属性(如__proto__
或__lookupGetter__
),则该对象将被阻止。ensureSafeFunction()
函数可防止调用call()
、apply()
、bind()
或constructor()
。
你可以通过访问这个fiddle (opens new window)并在angular.js
文件的第 13275 行设置断点来查看沙箱的运行情况。变量fnString
包含了你的重写代码,你可以查看 AngularJS 是如何转换它的。
# 4AngularJS沙箱逃逸是如何实现的?
沙箱逃逸涉及欺骗沙箱,使其认为恶意表达式是良性的。最著名的逃逸——在表达式中全局使用修改后的charAt()
函数:
'a'.constructor.prototype.charAt=[].join
当它最初被发现时,AngularJS 并没有阻止这种修改。该攻击的工作原理是使用[].join
方法覆盖该函数,这导致charAt()
函数会返回发送给它的所有字符,而不是特定的单个字符。由于 AngularJS 中isIdent()
函数的逻辑,该函数会将它认为是单个字符的内容 与 多个字符进行比较。由于单个字符始终小于多个字符,因此isIdent()
函数总是返回true
,如以下示例所示:
isIdent = function(ch) {
return ('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || '_' === ch || ch === '$');
}
isIdent('x9=9a9l9e9r9t9(919)')
2
3
4
一旦isIdent()
函数被愚弄,你就可以注入恶意的 JavaScript。例如,允许使用$eval('x=alert(1)')
之类的表达式,因为 AngularJS 将每个字符都视为标识符。请注意,我们需要使用 AngularJS 的$eval()
函数,因为覆盖charAt()
函数只有在 沙箱代码执行后才会生效。然后,此技术将绕过沙箱,并允许任意 JavaScript 执行。PortSwigger Research曾多次全面破解 AngularJS 沙箱 (opens new window)。
# 4.1构建高级AngularJS沙箱绕过
至此,你已经了解了基本的沙箱逃逸工作原理,但你可能会遇到 对允许使用的字符进行更严格限制 的网站。例如,网站可能会阻止你使用双引号或单引号。在这种情况下,你需要使用String.fromCharCode()
等函数来生成字符。尽管 AngularJS 阻止访问表达式中的String
构造函数,但你可以通过改用字符串的 constructor 属性来解决此问题。但这显然需要一个字符串,想要构造这样的攻击,你需要找到一种不使用单引号或双引号来创建字符串的方法。
在标准的沙箱逃逸中,你将使用$eval()
来执行 JavaScript 有效负载,但在下面的实验室中,$eval()
函数是未定义的。幸运的是,我们可以改用orderBy
过滤器。orderBy
过滤器的典型语法如下:
[123]|orderBy:'Some string'
请注意,运算符|
的含义与 JavaScript 中的含义不同。通常,这是一个按位OR
操作,但在 AngularJS 中,它表示过滤操作。在上面的代码中,我们将左侧的数组[123]
发送到右侧的orderBy
过滤器。冒号表示要发送到过滤器中的参数,在本例中为一个字符串。orderBy
过滤器通常用于对象排序操作,但它也接受表达式,这意味着我们可以使用它来传递有效负载。
你现在应该拥有了完成下一个实验所需的所有工具。
- name: 实验室-专家
desc: 客户端模板注入-反射型XSS-无字符串的AngularJS沙箱逃逸 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/cross-site-scripting/contexts/client-side-template-injection/lab-angular-sandbox-escape-without-strings
bgColor: '#001350'
textColor: '#d112fe'
2
3
4
5
6
# 5AngularJS CSP绕过是如何实现的?
内容安全策略(CSP)的绕过方式 与 标准的沙箱逃逸类似,但其通常涉及一些 HTML 注入。当 CSP 模式在 AngularJS 中处于活动状态时,它会以不同的方式解析模板表达式,并避免使用Function
构造函数。这意味着上述的标准沙箱逃逸将不再有效。
根据特定策略,CSP 将阻止 JavaScript 事件。但是,AngularJS 定义了自己的事件,可以使用这些事件 来代替原有的默认事件。在事件中,AngularJS 定义了一个特殊的$event
对象,该对象只是引用了浏览器事件对象。可以使用此对象执行 CSP 绕过。在 Chrome 上,$event/event
对象上有一个名为path
的特殊属性。此属性包含导致事件执行的对象数组。最后一个属性始终是window
对象,我们可以使用它来执行沙箱逃逸。通过将这个数组传递给orderBy
过滤器,我们可以枚举数组并使用最后一个元素(也就是window
对象)来执行全局函数,例如alert()
。以下代码对此进行了演示:
<input autofocus ng-focus="$event.path|orderBy:'[].constructor.from([1],alert)'">
请注意,这里使用了from()
函数,它允许你将对象转换为数组,并在该数组的每个元素上调用给定函数(在第二个参数中指定)。在本例中,我们调用alert()
函数。但我们不能直接调用函数,因为 AngularJS 沙箱会解析代码并检测window
对象是否被用来调用函数。相反,使用from()
函数可以有效地将window
对象隐藏在沙箱中,从而允许我们注入恶意代码。
PortSwigger Research运用该技术创建了一个使用 AngularJS 的 CSP 旁路,长度为 56 个字符。 (opens new window)
# 5.1在AngularJS沙箱逃逸中绕过CSP
下一个实验采用了长度限制,因此上面的向量将不起作用。为了利用实验室,你需要考虑各种方法,来隐藏 AngularJS 沙箱中的窗口对象。一种方法是使用array.map()
函数,如下所示:
[1].map(alert)
map()
接受一个函数作为参数,并为数组中的每一项调用该函数。这将绕过沙箱,因为此处对alert()
函数的调用,是在没有显式引用window
对象的情况下实现的。要解决实验室问题,请在不触发 AngularJS window
检测的情况下,尝试各种执行alert()
的方法。
- name: 实验室-专家
desc: 客户端模板注入-反射型XSS-AngularJS沙箱逃逸中的CSP >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/cross-site-scripting/contexts/client-side-template-injection/lab-angular-sandbox-escape-and-csp
bgColor: '#001350'
textColor: '#d112fe'
2
3
4
5
6
# 6如何防范客户端模板注入漏洞
为防范客户端模板注入漏洞,请避免使用 不受信任的用户输入 来生成模板或表达式。如果这不可行,请考虑 在将模板表达式语法嵌入到客户端模板之前,从用户输入中过滤掉模板表达式语法。
请注意,HTML 编码不足以防范客户端模板注入攻击,因为框架在查找和执行模板表达式之前,会对相关内容执行 HTML 解码。