Findings Series: Cross-Site Scripting (XSS)
Contents
Description
Cross-site scripting (XSS) is a code injection attack caused by improper input sanitization of user input in web applications. Attackers submit malicious input to a web application and run JavaScript functions that can dump cookies, hijack sessions, or even log keystrokes from a victim's browser.
XSS vulnerabilities are into three distinct types:
- Reflected - This occurs when a malicious payload is injected into a user-controlled input or parameter as part of the victim's request, which is then rendered within an application response.
- Stored - This occurs when user-controlled input is rendered within a response in a persistent way, meaning that it will continue to execute until the injected code is removed. When this happens, the payload is usually stored within the application database and returned automatically on a page where the payload is meant to be displayed.
- DOM-based - This occurs when user-controlled input is passed into a JavaScript function that displays results on the page. The execution occurs within the victim's browser.
Classification
Discovering Cross-site scripting is usually an automatable process, and the effects of a successful exploit allow an attacker to perform actions within the application under the context of the user account executing the JavaScript payload. Attackers can also leverage cross-site scripting attacks to exploit vulnerabilities within the victim's browser to execute code on the victim's machine.
Probability
Impact
Medium - High
Medium - Critical
Classification Criteria
Cross-Site Scripting is relatively simple to fuzz for and identify. The probability is ranked highly for this reason. While serious, exploitation of a Reflected XSS has much less significant consequence than a Stored or DOM-Based XSS. It is for this reason the Impact category ranges from Medium to Critical.
Examples
There are three (3) categories under which XSS attacks currently fall: Reflected, Stored, and DOM-Based. The categories depend on how the attacker supplies malicious input and whether or not that payload remains embedded in the vulnerable application after the attack. Reflected and Stored XSS attacks are also referred to as Server Side XSS, while DOM-Based XSS is sometimes referred to as Client Side XSS. The screenshots from the following examples were created using the Damn Vulnerable Web Application on a NAT networked group of Virtual Machines.
Reflected
Reflected XSS occurs when an attacker crafts a URL that contains a JavaScript payload to be executed within a web application. This URL is then sent to a victim in hopes that the victim will visit the attacker-crafted URL thereby executing the injected code contained within the link. The injected codeis not stored on the server and is caused by improper input sanitization. The simplest way for this vulnerability to manifest is by allowing script
tags to be injected directly into the web application. Take the following form:
The above input is sent to the server and a new page is rendered containing the user's input. If, instead, a script
tag is provided to the form containing a JavaScript alert
, the application can be manipulated:
When this input is rendered by the application, the JavaScript is executed and causes an alert dialog to appear:
The ability to execute arbitrary JavaScript against the web application provides the ability to interact with application users who execute the JavaScript code.
Stored
Different from reflected XSS, stored cross-site scripting occurs when malicious input persists on a database that victims repeatedly interact with. After the payload is submitted by the attacker and stored on the server, any user who accesses the infected resource will also execute the payload. Look at the comment section below:
Users comment on the page and the input is stored on the web application. If the input is not being sanitized an attacker could include a JavaScript payload instead of a regular text comment:
The above payload will execute and reveal the domain of the web application in an alert as follows:
In addition to seeing the payload execute as the attacker, any user who browses to this comment section will see the alert as well. This is because the payload is stored on the server and gets rendered every time the resource is called. This is an incredibly effective way for an attacker to potentially hijack multiple users sessions and perform actions such as gathering credentials from various accounts on a vulnerable application.
DOM-Based
DOM-Based XSS is often misunderstood, as the attack vector is different from Stored or Reflected XSS. DOM-Based XSS is a client side injection where the XSS payload is injected into a function which then updates the page being viewed by the victim. On the webpage below, there is a user controlled parameter that allows an end user to select the language of the page. When a new language is selected from the dropdown, the parameter in the URL changes:
Instead of selecting the options from the dropdown, the attacker can try to modify the DOM directly and inject a payload:
After requesting this new resource, the JavaScript executes and renders to the end user:
Going Beyond the Popup
Cross-site Scripting attacks are generally demonstrated by injecting a basic JavaScript payload which generates a pop-up box with a message like "XSS" (in fact, we've demonstrated it that way above), but the real impact of these vulnerabilities requires a deeper dive into what an attacker could actually do after confirming the vulnerability.
Browser Hooking
Browser hooking is a technique where an attacker loads a JavaScript payload into a vulnerable web application via an attack such as cross-site scripting. The JavaScript payload executes in the victims browser. One thing that these hooks can do is connect back to an attacker's system and perform actions requested by the attacker. These actions can include accessing the victim's cookies, localStorage
or session storage, redirecting the victim to another location, exploiting vulnerabilities within the victim's browser, or interacting with the victim to prompt for credentials or other potentially sensitive information.
Browser hooking can be demonstrated using the Browser Exploitation Framework (BeEF). Using the DVWA example above, a different payload can be injected into the application which loads the JavaScript hook instead of showing a popup alert. By inserting a script
tag which imports JavaScript browser hooking code, a vitcim's browser will automatically execute the imported code when accessing the vulnerable page. The image below shows an example of an attacker injecting the payload into the application:
This results in the application loading the JavaScript file located at https://xss.fyi/hook.js which is connected to the attacker's BeEF server. Once added, the code will execute anytime a victim accesses the page containing the injected code. When this occurs, the victim doesn't encounter any visual indicators that something has happened (though communication with the attacker's system can be observed by inspection the application network requests), yet a BROWSERHOOK
cookie is added to the established session and a communication channel has been established via the execution of the hooking code:
On the attacker's system, the hooked browser shows up on the BeEF dashboard, indicating that the attacker can interact with the hooked victim:
Using modules contained within the BeEF tool, the attacker can send a comand to the JavaScript hook to return all cookies which aren't configured with the HTTPOnly
flag. The results from this request are shown in the module command results:
The attacker can perform other actions, like prompt the victim for their system credentials as a way to collect potentially sensitive information that could be used in other attacks. The image below shows a BeEF "Pretty Theft" module which is styled to look like a Windows credential prompt:
When the attacker sends the credential prompt to the victim, the victim's browser window will display the prompt, instructing the victim to enter their local system credentials to dismiss the window and continue using their browser:
Any information submitted by the victim is returned to the attacker, as shown below:
This shows only a few ways in which cross-site scripting vulnerabilities can be leveraged by an attacker within a web application. Depending on the intended use of the application, an XSS vulenrability can allow an attacker to perform numerous actions against any users who are targeted directly or indirectly by these attacks.
Remediation
Cross-site scripting can be effectively prevented by sanitizing all user input. Ensuring that all user-controlled variables are subjected to input validation greatly decreases the chance of compromise. Preventing DOM-Based XSS requires the structure of the application be assessed in its entirety and is thus more complex. Tips for preventing DOM-Based XSS can be implemented as a result of a source code review. See references for a guide on conducting application source code reviews.
Many modern web libraries have built-in protections to automatically render data in a sanitized way. One common tactic is to replace characters required for XSS with their Unicode equivalent. For example, the left angle bracket required for a <script>
tag could be rendered as Unicode character U+3008
or U+2329
, appearing the same to a user but not rendering as a valid HTML character.
Additional protections can be implemented and layered as part of a defense-in-depth strategy. These protections include the addition of HTTP response headers such as a Content-Security-Policy
which can restrict the origins from which scripts can be loaded or be configured to define specific criteria that must be met to allow for script execution.
References
OWASP XSS
OWASP XSS Prevention
OWASP Source Code Review Guide
Content Security Policy - Portswigger
Ensure CSP is effective against XSS attacks