# DOM XSS {{#include ../../banners/hacktricks-training.md}} ## DOM 漏洞 DOM 漏洞发生在来自攻击者控制的 **sources**(如 `location.search`、`document.referrer` 或 `document.cookie`)的数据不安全地传输到 **sinks** 时。Sinks 是可以执行或渲染有害内容的函数或对象(例如 `eval()`、`document.body.innerHTML`),如果给定恶意数据。 - **Sources** 是可以被攻击者操控的输入,包括 URL、cookies 和网页消息。 - **Sinks** 是潜在危险的端点,恶意数据可能导致不良后果,例如脚本执行。 风险在于数据从源流向汇时没有适当的验证或清理,从而使攻击如 XSS 成为可能。 > [!NOTE] > **您可以在** [**https://github.com/wisec/domxsswiki/wiki**](https://github.com/wisec/domxsswiki/wiki) **找到更更新的 sources 和 sinks 列表** **常见 sources:** ```javascript document.URL document.documentURI document.URLUnencoded document.baseURI location document.cookie document.referrer window.name history.pushState history.replaceState localStorage sessionStorage IndexedDB(mozIndexedDB, webkitIndexedDB, msIndexedDB) Database ``` **常见的接收点:** | [**开放重定向**](dom-xss.md#open-redirect) | [**Javascript 注入**](dom-xss.md#javascript-injection) | [**DOM 数据操作**](dom-xss.md#dom-data-manipulation) | **jQuery** | | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------- | | `location` | `eval()` | `scriptElement.src` | `add()` | | `location.host` | `Function() constructor` | `scriptElement.text` | `after()` | | `location.hostname` | `setTimeout()` | `scriptElement.textContent` | `append()` | | `location.href` | `setInterval()` | `scriptElement.innerText` | `animate()` | | `location.pathname` | `setImmediate()` | `someDOMElement.setAttribute()` | `insertAfter()` | | `location.search` | `execCommand()` | `someDOMElement.search` | `insertBefore()` | | `location.protocol` | `execScript()` | `someDOMElement.text` | `before()` | | `location.assign()` | `msSetImmediate()` | `someDOMElement.textContent` | `html()` | | `location.replace()` | `range.createContextualFragment()` | `someDOMElement.innerText` | `prepend()` | | `open()` | `crypto.generateCRMFRequest()` | `someDOMElement.outerText` | `replaceAll()` | | `domElem.srcdoc` | **\`\`**[**本地文件路径操作**](dom-xss.md#local-file-path-manipulation) | `someDOMElement.value` | `replaceWith()` | | `XMLHttpRequest.open()` | `FileReader.readAsArrayBuffer()` | `someDOMElement.name` | `wrap()` | | `XMLHttpRequest.send()` | `FileReader.readAsBinaryString()` | `someDOMElement.target` | `wrapInner()` | | `jQuery.ajax()` | `FileReader.readAsDataURL()` | `someDOMElement.method` | `wrapAll()` | | `$.ajax()` | `FileReader.readAsText()` | `someDOMElement.type` | `has()` | | **\`\`**[**Ajax 请求操作**](dom-xss.md#ajax-request-manipulation) | `FileReader.readAsFile()` | `someDOMElement.backgroundImage` | `constructor()` | | `XMLHttpRequest.setRequestHeader()` | `FileReader.root.getFile()` | `someDOMElement.cssText` | `init()` | | `XMLHttpRequest.open()` | `FileReader.root.getFile()` | `someDOMElement.codebase` | `index()` | | `XMLHttpRequest.send()` | [**链接操作**](dom-xss.md#link-manipulation) | `someDOMElement.innerHTML` | `jQuery.parseHTML()` | | `jQuery.globalEval()` | `someDOMElement.href` | `someDOMElement.outerHTML` | `$.parseHTML()` | | `$.globalEval()` | `someDOMElement.src` | `someDOMElement.insertAdjacentHTML` | [**客户端 JSON 注入**](dom-xss.md#client-side-sql-injection) | | **\`\`**[**HTML5 存储操作**](dom-xss.md#html-5-storage-manipulation) | `someDOMElement.action` | `someDOMElement.onevent` | `JSON.parse()` | | `sessionStorage.setItem()` | [**XPath 注入**](dom-xss.md#xpath-injection) | `document.write()` | `jQuery.parseJSON()` | | `localStorage.setItem()` | `document.evaluate()` | `document.writeln()` | `$.parseJSON()` | | **``**[**拒绝服务**](dom-xss.md#denial-of-service)**``** | `someDOMElement.evaluate()` | `document.title` | **\`\`**[**Cookie 操作**](dom-xss.md#cookie-manipulation) | | `requestFileSystem()` | **\`\`**[**文档域操作**](dom-xss.md#document-domain-manipulation) | `document.implementation.createHTMLDocument()` | `document.cookie` | | `RegExp()` | `document.domain` | `history.pushState()` | [**WebSocket-URL 中毒**](dom-xss.md#websocket-url-poisoning) | | [**客户端 SQL 注入**](dom-xss.md#client-side-sql-injection) | [**Web 消息操作**](dom-xss.md#web-message-manipulation) | `history.replaceState()` | `WebSocket` | | `executeSql()` | `postMessage()` | \`\` | \`\` | **`innerHTML`** 接收点在任何现代浏览器中都不接受 `script` 元素,也不会触发 `svg onload` 事件。这意味着您需要使用替代元素,如 `img` 或 `iframe`。 这种类型的 XSS 可能是 **最难发现的**,因为您需要查看 JS 代码,看看它是否 **使用** 任何您 **控制** 的对象,并在这种情况下,查看是否有 **任何方法可以滥用** 它以执行任意 JS。 ## 查找工具 - [https://github.com/mozilla/eslint-plugin-no-unsanitized](https://github.com/mozilla/eslint-plugin-no-unsanitized) - 检查每个到达潜在接收点的数据的浏览器扩展:[https://github.com/kevin-mizu/domloggerpp](https://github.com/kevin-mizu/domloggerpp) ## 示例 ### 开放重定向 来自:[https://portswigger.net/web-security/dom-based/open-redirection](https://portswigger.net/web-security/dom-based/open-redirection) **DOM 中的开放重定向漏洞** 发生在脚本将攻击者可以控制的数据写入能够跨域发起导航的接收点时。 理解执行任意代码(例如 **`javascript:alert(1)`**)是可能的,如果您控制重定向发生的 URL 开头,这是至关重要的。 接收点: ```javascript location location.host location.hostname location.href location.pathname location.search location.protocol location.assign() location.replace() open() domElem.srcdoc XMLHttpRequest.open() XMLHttpRequest.send() jQuery.ajax() $.ajax() ``` ### Cookie manipulation 来自: [https://portswigger.net/web-security/dom-based/cookie-manipulation](https://portswigger.net/web-security/dom-based/cookie-manipulation) 基于DOM的cookie操控漏洞发生在脚本将攻击者可以控制的数据纳入cookie的值时。如果在网站内使用该cookie,这个漏洞可能导致网页出现意外行为。此外,如果cookie涉及用户会话跟踪,它可以被利用来进行会话固定攻击。与此漏洞相关的主要接收点是: Sinks: ```javascript document.cookie ``` ### JavaScript 注入 来自: [https://portswigger.net/web-security/dom-based/javascript-injection](https://portswigger.net/web-security/dom-based/javascript-injection) 基于 DOM 的 JavaScript 注入漏洞是在脚本将可以被攻击者控制的数据作为 JavaScript 代码运行时产生的。 Sinks: ```javascript eval() Function() constructor setTimeout() setInterval() setImmediate() execCommand() execScript() msSetImmediate() range.createContextualFragment() crypto.generateCRMFRequest() ``` ### Document-domain manipulation From: [https://portswigger.net/web-security/dom-based/document-domain-manipulation](https://portswigger.net/web-security/dom-based/document-domain-manipulation) **文档域操控漏洞**发生在脚本使用攻击者可以控制的数据设置`document.domain`属性时。 `document.domain`属性在浏览器的**同源策略**的**执行**中发挥着**关键作用**。当来自不同源的两个页面将其`document.domain`设置为**相同的值**时,它们可以不受限制地相互交互。尽管浏览器对可分配给`document.domain`的值施加了某些**限制**,防止将完全不相关的值分配给实际页面源,但仍然存在例外。通常,浏览器允许使用**子域**或**父域**。 Sinks: ```javascript document.domain ``` ### WebSocket-URL 中毒 来自: [https://portswigger.net/web-security/dom-based/websocket-url-poisoning](https://portswigger.net/web-security/dom-based/websocket-url-poisoning) **WebSocket-URL 中毒** 发生在脚本将 **可控数据作为 WebSocket 连接的目标 URL** 使用时。 Sinks: `WebSocket` 构造函数可能导致 WebSocket-URL 中毒漏洞。 ### 链接操控 来自: [https://portswigger.net/web-security/dom-based/link-manipulation](https://portswigger.net/web-security/dom-based/link-manipulation) **基于 DOM 的链接操控漏洞** 出现于脚本将 **攻击者可控的数据写入当前页面的导航目标**,例如可点击的链接或表单的提交 URL。 Sinks: ```javascript someDOMElement.href someDOMElement.src someDOMElement.action ``` ### Ajax 请求操控 来自: [https://portswigger.net/web-security/dom-based/ajax-request-header-manipulation](https://portswigger.net/web-security/dom-based/ajax-request-header-manipulation) **Ajax 请求操控漏洞** 出现于脚本将 **攻击者可控的数据写入使用 `XmlHttpRequest` 对象发出的 Ajax 请求** 时。 接收点: ```javascript XMLHttpRequest.setRequestHeader() XMLHttpRequest.open() XMLHttpRequest.send() jQuery.globalEval() $.globalEval() ``` ### 本地文件路径操控 来自: [https://portswigger.net/web-security/dom-based/local-file-path-manipulation](https://portswigger.net/web-security/dom-based/local-file-path-manipulation) **本地文件路径操控漏洞** 出现于脚本将 **攻击者可控的数据传递给文件处理API** 作为 `filename` 参数时。攻击者可以利用此漏洞构造一个URL,如果另一个用户访问该URL,可能导致 **用户的浏览器打开或写入任意本地文件**。 Sinks: ```javascript FileReader.readAsArrayBuffer() FileReader.readAsBinaryString() FileReader.readAsDataURL() FileReader.readAsText() FileReader.readAsFile() FileReader.root.getFile() FileReader.root.getFile() ``` ### 客户端 SQL 注入 来自: [https://portswigger.net/web-security/dom-based/client-side-sql-injection](https://portswigger.net/web-security/dom-based/client-side-sql-injection) **客户端 SQL 注入漏洞** 发生在脚本以 **不安全的方式将攻击者可控的数据纳入客户端 SQL 查询** 时。 Sinks: ```javascript executeSql() ``` ### HTML5-storage manipulation From: [https://portswigger.net/web-security/dom-based/html5-storage-manipulation](https://portswigger.net/web-security/dom-based/html5-storage-manipulation) **HTML5-storage manipulation vulnerabilities** 出现于脚本 **在网页浏览器的 HTML5 存储中存储攻击者可控的数据** (`localStorage` 或 `sessionStorage`)。虽然此操作本身并不是安全漏洞,但如果应用程序随后 **读取存储的数据并不安全地处理它**,则会变得有问题。这可能允许攻击者利用存储机制进行其他基于 DOM 的攻击,例如跨站脚本和 JavaScript 注入。 Sinks: ```javascript sessionStorage.setItem() localStorage.setItem() ``` ### XPath 注入 来自: [https://portswigger.net/web-security/dom-based/client-side-xpath-injection](https://portswigger.net/web-security/dom-based/client-side-xpath-injection) **基于 DOM 的 XPath 注入漏洞** 发生在脚本将 **攻击者可控的数据纳入 XPath 查询** 时。 Sinks: ```javascript document.evaluate() someDOMElement.evaluate() ``` ### 客户端 JSON 注入 来自: [https://portswigger.net/web-security/dom-based/client-side-json-injection](https://portswigger.net/web-security/dom-based/client-side-json-injection) **基于 DOM 的 JSON 注入漏洞** 发生在脚本将 **攻击者可控的数据纳入一个被解析为 JSON 数据结构的字符串中,然后由应用程序处理**。 Sinks: ```javascript JSON.parse() jQuery.parseJSON() $.parseJSON() ``` ### Web-message manipulation 来自: [https://portswigger.net/web-security/dom-based/web-message-manipulation](https://portswigger.net/web-security/dom-based/web-message-manipulation) **Web-message 漏洞** 出现于脚本将 **攻击者可控的数据作为网络消息发送到浏览器内的另一个文档**。一个 **示例** 的易受攻击的 Web-message 操作可以在 [PortSwigger's Web Security Academy](https://portswigger.net/web-security/dom-based/controlling-the-web-message-source) 找到。 Sinks: `postMessage()` 方法用于发送网络消息,如果接收消息的事件监听器以不安全的方式处理传入数据,可能会导致漏洞。 ### DOM-data manipulation 来自: [https://portswigger.net/web-security/dom-based/dom-data-manipulation](https://portswigger.net/web-security/dom-based/dom-data-manipulation) **DOM-data 操作漏洞** 出现于脚本将 **攻击者可控的数据写入 DOM 中的一个字段**,该字段在可见的 UI 或客户端逻辑中被使用。攻击者可以利用此漏洞构造一个 URL,如果其他用户访问该 URL,可能会改变客户端 UI 的外观或行为。 Sinks: ```javascript scriptElement.src scriptElement.text scriptElement.textContent scriptElement.innerText someDOMElement.setAttribute() someDOMElement.search someDOMElement.text someDOMElement.textContent someDOMElement.innerText someDOMElement.outerText someDOMElement.value someDOMElement.name someDOMElement.target someDOMElement.method someDOMElement.type someDOMElement.backgroundImage someDOMElement.cssText someDOMElement.codebase document.title document.implementation.createHTMLDocument() history.pushState() history.replaceState() ``` ### 拒绝服务 来自: [https://portswigger.net/web-security/dom-based/denial-of-service](https://portswigger.net/web-security/dom-based/denial-of-service) **基于DOM的拒绝服务漏洞**发生在脚本将**攻击者可控的数据不安全地传递给有问题的平台API**时。这包括在调用时可能导致用户计算机消耗**过多的CPU或磁盘空间**的API。这类漏洞可能会产生显著的副作用,例如浏览器通过拒绝尝试在`localStorage`中存储数据或终止繁忙的脚本来限制网站的功能。 Sinks: ```javascript requestFileSystem() RegExp() ``` ## DOM覆盖 {{#ref}} dom-clobbering.md {{#endref}} {{#include ../../banners/hacktricks-training.md}}