Cross-Site Scripting

Dom, reflected, and stored cross-site scripting

web app cross site scripting XSS Javascript enumeration

XSS Intro

When I was in college around 2008–2009, I accidentally fell into freelance web design. Like most millennials, I learned basic coding through MySpace and AngelFire. Pages would autoplay emo songs, sparkle with glitter, and use simple HTML and CSS. One could easily get by with just HTML and CSS, especially for self hosted websites, because most sites were more informational than interactive. As the web evolved, more sites began using JavaScript, which I hadn't bothered to learn. I was working full time and going to college full time, so learning a new programming language felt like... a lot, and that is where my web design journey ended.

Today, JavaScript is used on roughly 98 percent of all websites. JavaScript is a client-side programming language used to add interactivity to web pages, including things like dynamic content, animations, form validation, and user interaction. With more advanced functionality comes more advanced vulnerabilities. One of the most common is called Cross-Site Scripting (XSS).

Cross-Site Scripting (XSS)

A typical web application sends HTML from the server to the client, where it is rendered in the browser. If user input is not properly sanitized, attackers can inject malicious JavaScript into input fields such as forms. This type of attack can be used to steal cookies or session data, capture login credentials, modify page content, and create fake login forms that can be used for social engineering attacks. XSS vulnerabilities execute entirely on the client side and do not directly modify the back end.

Types of XSS

XSS Types

There are three main types of XSS:

  • Stored (Persistent) XSS - Payload is stored on the server or database and executes for every user who visits the page. This is the most dangerous type.

  • Reflected XSS - Input is processed and immediately returned in the response. It is not stored and usually only affects the user who triggers it.

  • DOM-Based XSS - Executed entirely on the client side when JavaScript modifies the DOM using unsanitized input.

Stored XSS

Stored XSS

Consider a Reddit-style comment system where users can submit input. If you enter something like <h1>test</h1>, and the text renders as a large heading, that tells you HTML is being interpreted. If the result appears for multiple users, the payload is being stored. This confirms a stored XSS vulnerability.

Container 1 Container 2

This allows attackers to do things like deface websites, inject scripts, and/or deliver malicious content to the user. You can also inject JavaScript:

<script>prompt(1)</script>

Script Example

This will execute for every user who visits the page.

Reflected XSS

Reflected XSS

In this case, input is reflected directly in the response. We can try the following code. If it successfully executes, the application is vulnerable to reflected XSS.

<script>alert(window.origin)</script>

Reflected Example Result

Once the page refreshes, the payload disappears. This is what makes it non-persistent.

Source View

Reflected XSS often uses GET requests, meaning the payload appears in the URL. This allows attackers to craft malicious links and send them to victims.

GET Request URL Payload

DOM-Based XSS

DOM XSS

DOM-based XSS happens entirely in the browser. If you do not see an HTTP request in your browser DevTools, the payload is likely handled client-side. Basic script tags may not work due to protections, but other payloads can. The following script works because the image fails to load and triggers the onerror event.

<img src="" onerror=alert(window.origin)>

DOM Example DOM Result

You can use DOM XSS with the $lt;img> attribute to execute all sorts of command, such as redirecting users to another site, like one that hosts a malicious file or a site that you control, for example:

Redirect

Source vs Sink

To understand DOM XSS, you need to first understand the concept of the Source and Sink. The source is the JavaScript object where user input enters the application. The sink is where that input is written to the page. If the input is not properly sanitized, the page is vulnerable to cross-site scripting. Come commonly used vulnerable sinks include document.write, innerHTML, and outerHTML.

Finding XSS Vulnerabilities

As JavaScript, and with is XSS vulnerabilities, become more common, so too do the tools that can help detect XSS. A few common tools that can help identify XSS vulnerabilities include Burp Suite, Nessus, and OWASP ZAP. These tools can perform a passive scan, which analyzes code for potential vulnerabilities, or an active scan, which sends multiple payloads to try to trigger one.

While it can be time consuming, manual testing is still important. You can find lists of XSS payloads to try at PayloadsAllTheThings and PayloadBox. Trying multiple payloads and observing behavior is how you really understand what is happening. There are some tools you can use to automate this process, such as XSStrike, BruteXSS, and XSSer. Automation helps, especially in a time crunch, but understanding the underlying concepts is what actually makes you effective.

XSS is deep, nuanced, and constantly evolving. The tools will change as developers find ways to prevent XSS vulnerabilities, but the concepts stay the same. This is just a brief overview of what I have learned so far.

I hope this helps! Happy Hacking!