githubEdit

DOM BASED

What is the DOM?

  • The Document Object Model (DOM) is a web browser's hierarchical representation of the elements on the page.

    • Websites use JavaScript to manipulate the nodes and objects of the DOM and their properties.

    • JavaScript that handles data insecurely can enable various attacks.

  • DOM-based vulnerabilities arise when a website contains JavaScript that takes an attacker-controllable value and passes it into a dangerous function (a sink).

DOM-based cross-site scripting

  • This arises when an application contains some client-side JavaScript that processes data from an untrusted source in an unsafe way and writes the data to the DOM.

Example:

var search = document.getElementById('search').value;
var results = document.getElementById('results');
results.innerHTML = 'You searched for: ' + search;

This can be attacked with a malicious value that causes script to execute. For example: You searched for:

In most cases the input field would be populated from part of the HTTP request, e.g. a URL query string parameter.

Taint-flow vulnerabilities

  • Many DOM-based vulnerabilities can be traced back to problems in how client-side code manipulates attacker-controllable data.

What is Taint flow?

  • Taint flow involves tracking potentially unsafe or untrusted data as it moves through a program to ensure it does not reach sensitive parts of the system.

Sources

  • JavaScript properties that accept potentially attacker-controlled data:

    • location.search (reads inputs from the query string)

    • document.referrer

    • document.cookie

    • web messages, etc.

Sinks

  • Potentially dangerous JavaScript functions or DOM objects that can cause undesirable effects if attacker-controlled data is passed to them:

    • eval() (executes argument as JavaScript)

    • HTML sinks like document.body.innerHTML (allows injection of malicious HTML and execution of arbitrary JavaScript)

DOM-based vulnerabilities arise when data flows from a source to a sink in an unsafe way.

Example showing unsafe handling of location.hash:

  • Vulnerable because location.hash is attacker-controllable and is used unsafely to set location, allowing redirection to an attacker-controlled site.

Common sources (examples):

DOM-based XSS

  • DOM-based XSS occurs when JavaScript takes data from an attacker-controllable source (e.g., URL) and passes it to a sink that supports dynamic code execution (e.g., eval() or innerHTML), enabling an attacker to execute malicious JavaScript (e.g., hijack user accounts).


Labs (each lab is presented as a stepper)

1

Lab: DOM XSS in document.write sink using source location.search — Overview

  • Target: QuickSearch (Web_Pentest730). The site displays search results dynamically and insecurely writes search queries directly to the webpage using document.write(). Input is taken from location.search, making it vulnerable to DOM-based XSS.

Goal: Inject HTML/JavaScript into the page by modifying the URL.

2

Vulnerability — Problem

  • document.write() is used with data from location.search without sanitization.

  • location.search contains the URL query string (anything after ?), which an attacker controls.

3

Reconnaissance plan

  • Enter a random alphanumeric string into the search box.

  • Inspect and confirm the random string is placed inside an img src attribute in the resulting HTML.

  • Task: break out of the img attribute context.

4

Attack / Payload

  • Payload: "><svg onload=alert(1)>

    • Breaks out of the src attribute of an <img> tag, closes the tag, injects an <svg> with onload that runs alert(1).

5

Exploit & Trigger

  • Add payload to the URL query string: https://YOUR-LAB-ID.web-security-academy.net/?search="><svg onload=alert(1)>

  • When the victim loads the URL, location.search is written to the page by document.write(). The injected SVG onload runs and triggers the alert.

6

Why the exploit works & impact

  • Breaking out of the attribute context: "> closes <img> tag allowing new elements.

  • document.write() writes input as-is—dangerous if unsanitized.

  • location.search is attacker-controlled.

  • Real-world impact: account hijacking (steal document.cookie), phishing (fake forms), malware distribution.

7

Lab: DOM XSS in document.write sink using source location.search inside a select element — Overview

  • Target: Online shopping platform (Web_Pentest731). storeId parameter from URL is added as an <option> inside a <select> via document.write() without sanitization.

  • Goal: Break out of <select>, inject HTML/JS, trigger an alert.

