Why XSS is dangerous (a showcase)

In this blog article I will demonstrate how dangerous Cross-Site Scripting can be.

Important Notice: In this article a XSS vulnerability is demonstrated on a production context application. The shown exploit was reported on 18.07.2021. I contacted the platform administrators multiple times and through multiple channels but received no response. After waiting months I decided to publish the full article.

What is XSS?

XSS stands for Cross-Site Scripting and is a web-based vulnerability in which an attacker can inject malicious scripts in a target application. There are different types of such attacks. This article covers a type-I XSS.

Did you know that Cross-Site Scripting carried out on websites accounted for roughly 84% of all security vulnerabilities documented by Symantec?

What is this blog article about?

Basically a process about how I discovered a XSS vulnerability on Semicolon.dev and exploited it.

To be fair: The application seems to be under heavy development. It could be that sooner or later the vulnerability I found would have been found by the developers themselves.

I think that in the process of finding the vulnerability, there are some interesting knowledge points. I hope you can take something away from my writeup. My intention is of a purely informative nature and intended to make applications more secure.

Before we start, I will shortly explain what Semicolon.dev is about. On this application web developers can share tutorials, projects or just discuss with the community. Main features were inspired from Twitter. There are about ~100-200 live users on the site and 3599 verified accounts (status: 18/07/2021). 

What is this blog article NOT about?

Blame, or disregard the work of other developers. It is important to understand that XSS is (unfortunately) very common. Such vulnerabilities have even been found on Twitter and google. Numerous such vulnerabilities were found. The most of them has been fixed before attacker could maliciously exploit them and compromise systems. 

Let's start with a proof of concept.

Imagine a Twitter-like social media platform. Each account has its own public profile, which can only be viewed by other registered users. We have to register 2 accounts. An attacker (X) and a victim account (Y). X will store malicious code on his public profile. As soon as Y visits X's public profile, the malicious code is executed. In this case, the malicious code ensures that Y sends a tweet which is defined by X.

proof of concept

How I started.

At first I tried to send some XSS polyglots inside the given inputs to see how the server responds. Basically a try to bypass restrictions. In case of semicolum.dev all inputs where validated with JavaScript. The validated data was send trough an API to the server. So, I tried to send raw data directly to the endpoint to skip the client-side validation stuff.

With a little trial and error, I was able to register an user through API calls and verify an account without even using the application. This is not dangerous at first, but it can be abused in this way because the client side validation is bypassed and it seems that input validation and data sanitization were not applied on the backend.

If so, I could try some serious injection and check the output. Guess what? XSS was possible in one case.

The vulnerability

I was able to execute code on the user profile page. The vulnerability was found in the output of "location" because the output was not escaped.

In the following section, I'll take an devastating example (like shown in the video) of a possible form of attack.

Of course many other XSS attacks are possible, but I think to bypass CSRF protection is one of the most impressive and dangerous exploit scenario.

Please be assured that the exploit was only used for demo purposes and was then immediately removed from the system. No real accounts were affected by this attack.

The attack

As you can see in the screenshot above, I used the API as endpoint for the attack (again). I was able to bypass the client-based validation and send the payload in JSON format directly and unfiltered to the server via POST request (again).

Now, in this case, I chose the malicious code as the value for the location key because there was no html entity encoding on the users account page and the malicious code could be interpreted and executed by the browser.

On this point, every user who is now visiting the attackers profile, is posting a Tweet without even knowing it. The tweet content is given by the attackers injection and could be everything. That means that the victim loose control over his own account for this one moment. This attack hits a 8.3 CVSS Base Score (?).

But wait... How can this work without knowing the active user token of a possible victim?

Well, we don't need to know the token but how to get it.

When a user is logged in, a token is stored in a storage object which can be used to access the current origin's local storage space. The screenshot shows that the token from the active user can be called from that object and is used in the payload as token value.

Because of such attacks, there is a lot of advice to avoid storing tokens in LocalStorage. The problem is not just stealing data from LocalStorage. As you can see, a successful XSS attack gives the attacker almost full control over the application code. Any action enabled through the API could be used on a victim.

How to prevent such attacks?

We as developers can follow good practices to prevent most of XSS-Attacks. So do use good practises!
Keep focus on preventing XSS vulnerabilities!

Sanitize user input, validate the data and escape the output.

Check out this for detailed information: Cross Site Scripting Prevention - OWASP Cheat Sheet Series

Thanks for reading ❤️