Stored Cross-Site Scripting

Let’s start to understand the details of a stored cross-site scripting vulnerability. We are going to hear about the basic description of the vulnerability and how to search for it.

What is stored cross-site scripting?

Stored XSS is occurring if a malicious Javascript payload, that has been previously stored on a system, is requested and delivered in an HTTP response by a victim at a later point in time.

Let’s break this down a bit. Many web applications allow a user to store information these days. This data is typically stored in a database and can be obtained at a later point in time by one or multiple people.

As everybody is heavily using social media platforms in our modern age, let’s think of a text-sharing application similar to Twitter. A user can log in and post a short message. Other users of the same service can view posts of the author.

Example: You check your message feed and find this message!

Let’s have a look how an HTTP request creating this post could look like.

POST /message/new HTTP/1.1
Host: example.com
Content-Length: 90

user=regularUser&post=This+is+my+test+message!+I+really+enjoy+using+Intigriti's+Hackademy.

An attacker could right now try to use that same request for malicious purposes. He could e.g. test the application by trying to insert HTML tags (such as the <a> tag in the following example).

POST /message/new HTTP/1.1
Host: example.com
Content-Length: 123

user=attacker&post=This+is+my+test+message!+I+really+enjoy+using+<a+href="https://intigriti.com">Intigriti's</a>+Hackademy.

If the application does not perform input sanitization or any output encoding, the attacker could end up seeing something similar to this:

HTML <a> tag inserted a link making the string “Intigriti’s” clickable

While this is already a good indicator of an XSS vulnerability, inserting various HTML tags is technically not an XSS exploit. For that, let’s have a look at the <script> tag.

POST /message/new HTTP/1.1
Host: example.com
Content-Length: 126

user=attacker&post=<script>alert("attacker")</script>+This+is+my+test+message!+I+really+enjoy+using+Intigriti's</a>+Hackademy.

If an attacker inserts an XSS payload such as <script>alert("attacker")</script>, the Javascript code is executed.

If a victim visits the post of the attacker, the malicious code will execute in the browser of the victim.

An example lab

Check out our Hackademy video for a sample lab showcasing a basic stored XSS vulnerability.

How do you search for stored cross-site scripting vulnerabilities?

Let’s look at ways how you can manually search for XSS vulnerabilities. There are also automated tools that can help you on the way. However, those we are covering in our “Hacking Tools” section.

Manually testing for XSS is a very time-consuming task. Firstly, we need to identify every single way a user can input data, which then gets processed by the application. Some possible ways are:

  • Inside an HTTP request, via

    • URL parameters

    • Message body parameters

    • HTTP headers

    • The URL file path

  • Inside external inputs processed by the application, e.g.

    • Emails read by a web mail solution

    • Blog content read by an RSS feed aggregator

    • Log output collected via a webhook in a monitoring solution

    • Etc.

Secondly, we need to monitor where this input is going to be displayed. This task can range from very easy to very hard. Think of a user forum. You post a message onto a thread and your text is most likely going to be displayed straight away on the same page.

However, there are many applications where you don’t immediately see your input displayed. Picture a support portal. Here, you do send in a support question, but you don’t know where it is going to be displayed. You most likely don’t even have access to it.

A common strategy to trace your inputs is to use different strings for every single input source. E.g. you could use a human recognizable chain of words, such as test_input_thread_post. If you see this string popping up somewhere, you immediately know where it is coming from.

If you are planning to automate your search, you can go one step further and use e.g. UUIDv4 strings in order to have unique computer recognizable input (example UUID: e34f3af1-d0e4-462b-b463-604d1ed207bf).

How do you test for stored cross-site scripting?

XSS vulnerabilities come in various different shapes. There is no one golden payload that works in every case. On the one hand, you can go ahead and try payloads listed on pages like OWASP’s XSS filter evasion cheatsheet. On the other hand, it is not recommended to spread payloads over an application that you don’t understand.

It’s important to understand that specific cross-site scripting payloads do not just work for either reflected, stored, or DOM-based vulnerabilities. There is a lot of common ground, which is why we are summarising attack vectors in this article.