8

Reconnaissance & Vulnerability

  • Test with: https://YOUR-LAB-ID.web-security-academy.net/product?productId=1&storeId=test123

  • Confirm test123 appears as <option>test123</option>.

  • Vulnerability: storeId is written directly into an option with document.write().

9

Attack / Payload

  • Payload: "></select><img src=1 onerror=alert(1)>

    • "> closes the <option>.

    • </select> closes the <select>.

    • <img src=1 onerror=alert(1)> injects an image that fails to load and triggers onerror.

  • Malicious URL: https://YOUR-LAB-ID.web-security-academy.net/product?productId=1&storeId="></select><img%20src=1%20onerror=alert(1)>

10

Exploit & Trigger

  • Victim visits URL → payload breaks out of <select> and adds <img>.

  • Image fails to load (src=1), onerror executes alert(1).

11

Why it works

  • The payload closes <option> and <select>, allowing injection.

  • document.write() writes input as-is.

  • Injected <img> triggers onerror when load fails.

12

Lab: DOM XSS in innerHTML sink using source location.search inside a div — Overview

  • Target: techBlogs (Web_Pentest731). Search terms from location.search are inserted into a <div> via innerHTML.

  • Goal: Inject HTML through the URL, which innerHTML interprets and executes JS.

13

Vulnerability

  • Source: location.search from URL query string.

  • Sink: element.innerHTML used to update a <div> element unsafely.

14

Reconnaissance & Attack

  • Payload: <img src=1 onerror=alert(1)>

  • Malicious URL: https://YOUR-LAB-ID.web-security-academy.net/?search=<img%20src=1%20onerror=alert(1)>

15

Exploit & Trigger

  • Victim loads URL → location.search written into <div> using innerHTML.

  • Browser creates <img> that fails to load and triggers onerroralert(1).

16

Why it works

  • innerHTML interprets input as HTML (no escaping).

  • location.search is attacker-controlled.

  • Real-world impact: cookie theft, etc.

17

Lab: DOM XSS in jQuery anchor href attribute sink using location.search — Overview

  • Target: feedbackHub (Web_Pentest731). jQuery updates a "Back" link's href from the returnPath URL parameter.

  • Goal: Inject a javascript: URL into href to execute arbitrary code (e.g., alert document.cookie).

18

Vulnerability

  • Source: returnPath from location.search.

  • Sink: jQuery selector modifies anchor href attribute using untrusted input.

19

Reconnaissance & Attack

  • Test: https://YOUR-LAB-ID.web-security-academy.net/feedback?returnPath=/test123

  • Payload for returnPath: javascript:alert(document.cookie)

  • Malicious URL: https://YOUR-LAB-ID.web-security-academy.net/feedbackreturnPath=javascript:alert(document.cookie)

20

Exploit & Trigger

  • Load the modified URL and click the "Back" link → javascript: URL executes and shows document.cookie.

21

Why it works & impact

  • jQuery attribute modification with untrusted input is a dangerous sink.

  • javascript: scheme executes code when clicked.

  • Real-world impact: cookie theft, phishing, drive-by malware.

22

Lab: DOM XSS in jQuery selector sink using a hashchange event — Overview

  • Target: Site uses $(location.hash) to scroll to posts based on URL hash and listens for hashchange.

  • Vulnerability: $(location.hash) uses attacker-controlled hash in jQuery selector, enabling injection.

  • Goal: Cause execution of print() via an injected payload in the hash using an iframe exploit.

23

Reconnaissance & Attack

  • Construct an iframe on the exploit server:

  • When the iframe loads it updates its src to include <img src=x onerror=print()> in the hash, triggering hashchange and the payload.

24

Exploit & Trigger

  • Save and view the exploit. Deliver the exploit to victim by serving the iframe.

  • When the victim visits the iframe, the modified URL with malicious hash is auto-loaded and print() executes.

25

Why it works & impact

  • jQuery's $() selector combined with location.hash is treated as trusted and can be abused.

  • The hashchange event can be triggered automatically by modifying src of an iframe.

  • Real-world impact: data theft, phishing, drive-by malware.

