基于DOM的XSS

在本节中,我们将描述基于DOM的跨站脚本(DOM型XSS),讲解如何发现DOM型XSS漏洞,并讨论如何利用不同的源和接收器来利用DOM型XSS。

什么是基于DOM的跨站脚本?

基于DOM的XSS漏洞通常发生在JavaScript从攻击者可控制的源(如URL)获取数据,并将其传递到支持动态代码执行的接收器(如eval()innerHTML)时。这样,攻击者就可以执行恶意JavaScript,通常能够劫持其他用户的账户。

要实施基于DOM的XSS攻击,你需要将数据放入源中,使其传播到接收器并导致任意JavaScript的执行。

对于DOM型XSS,最常见的源是URL,通常通过window.location对象访问。攻击者可以通过在URL的查询字符串和片段部分中嵌入有效载荷来构造一个链接,将受害者引导到一个存在漏洞的页面。在某些情况下,比如在针对404页面或运行PHP的网站时,有效载荷也可以放置在路径中。

有关源与接收器之间污染流的详细解释,请参阅基于DOM的漏洞

如何测试基于DOM的跨站脚本

大多数DOM型XSS漏洞可以通过Burp Suite的Web漏洞扫描器快速可靠地找到。手动测试基于DOM的跨站脚本,通常需要使用带有开发者工具的浏览器,如Chrome。你需要逐个检查每个可用的源,并逐个进行测试。

测试HTML接收器

要测试HTML接收器中的DOM型XSS,可以将一个随机的字母数字字符串放入源(如location.search),然后使用开发者工具检查HTML并找到字符串出现的位置。请注意,浏览器的查看源代码选项不适用于DOM型XSS测试,因为它未考虑JavaScript对HTML所做的更改。在Chrome的开发者工具中,你可以使用Control+F(或MacOS上的Command+F)在DOM中搜索字符串。

对于字符串出现在DOM中的每个位置,都需要确定上下文。基于这个上下文,改进你的输入以查看它是如何被处理的。例如,如果你的字符串出现在一个双引号属性中,那么尝试在字符串中注入双引号,看看是否可以跳出该属性。

请注意,浏览器对URL编码的处理方式各不相同,Chrome、Firefox和Safari会对location.searchlocation.hash进行URL编码,而IE11和Microsoft Edge(Chromium之前的版本)不会对这些源进行URL编码。如果你的数据在处理之前就被URL编码,那么XSS攻击不太可能奏效。

测试JavaScript执行接收器

针对基于DOM的XSS进行JavaScript执行接收器测试要稍微困难一些。对于这些接收器,你的输入不一定会出现在DOM中,因此无法对其进行搜索。相反,你需要使用JavaScript调试器来确定输入是否以及如何被发送到接收器。

对于每个潜在的源,如location,首先需要在页面的JavaScript代码中找到源被引用的地方。在Chrome的开发者工具中,你可以使用Control+Shift+F(或MacOS上的Command+Alt+F)搜索页面上的所有JavaScript代码以找到源。

一旦找到源被读取的地方,就可以使用JavaScript调试器添加一个断点,并跟踪源的值如何被使用。可能你会发现源被分配给了其他变量。如果是这种情况,就需要再次使用搜索功能来跟踪这些变量,看看它们是否被传递给了接收器。当找到一个接收来自源的数据的接收器时,你可以使用调试器检查变量的值,方法是将鼠标悬停在变量上,显示其被发送到接收器之前的值。然后,与HTML接收器一样,改进你的输入,看看是否可以成功实施XSS攻击。

使用DOM Invader测试DOM型XSS

在实际环境中识别和利用DOM型XSS可能是一个繁琐的过程,通常需要手动遍历复杂的、经过精简的JavaScript。不过,如果使用Burp的浏览器,就可以利用其内置的DOM Invader扩展,它能为你完成大部分繁重的工作。

阅读更多

利用不同源和接收器进行DOM型XSS利用

原则上,如果存在一条可执行路径,数据可以从源传播到接收器,那么网站就容易受到基于DOM的跨站脚本攻击。在实践中,不同的源和接收器具有不同的属性和行为,这可能会影响可利用性,并决定需要采取什么技术。此外,网站的脚本可能会对数据进行验证或其他处理,在尝试利用漏洞时必须考虑这些处理。有多种与基于DOM的漏洞相关的接收器。详情请参阅这个列表

document.write接收器可与script元素配合使用,因此你可以使用一个简单的有效载荷,如下所示:

document.write('... <script>alert(document.domain)</script> ...');

LAB

在使用location.search源的document.write接收器中的DOM型XSS

但请注意,在某些情况下,写入document.write的内容可能会包含一些周围的上下文,在利用时需要考虑到这些上下文。例如,在使用JavaScript有效载荷之前,可能需要关闭一些现有元素。

LAB

在select元素内使用location.search源的document.write接收器中的DOM型XSS

innerHTML接收器在任何现代浏览器上都不接受script元素,也不会触发svg onload事件。意味着你需要使用imgiframe等替代元素。onloadonerror等事件处理器可以与这些元素结合使用。例如:

element.innerHTML='... <img src=1 onerror=alert(document.domain)> ...'

