mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
301 lines
22 KiB
Markdown
301 lines
22 KiB
Markdown
# DOM XSS
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|
|
|
|
## DOM 취약점
|
|
|
|
DOM 취약점은 공격자가 제어하는 **소스**(예: `location.search`, `document.referrer`, 또는 `document.cookie`)에서 데이터가 안전하지 않게 **싱크**로 전송될 때 발생합니다. 싱크는 악성 데이터를 받으면 해로운 콘텐츠를 실행하거나 렌더링할 수 있는 함수나 객체(예: `eval()`, `document.body.innerHTML`)입니다.
|
|
|
|
- **소스**는 공격자가 조작할 수 있는 입력으로, URL, 쿠키 및 웹 메시지를 포함합니다.
|
|
- **싱크**는 악성 데이터가 부작용을 초래할 수 있는 잠재적으로 위험한 엔드포인트로, 스크립트 실행과 같은 결과를 초래할 수 있습니다.
|
|
|
|
위험은 데이터가 적절한 검증이나 정화 없이 소스에서 싱크로 흐를 때 발생하여 XSS와 같은 공격을 가능하게 합니다.
|
|
|
|
> [!NOTE]
|
|
> **더 업데이트된 소스 및 싱크 목록은** [**https://github.com/wisec/domxsswiki/wiki**](https://github.com/wisec/domxsswiki/wiki)에서 확인할 수 있습니다.
|
|
|
|
**일반적인 소스:**
|
|
```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) | [**자바스크립트 주입**](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` | **\`\`**[**쿠키 조작**](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) | [**웹 메시지 조작**](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에서의 오픈 리다이렉트 취약점**은 스크립트가 공격자가 제어할 수 있는 데이터를 도메인 간 탐색을 시작할 수 있는 싱크에 기록할 때 발생합니다.
|
|
|
|
리다이렉션이 발생하는 URL의 시작 부분을 제어할 수 있는 경우, **`javascript:alert(1)`**와 같은 임의의 코드를 실행할 수 있다는 점을 이해하는 것이 중요합니다.
|
|
|
|
싱크:
|
|
```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()
|
|
```
|
|
### 쿠키 조작
|
|
|
|
From: [https://portswigger.net/web-security/dom-based/cookie-manipulation](https://portswigger.net/web-security/dom-based/cookie-manipulation)
|
|
|
|
DOM 기반 쿠키 조작 취약점은 스크립트가 공격자가 제어할 수 있는 데이터를 쿠키의 값에 포함할 때 발생합니다. 이 취약점은 쿠키가 사이트 내에서 사용될 경우 웹페이지의 예기치 않은 동작을 초래할 수 있습니다. 또한, 쿠키가 사용자 세션 추적에 관여하는 경우 세션 고정 공격을 수행하는 데 악용될 수 있습니다. 이 취약점과 관련된 주요 싱크는:
|
|
|
|
Sinks:
|
|
```javascript
|
|
document.cookie
|
|
```
|
|
### JavaScript Injection
|
|
|
|
From: [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 manipulation vulnerabilities**는 스크립트가 공격자가 제어할 수 있는 데이터를 사용하여 `document.domain` 속성을 설정할 때 발생합니다.
|
|
|
|
`document.domain` 속성은 브라우저의 **same-origin policy** **강제**에 **중요한 역할**을 합니다. 서로 다른 출처의 두 페이지가 `document.domain`을 **같은 값**으로 설정하면 제한 없이 상호작용할 수 있습니다. 브라우저는 `document.domain`에 할당할 수 있는 값에 대해 특정 **제한**을 두지만, 완전히 관련 없는 값을 실제 페이지 출처에 할당하는 것을 방지하는 예외가 존재합니다. 일반적으로 브라우저는 **자식** 또는 **부모 도메인**의 사용을 허용합니다.
|
|
|
|
Sinks:
|
|
```javascript
|
|
document.domain
|
|
```
|
|
### WebSocket-URL 중독
|
|
|
|
From: [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 중독 취약점으로 이어질 수 있습니다.
|
|
|
|
### 링크 조작
|
|
|
|
From: [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 요청 조작
|
|
|
|
From: [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 요청에 **공격자가 제어할 수 있는 데이터를 기록할 때** 발생합니다.
|
|
|
|
Sinks:
|
|
```javascript
|
|
XMLHttpRequest.setRequestHeader()
|
|
XMLHttpRequest.open()
|
|
XMLHttpRequest.send()
|
|
jQuery.globalEval()
|
|
$.globalEval()
|
|
```
|
|
### 로컬 파일 경로 조작
|
|
|
|
From: [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을 구성하여 다른 사용자가 방문할 경우 **사용자의 브라우저가 임의의 로컬 파일을 열거나 작성하게** 할 수 있습니다.
|
|
|
|
Sinks:
|
|
```javascript
|
|
FileReader.readAsArrayBuffer()
|
|
FileReader.readAsBinaryString()
|
|
FileReader.readAsDataURL()
|
|
FileReader.readAsText()
|
|
FileReader.readAsFile()
|
|
FileReader.root.getFile()
|
|
FileReader.root.getFile()
|
|
```
|
|
### 클라이언트 측 SQL 인젝션
|
|
|
|
From: [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 취약점**은 스크립트가 **공격자가 제어할 수 있는 데이터를 웹 브라우저의 HTML5 저장소**(`localStorage` 또는 `sessionStorage`)에 저장할 때 발생합니다. 이 작업 자체는 본질적으로 보안 취약점이 아니지만, 애플리케이션이 이후에 **저장된 데이터를 읽고 안전하지 않게 처리할 경우** 문제가 됩니다. 이는 공격자가 저장 메커니즘을 활용하여 교차 사이트 스크립팅 및 JavaScript 주입과 같은 다른 DOM 기반 공격을 수행할 수 있게 할 수 있습니다.
|
|
|
|
Sinks:
|
|
```javascript
|
|
sessionStorage.setItem()
|
|
localStorage.setItem()
|
|
```
|
|
### XPath 주입
|
|
|
|
From: [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 주입
|
|
|
|
From: [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()
|
|
```
|
|
### 웹 메시지 조작
|
|
|
|
From: [https://portswigger.net/web-security/dom-based/web-message-manipulation](https://portswigger.net/web-security/dom-based/web-message-manipulation)
|
|
|
|
**웹 메시지 취약점**은 스크립트가 **공격자가 제어할 수 있는 데이터를 웹 메시지로 다른 문서에 전송할 때** 발생합니다. 취약한 웹 메시지 조작의 **예**는 [PortSwigger의 웹 보안 아카데미](https://portswigger.net/web-security/dom-based/controlling-the-web-message-source)에서 찾을 수 있습니다.
|
|
|
|
Sinks:
|
|
|
|
웹 메시지를 전송하기 위한 `postMessage()` 메서드는 메시지를 수신하는 이벤트 리스너가 수신 데이터를 안전하지 않게 처리할 경우 취약점으로 이어질 수 있습니다.
|
|
|
|
### DOM 데이터 조작
|
|
|
|
From: [https://portswigger.net/web-security/dom-based/dom-data-manipulation](https://portswigger.net/web-security/dom-based/dom-data-manipulation)
|
|
|
|
**DOM 데이터 조작 취약점**은 스크립트가 **공격자가 제어할 수 있는 데이터를 DOM 내의 필드에 기록할 때** 발생하며, 이는 가시 UI 또는 클라이언트 측 로직에서 사용됩니다. 이 취약점은 공격자가 다른 사용자가 방문할 경우 클라이언트 측 UI의 모양이나 동작을 변경할 수 있는 URL을 구성하는 데 악용될 수 있습니다.
|
|
|
|
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()
|
|
```
|
|
### 서비스 거부
|
|
|
|
From: [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 Clobbering
|
|
|
|
{{#ref}}
|
|
dom-clobbering.md
|
|
{{#endref}}
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|