26

Lab: DOM XSS in AngularJS expression with angle brackets and double quotes HTML-encoded — Overview

  • Target: AngularJS-based site (Web_Pentest732) where search input is evaluated as an AngularJS expression inside {{ }}.

  • Challenge: Angle brackets < and > are encoded, so classic tag-based payloads won't work.

  • Goal: Execute JavaScript via AngularJS expression.

27

Attack / Payload

  • Use AngularJS expression: {{$on.constructor('alert(1)')()}}

    • $on.constructor accesses a constructor to build a new function from a string ('alert(1)') and executes it.

  • Enter the payload in the search box and trigger the search.

28

Exploit & Trigger

  • AngularJS evaluates the expression inside {{ }}. The payload becomes a function and executes alert(1).

29

Why it works & impact

  • AngularJS will evaluate expressions inside {{ }} allowing arbitrary JS if an exploitable path exists.

  • No angle brackets needed.

  • Real-world impact: data theft, phishing, malware distribution.

30

Lab: Reflected DOM XSS — Overview

  • Target: Site (Web_Pentest737) where the search term is sent to server, server reflects it in JSON response as searchTerm, and client code passes it to eval().

  • Goal: Break out of JSON string to execute JavaScript.

31

Vulnerability & Reconnaissance

  • Server reflects user input in JSON: {"searchTerm":"XSS", "results":[]}

  • Client-side script processes JSON and uses eval()—unsafe if data isn't sanitized.

  • Observe escaping behavior: double quotes " are escaped, but backslashes \ may not be handled correctly.

32

Attack / Payload

  • Payload to break out of JSON string: \"-alert(1)}//

    • \" attempts to close/escape string.

    • - harmless arithmetic separator.

    • alert(1) JS to execute.

    • } closes JSON object.

    • // comments out remainder.

33

Exploit & Trigger

  • Search with the payload.

  • JSON response may look like: {"searchTerm":"\\"-alert(1)}//", "results":[]}

  • Due to improper handling of backslashes, this can result in eval() executing alert(1).

34

Why it works & impact

  • Backslash injection subverts escaping.

  • Breaking out of JSON string allows code injection.

  • Using eval() executes the injected code.

  • Real-world impact: cookie theft, phishing, malware.

35

Lab: Stored DOM XSS — Overview

  • Target: Blog site (Web_Pentest74) storing comments. A comment is stored on server and later inserted into the DOM client-side unsafely. The site attempts to encode angle brackets but only replaces the first occurrence.

  • Goal: Bypass the single-replacement encoding and execute JavaScript.

36

Vulnerability & Reconnaissance

  • Site encodes angle brackets < > only for the first occurrence in the string (e.g., using replace() once).

  • Subsequent angle brackets remain unencoded and are interpreted as HTML.

37

Attack / Payload

  • Payload: <><img src=1 onerror=alert(1)>

    • The initial <> is encoded by the single-replacement filter, allowing the later <img> to remain and be interpreted as HTML.

  • Submit the payload as a comment.

38

Exploit & Trigger

  • Comment is stored by the server.

  • When another user views the post, the unsafe client-side script inserts the stored comment into the DOM (likely via innerHTML), rendering the <img> which fails to load and fires onerroralert(1).

39

Why it works & impact

  • The site’s filter replaces only the first </>; extra brackets bypass the filter.

  • The stored comment ends up in the DOM and is interpreted as HTML.

  • Real-world impact: data theft, phishing, malware.


Common sinks that can lead to DOM-XSS vulnerabilities

jQuery functions that are also sinks


WEB DOM CLOBBERING

  • Analogy: A JavaScript program is a party and variables are people wearing name tags. If an attacker injects an HTML element with an id/name that matches a global variable, the browser may resolve that global to the DOM node instead of the intended JS object.

Example vulnerable code:

Attacker injection:

  • window.someObject becomes the DOM node (the <a>), so someObject.url points to the link's url/name property, and a script is loaded from the attacker-controlled URL.

