# 基于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的漏洞](/wsa/client-side/dom-based.md)。

## 如何测试基于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.search`和`location.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 Invader文档](https://portswigger.net/burp/documentation/desktop/tools/dom-invader)

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

原则上，如果存在一条可执行路径，数据可以从源传播到接收器，那么网站就容易受到基于DOM的跨站脚本攻击。在实践中，不同的源和接收器具有不同的属性和行为，这可能会影响可利用性，并决定需要采取什么技术。此外，网站的脚本可能会对数据进行验证或其他处理，在尝试利用漏洞时必须考虑这些处理。有多种与基于DOM的漏洞相关的接收器。详情请参阅这个[列表](https://github.com/0xf4n9x/Web_Security_Academy-zh/blob/master/client-side/dom-based/index.md#哪些接收器可能导致基于DOM的漏洞？)。

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

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

> **LAB**
>
> [在使用`location.search`源的`document.write`接收器中的DOM型XSS](https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-document-write-sink)

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

> **LAB**
>
> [在select元素内使用`location.search`源的`document.write`接收器中的DOM型XSS](https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-document-write-sink-inside-select-element)

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

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

> **LAB**
>
> [在使用`location.search`源的`innerHTML`接收器中的DOM型XSS](https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-innerhtml-sink)

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

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

#### jQuery中的DOM型XSS

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

```javascript
$(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](https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-jquery-href-attribute-sink)

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

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

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

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

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

```html
<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](https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-jquery-selector-hash-change-event)

#### AngularJS中的DOM型XSS

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

> **LAB**
>
> [带有角括号和双引号HTML编码的AngularJS表达式中的DOM型XSS](https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-angularjs-expression)

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

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

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

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

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

> **LAB**
>
> [反射型DOM XSS](https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-dom-xss-reflected)

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

```html
element.innerHTML = comment.author
```

> **LAB**
>
> [存储型DOM XSS](https://portswigger.net/web-security/cross-site-scripting/dom-based/lab-dom-xss-stored)

## 哪些接收器可能导致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的漏洞](/wsa/client-side/dom-based.md)中描述的通用措施外，还应避免允许来自任何不受信任的源的数据动态写入到HTML文档。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://web-sec.gitbook.io/wsa/client-side/cross-site-scripting/dom-based.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
