- DOM = Document Object Model
- It's basically a tree-like structure of everything on a webpage.
- Every element, text, and attribute is part of this tree.
Think of it like a family tree for your webpage:
<html>
└── <body>
├── <div id="header">Hello</div>
└── <p>Welcome</p>Here, <html> is the root, <body> is a child, <div> and <p> are children of <body>.
What is a DOM context?
- A context is just where your code or content exists in the DOM.
- Depending on the context, what JavaScript can do changes.
Some common DOM contexts:

💡 Important: WAFs usually block <script> or other tags, but existing DOM elements and attributes are still "alive" in the page, so that's what people mean by "DOM structures or contexts."
⚠️ Important difference

DOM-based XSS (also known as DOM XSS) arises when an application contains some client-side JavaScript that processes data from an untrusted source in an unsafe way, usually by writing the data back to the DOM.
In the following example, an application uses some JavaScript to read the value from an input field and write that value to an element within the HTML:
var search = document.getElementById('search').value; var results = document.getElementById('results'); results.innerHTML = 'You searched for: ' + search;
If the attacker can control the value of the input field, they can easily construct a malicious value that causes their own script to execute:
You searched for: <img src=1 onerror='/* Bad stuff here... */'>
In a typical case, the input field would be populated from part of the HTTP request, such as a URL query string parameter, allowing the attacker to deliver an attack using a malicious URL, in the same manner as reflected XSS.
JavaScript can read user-controlled data from many places:
Common sources of untrusted input:
- URL
- window.location.search
2. URL hash (#)
- window.location.hash
3. Input fields
- document.getElementById("search").value
4. Cookies / localStorage
👉 All of these are controlled by the user
⚡ Example :
Step 1: Server sends this page
<div id="output"></div>
<script>
var name = new URLSearchParams(window.location.search).get("name");
document.getElementById("output").innerHTML = "Hello " + name;
</script>Step 2: User opens normal URL
https://site.com?name=RahulPage shows:
Hello RahulStep 3: Attacker changes URL
https://site.com?name=<img src=x onerror=alert('XSS')>Step 4: What browser does
- JavaScript reads
namefrom URL - Inserts it into page using
innerHTML
👉 The DOM is modified after the page loads
👉 Script executes
The server sends code like this:
innerHTML = userInput;👉 That line is the real problem
Because it tells the browser:
"Whatever the user gives → treat it as HTML"
How to find DOM XSS ?
Step 1: Look for JavaScript using user input
Search for:
window.locationdocument.URLdocument.cookielocation.search
Step 2: Look for dangerous sinks
These are risky:
innerHTMLouterHTMLdocument.write()eval()
Step 3: Test input
Try putting:
<script>alert(1)</script>or:
<img src=x onerror=alert(1)>Step 4: Check if it executes
If yes → DOM XSS exists
What are "Source" and "Sink"?
✅ Source (very simple)
Source = where user input comes from
👉 Examples:
window.location.search→ data from URL (?name=abc)window.location.hash→ data after#- input fields (
textbox.value)
✅ Sink (very simple)
Sink = where that data is used in a dangerous way
👉 Examples:
innerHTML(inserts HTML)document.write()eval()
So basically ,
Source → gives data Sink → uses data (can be dangerous)
var name = new URLSearchParams(window.location.search).get("name"); // SOURCE
document.getElementById("output").innerHTML = name; // SINK👉 If attacker controls name → and it goes to innerHTML → XSS possible
Testing DOM XSS :
We'll follow what your text says but in simple practical steps.
🔍 Step 1: Put a test string (find if input is used)
Open website and add something random in URL:
https://site.com?test=abc123XYZ👉 abc123XYZ is your test string
🔍 Step 2: Check if it appears in the page (DOM)
- Open DevTools (Right click → Inspect)
- Press Ctrl + F
- Search:
abc123XYZ
✅ If you find it:
👉 That means:
- Your input (source) is reaching the page (sink)
💡 Example
If you see in HTML:
<div>You searched: abc123XYZ</div>👉 Now you know:
- Input is being inserted into DOM
🔥 Step 3: Try breaking it (attack)
Now replace test string with payload:
"><img src=x onerror=alert(1)>🎯 If alert shows → XSS confirmed
🧠 3. Understanding "Context" (very important)
Where your input appears matters.
Example 1: Inside HTML
<div>abc123XYZ</div>👉 You can inject:
<script>alert(1)</script>Example 2: Inside attribute
<input value="abc123XYZ">👉 You need to break out:
" onfocus=alert(1) autofocus=""Understand context and refine input"
Testing for DOM XSS using DOM Invader
Identifying and exploiting DOM XSS in the wild can be a tedious process, often requiring you to manually trawl through complex, minified JavaScript. If you use Burp's browser, however, you can take advantage of its built-in DOM Invader extension, which does a lot of the hard work for you.
This is how source and sink works :
When I type "><img src=x onerror=alert(1)> in the URL, that part is the source.
After loading, sometimes it doesn't appear in page source but appears in DevTools (DOM) because JavaScript inserts it.
The place where it gets inserted (like innerHTML) is the sink.
If I can't see it in the DOM, I should search JavaScript for sources like location.hash and then track where that data goes to find a sink.
Sources and sinks in third-party dependencies
Modern web applications are typically built using a number of third-party libraries and frameworks, which often provide additional functions and capabilities for developers. It's important to remember that some of these are also potential sources and sinks for DOM XSS.
Which sinks can lead to DOM-XSS vulnerabilities?
The following are some of the main sinks that can lead to DOM-XSS vulnerabilities:
document.write()
document.writeln()
document.domain
element.innerHTML
element.outerHTML
element.insertAdjacentHTML
element.oneventThe following jQuery functions are also sinks that can lead to DOM-XSS vulnerabilities:
add()
after()
append()
animate()
insertAfter()
insertBefore()
before()
html()
prepend()
replaceAll()
replaceWith()
wrap()
wrapInner()
wrapAll()
has()
constructor()
init()
index()
jQuery.parseHTML()
$.parseHTML()Difference between Reflected xss and DOM xss :
- Reflected XSS → server puts your input into HTML response (server inserts data)
- DOM XSS → JavaScript in browser puts your input into DOM (browser(javascript) inserts data)
👉 The difference is who inserts the data, not just where it appears.
Exploiting DOM Xss :
- A site is vulnerable if data can flow from a source → to a sink that executes code.
- But not all sources and sinks behave the same, so exploiting them may require different tricks.
- Sometimes the site tries to filter or modify input, so attackers must work around that.
There are a variety of sinks that are relevant to DOM-based vulnerabilities. Please refer to the list below for details.
DOM XSS in document.write sink using source location.search :
The document.write sink works with script elements, so you can use a simple payload, such as the one below:
document.write('... <script>alert(document.domain)</script> ...');
Note, however, that in some situations the content that is written to document.write includes some surrounding context that you need to take account of in your exploit.
Think of it like this:
The website might be doing something like:
document.write('<img src="' + userInput + '">');So your input is not alone — it's wrapped inside:
<img src="YOUR_INPUT_HERE">If you just enter:
<script>alert(1)</script>It becomes:
<img src="<script>alert(1)</script>">👉 That won't execute, because it's treated as part of the src attribute, not as real code.
What you need to do
You must:
- Break out of the current structure (like the
srcattribute) - Then add your own executable code
Example
Input:
"><svg onload=alert(1)>What happens:
<img src="">
<svg onload="alert(1)">Now:
- You closed the attribute (
") - You closed the tag (
>) - Then injected your own element that runs JavaScript
DOM XSS in innerHTML sink using source location.search :
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. Event handlers such as onload and onerror can be used in conjunction with these elements. For example:
element.innerHTML='... <img src=1 onerror=alert(document.domain)> ...'
Payloads:
"><svg onload=alert(1)>
"></select><img src=1 onerror=alert(1)>DOM XSS in jQuery
👉 jQuery is a JavaScript library (a ready-made toolset)
In simple words:
It makes writing JavaScript easier and shorter.
If a JavaScript library such as jQuery is being used, look out for sinks that can alter DOM elements on the page. For instance, jQuery's attr() function can change the attributes of DOM elements. If data is read from a user-controlled source like the URL, then passed to the attr() function, then it may be possible to manipulate the value sent to cause XSS. For example, here we have some JavaScript that changes an anchor element's href attribute using data from the URL:
$(function() { $('#backLink').attr("href",(new URLSearchParams(window.location.search)).get('returnUrl')); });
You can exploit this by modifying the URL so that the location.search source contains a malicious JavaScript URL. After the page's JavaScript applies this malicious URL to the back link's href, clicking on the back link will execute it:
?returnUrl=javascript:alert(document.domain)
Another potential sink to look out for is jQuery's $() selector function, which can be used to inject malicious objects into the DOM.
jQuery used to be extremely popular, and a classic DOM XSS vulnerability was caused by websites using this selector in conjunction with the location.hash source for animations or auto-scrolling to a particular element on the page. This behavior was often implemented using a vulnerable hashchange event handler, similar to the following:
$(window).on('hashchange', function() { var element = $(location.hash); element[0].scrollIntoView(); });
As the hash is user controllable, an attacker could use this to inject an XSS vector into the $() selector sink. More recent versions of jQuery have patched this particular vulnerability by preventing you from injecting HTML into a selector when the input begins with a hash character (#). However, you may still find vulnerable code in the wild.
To actually exploit this classic vulnerability, you'll need to find a way to trigger a hashchange event without user interaction. One of the simplest ways of doing this is to deliver your exploit via an iframe:
<iframe src="https://vulnerable-website.com#" onload="this.src+='<img src=1 onerror=alert(1)>'">
In this example, the src attribute points to the vulnerable page with an empty hash value. When the iframe is loaded, an XSS vector is appended to the hash, causing the hashchange event to fire.
Note
Even newer versions of jQuery can still be vulnerable via the $() selector sink, provided you have full control over its input from a source that doesn't require a # prefix.
DOM XSS in AngularJS
AngularJS is a tool (framework) made by Google that helps developers build dynamic websites more easily.
If a framework like AngularJS is used, it may be possible to execute JavaScript without angle brackets or events. When a site uses the ng-app attribute on an HTML element, it will be processed by AngularJS. In this case, AngularJS will execute JavaScript inside double curly braces that can occur directly in HTML or inside attributes.
In websites using AngularJS, there is a special feature:
👉 You can write code inside {{ }} (double curly braces)
👉 AngularJS will automatically run it
Example:
<div ng-app>
{{ alert('Hacked!') }}
or
<input value="{{ constructor.constructor('alert(1)')() }}">
</div>
## payload : {{$on.constructor('alert(1)')()}}👉 AngularJS sees {{ alert('Hacked!') }}
👉 It executes it
👉 Result: Popup appears ("Hacked!")
In AngularJS,
ng-app is a special attribute that tells AngularJS:
👉 "Start working from here!"
💡 Think of it like a switch
- When AngularJS sees
ng-app - It turns ON AngularJS for that part of the webpage
Without it → AngularJS does nothing With it → AngularJS becomes active
DOM XSS combined with reflected and stored data
This is talking about a mix of:
- Reflected data → server sends your input back in the page
- DOM XSS → JavaScript in the browser uses that data unsafely
👉 So the attack is:
- You send input → server reflects it
- JavaScript reads it → runs it dangerously
🧩 Example (no eval, but still DOM)
<input id="searchBox" value="USER_INPUT">
<script>
var val = document.getElementById("searchBox").value;
document.write(val); // dangerous sink
</script>Payload:
"><img src=x onerror=alert(1)>👉 Server reflects it → input field 👉 JS reads it → writes to DOM → executes
key dangerous sinks to remember
eval()(eg:eval('var data = "reflected string"');)document.write()innerHTML
Websites may also store data on the server and reflect it elsewhere. In a stored DOM XSS vulnerability, the server receives data from one request, stores it, and then includes the data in a later response. A script within the later response contains a sink which then processes the data in an unsafe way.
element.innerHTML = comment.author
Payload:
<><img src=1 onerror=alert(1)>13v! Bug Bounty Learner | H@ppie H@ck!nG