How complicated can XSS be

How complicated can XSS be

Unfortunately, according to the OWASP, XSS is one of the most common vulnerabilities. In simple terms, this is a type of injection that sends malicious code from one user to another. When executing an XSS vulnerability, the answer depends; some instances can be extremely complicated, and other scenarios can be very straightforward.

If you are talking about prevention, it is usually quite simple. The key to avoiding this is something called sanitization, but we’ll dive deeper into this later.

For now, let’s take a look at the following code:

// num is passed via URL parameters

function chooseTab(num) {

// Dynamically load the appropriate image.

var html = "Image " + parseInt(num) + "<br>";

html += "<img src='/static/level3/cloud" + num + ".jpg' />";

$('#tabContent').html(html);

// ...

}

The above code block was taken from the third level of xss-game.appspot.com, but we’ll discuss that later. The point is that it’s a perfectly valid scenario you would find in someone’s code.

If you are wondering what is wrong with the logic of that example, you are assuming the user will use your app the way you want it to be used, not how it can be used. That’s why the person who wrote that function probably thought that passing num directly as part of the name and creating the tag with that was an efficient way to do it. However, the person only thought about the accepted values for the feature to work, not the scenarios beyond that scope.

Here’s how you could inject code in that scenario:

// Add this as the URL parameter

/cat.png' onerror='alert(1)'/>

Why would that work? Because num can be whatever you want, and you can leverage that to close the src property of the img tag and then add the onerror property with the code you want. In this case, we are assuming that cat.png doesn’t exist, and if it doesn’t then onerror will be executed. We don’t even care about the remaining portion of the code ".jpg' /&gt;" because the browser would ignore that.

Now we can take a look at this other example:

function escape(input) {

input = input.replace(/[=(]/g, '');

return input;

}

This is the level 2 of the prompt.ml game (yes, we will also talk about this later in this article). This function removes the open parenthesis symbol and the equal sign to return the value.

You might be thinking…

“This function is great. If you can’t even use those symbols, you can’t insert a script, so no XSS! I’ll stop reading this article and use it in my next project.”

The bad news is…

Even though that script removes some of the characters you would typically need to insert a script, there is an alternative—yet not a very obvious—way to insert malicious code successfully.

Here is how you would do it:

<svg><script>alert&#40;1)</script>

This is why, at the beginning of the article, we said that XSS can be very simple or very complex (or weird). The reason this works is because of the svg tag. Inside this tag we can use encoded characters, &#40; is the encoded version of the open parenthesis symbol.

How dangerous can an XSS attack be?

As always, it depends. To get a more specific answer, you need to consider that parts of the system can be affected. However, we can simplify this by saying that you don’t want any level or type of XSS in any place, especially if avoiding it and fixing it is usually something that doesn’t require a lot of energy.

In a previous article of this blog, we said, “It is easier to adopt the mindset of patching every vulnerability as soon as possible, even if it doesn’t represent an immediate threat to your system, rather than debating whether or not to address a vulnerability.” (check that article here after finishing this one)

In simple words, an XSS vulnerability can be really bad or benign, depending on the circumstances, but either option should be addressed. But, if you want real-world examples, you can read this article from securityweek.com about XSS vulnerabilities on Yahoo Mail. Spoiler, it was a very expensive bug.

Prevention

The only good part of XSS is that it’s relatively easy to avoid. Even the most complex examples can be covered with these two principles:

  1. Never render HTML unnecessarily and
  2. Never trust the user.

Never render HTML unnecessarily

As simple as it sounds, this will save you a lot of trouble in the future. Sometimes, you need to create components dynamically, which will require rendering HTML, and that’s understandable, but don’t use methods like “innerHTML” - or any other equivalent - if you don’t need to render the HTML you created or curated.

Take a look at the following example we took from a very well-written article called “How to Ensure Security as a Front-End Engineer” (yes, it’s the same article we mentioned before)

// Option 1
document.querySelector('#title').textContent = 'Your new text';
// Option 2
document.querySelector('#title').innerHTML = 'Your new text';

If you don’t keep in mind XSS, you might think that either option is the same, but now you know that option #2 opens the door for an XSS attack.

Never trust the user

Earlier in this article, we said that the key to preventing XSS was sanitization, which means cleaning the user’s input before using it.

You can do it easily and effortlessly with either of these options: Using DOMPurify, a library recommended by OWASP on their page about XSS prevention. The other one by using the following snippet:

/**

* Sanitizes a string to prevent XSS (Cross-Site Scripting) and HTML injection attacks.

* @param {string} inputString - The input string to sanitize.

* @return {string} - The sanitized version of the input.

*/

function sanitizeInput(inputString) {

// Create a div element.

const div = document.createElement('div');

// Set the div’s text content to the input string.

// This will not interpret the input as HTML.

div.textContent = inputString;

// Return the innerHTML of the div, which will contain the sanitized version of the input.

return div.innerHTML;

}

How to improve your XSS skills

XSS is a topic with multiple nuances, and like any other skill, it can be polished with practice. Before trying to solve some coding challenges, check Portswingger’s page about this topic and OWASP’s official guide.

On top of that, you need to really comprehend how browsers work and render content. Some XSS attacks might work on one browser but not on another browser. You need to study the flexibility of the svg and img tags and have a good knowledge of HTML and its possibilities.

Once you have learned the core concept of XSS and its rules, you can try these two games (the games we mentioned in the examples above):

You’ll see that some examples will challenge your understanding of this topic and make you think outside the box. If you are having trouble with the solutions or you are just starting your XSS journey you can check this YouTube playlist that explains all the levels of the first game. In addition, you can check this GitHub repository to get the answers and explanations of the second game. It is understandable if you need to use those links, but try to craft your own solution first.