Dom clobbering is overwriting a JavaScript variable with a DOM element, often to trick the app into behaving unexpectedly.

How to find & test clobbering

  • Look for code patterns like window.xyz || {} or global variables used without strict checks.

  • Check places where HTML injection is possible (profile pages, comments, forums).

  • Try injecting elements with id/name matching global variable names:

  • Observe whether scripts/resources are loaded from attacker domains or behavior changes.

  • In DevTools: inspect console.log(window.someObject) after injection to see if it's a DOM node.


Lab: DOM-clobbering → Attribute injection → XSS

1

1. Lab Introduction

  • Feature: Comments that allow limited HTML anchors and render avatars using defaultAvatar.avatar.

  • Attack vector: DOM clobbering via anchors sharing the same id, combined with attribute values that decode at runtime (e.g., &quot;) to break attribute syntax and inject an onerror handler.

  • Techniques: DOM clobbering, attribute injection, unsafe client-side string concatenation / innerHTML.

2

2. Reconnaissance plan

  • Find comment posting pages allowing minimal HTML (e.g., <a>, <img>).

  • Check sanitizer rules: can you include id, name, encoded quotes like &quot;, cid: protocols?

  • Search client scripts for window.someVar || {} patterns, e.g., let defaultAvatar = window.defaultAvatar || {...}.

  • Check whether avatars are injected via innerHTML or img.src.

3

3. Vulnerability — Problem

  • Source: reliance on global window.defaultAvatar || {avatar: '...'} combined with user-controlled HTML that can create id/name colliding with globals.

  • Sink: unsafe HTML construction such as:

  • Sanitizer allows id/name, cid: protocol and &quot; sequences that decode into quotes at runtime.

4

4. End goal

  • Cause the site to render an element containing an onerror attribute executing alert(1), demonstrating that clobbering + attribute injection bypasses filters.

5

5. Attack (first attempt) — Payloads

Comment #1 (clobber):

  • Two <a> elements share id=defaultAvatar so window.defaultAvatar refers to the DOM collection/element.

  • Second anchor has name=avatar and href="cid:&quot;onerror=alert(1)//". When &quot; decodes to ", it can break attribute syntax later and inject onerror.

6

6. Exploit & Trigger

  1. Post Comment #1 (the two anchors).

  2. Post Comment #2 (e.g., "Nice post!") to trigger re-render of comments.

  3. Client executes code like:

  • With window.defaultAvatar clobbered, defaultAvatar.avatar resolves to cid:"onerror=alert(1)//.

  • Constructed HTML becomes:

  • Browser parses the broken attribute, image load fails, onerror fires → alert(1).

7

7. Enumeration

  • Confirm comments accept minimal HTML and id/name.

  • Locate client code patterns like window.X || {}.

  • Verify avatar rendering uses innerHTML / unsafe templating.

  • Test sanitizer: does it allow cid: and &quot;?

  • Use DevTools to inspect window.defaultAvatar after comment #1 to confirm clobbering.

8

8. Final payload & notes

  • Comment #1 (clobber):

  • Comment #2 (trigger): Nice post!

  • Single-line variant:

Why it works:

  • id=defaultAvatar clobbers JS variable.

  • name=avatar provides .avatar property on the DOM collection.

  • &quot; decodes to " and breaks src="...", inserting onerror.

  • Unsafe innerHTML concatenation results in a parsed element with onerror.

Real-world impact:

  • Look for window.X || {} patterns and sanitizer allowances (cid:, &quot;).

  • Clobbering can lead to script execution even when ordinary XSS vectors are filtered.


How to Prevent DOM-Based XSS (summary)

  • Avoid using dangerous sinks like document.write() where possible. Prefer safe DOM APIs:

    • Use textContent, innerText, or set properties like element.src instead of innerHTML/string templating.

  • Sanitize and encode user inputs appropriately for the context where they will be used.

  • Implement a strong Content Security Policy (CSP) to limit sources of executable scripts and reduce XSS impact.

  • Avoid relying on global variables that can be clobbered; validate and tightly control values used to build HTML.

Last updated