LAB

在使用location.search源的innerHTML接收器中的DOM型XSS

第三方依赖中的源和接收器

现代Web应用程序通常使用大量的第三方库和框架构建,这些库和框架通常为开发人员提供额外的功能和能力。重要的是要记住,其中一些也可能是DOM型XSS的潜在源和接收器。

jQuery中的DOM型XSS

如果使用的是jQuery之类的JavaScript库,则要留意是否有可以更改页面上DOM元素的接收器。比如,jQuery的attr()函数可以更改DOM元素的属性。如果数据是从用户控制的源(如URL)读取的,然后传递给attr()函数,那么就有可能操纵发送的值,从而导致XSS。例如,下面的JavaScript,它使用URL中的数据更改了锚元素的href属性:

$(function() {
	$('#backLink').attr("href",(new URLSearchParams(window.location.search)).get('returnUrl'));
});

你可以通过修改URL,使location.search源包含恶意JavaScript URL来利用这个漏洞。当页面的JavaScript将此恶意URL应用到返回链接的href后,点击返回链接将执行该URL:

?returnUrl=javascript:alert(document.domain)

LAB

在使用location.search源的jQuery锚href属性接收器中的DOM型XSS

另一个需要留意的潜在接收器是jQuery的$()选择器函数,它可以用于向DOM注入恶意对象。

jQuery曾经非常流行,一个典型的DOM型XSS漏洞就是由于网站将该选择器与location.hash源结合使用,以实现动画或自动滚动到页面上特定的元素而造成的。这种行为通常是使用一个易受攻击的hashchange事件处理器实现的,类似于以下:

$(window).on('hashchange', function() {
	var element = $(location.hash);
	element[0].scrollIntoView();
});

由于hash是用户可控的,攻击者可以利用它将XSS载体注入到$()选择器接收器中。较新版本的jQuery已修补了这一特定漏洞,当输入以哈希字符(#)开头时,将无法向选择器中注入HTML。不过,你仍然可以在实际环境中发现易受攻击的代码。

要真正利用这一经典漏洞,你需要找到一种方法,在不需要用户交互的情况下触发hashchange事件。其中一个最简单的方法就是通过iframe传递你的利用:

<iframe src="https://vulnerable-website.com#" onload="this.src+='<img src=1 onerror=alert(1)>'">

在这个例子中,src属性指向一个带有空哈希值的易受攻击的页面。当iframe加载时,XSS载体会被添加到哈希值中,从而导致hashchange事件触发。

注意

即使是较新版本的 jQuery,只要你对来自源的输入具有完全的控制,并且该源无需#前缀,那么它仍然可能通过$()选择器接收器而易受到攻击。

LAB

在使用hashchange事件的jQuery选择器接收器中的DOM型XSS

AngularJS中的DOM型XSS

如果使用像AngularJS这样的框架,则有可能在没有角括号或事件的情况下执行JavaScript。当一个网站在HTML元素上使用ng-app属性时,AngularJS将对其进行处理。在这种情况下,AngularJS将在双花括号内执行JavaScript,双花括号可以直接出现在HTML中,也可以出现在属性内。

LAB

带有角括号和双引号HTML编码的AngularJS表达式中的DOM型XSS

DOM型XSS结合反射和存储数据

一些纯粹基于DOM的漏洞在单个页面内自包含。如果脚本从URL读取一些数据并将其写入到一个危险的接收器,那么漏洞就完全是客户端的。

不过,源并不仅限于浏览器直接暴露的数据,它们也可以来自网站。例如,网站通常会在服务器的HTML响应中反射URL参数。这通常与普通XSS有关,但也可能导致反射型DOM XSS漏洞。

在反射型DOM XSS漏洞中,服务器会处理来自请求的数据,并将数据回显到响应中。反射的数据可能被放置在JavaScript字符串字面值中,或者在DOM的数据项中,如表单字段。然后页面上的某个脚本会以不安全的方式处理反射的数据,最终将其写入到一个危险的接收器。

eval('var data = "reflected string"');

LAB

反射型DOM XSS

网站可能也会将数据存储在服务器上,并在其他地方反射它。在一个存储型DOM XSS漏洞中,服务器从一个请求接收数据,存储它,然后在后续的响应中包含这些数据。后续响应中的某个脚本包含一个接收器,然后该接收器以不安全的方式处理数据。

element.innerHTML = comment.author

LAB

存储型DOM XSS

哪些接收器可能导致DOM型XSS漏洞?

以下是一些可能导致DOM型XSS漏洞的主要接收器:

document.write()
document.writeln()
document.domain
element.innerHTML
element.outerHTML
element.insertAdjacentHTML
element.onevent

以下jQuery函数也是可能导致DOM型XSS漏洞的接收器:

add()
after()
append()
animate()
insertAfter()
insertBefore()
before()
html()
prepend()
replaceAll()
replaceWith()
wrap()
wrapInner()
wrapAll()
has()
constructor()
init()
index()
jQuery.parseHTML()
$.parseHTML()

如何防范DOM型XSS漏洞

除了在基于DOM的漏洞中描述的通用措施外,还应避免允许来自任何不受信任的源的数据动态写入到HTML文档。

Last updated