What is Cross-Site Scripting (XSS)?

According to both OWASP and CWE, Cross-Site Scripting is one of the top 10 most dangerous web application security risks, and for good reason: OWASP’s data showed that around 2/3rds of all applications had at least one Cross-Site Scripting vulnerability, making it the second most prevalent issue in the OWASP top 10. It also labels the impact of XSS as moderate to severe, because it can vary from a simple prank, all the way to stealing important information (credit card info, passwords, and other PII), remotely executing code on the victim’s browser, and even delivering malware to the victim.

I have to admit that for the longest time as a web developer, I assumed XSS was uncommon and not that dangerous of a vulnerability. Turns out that I had those thoughts because I didn’t understand it.

XSS is an incredibly serious vulnerability that taps into the power of JavaScript, which is only increasing every year. Since your browser is a communication portal between you and the internet, and your browser is what executes client-side JavaScript code when you access websites, successful XSS attacks can end up taking over your browser.

Once that happens, either automatically or manually, the attacker can execute commands remotely, scraping information stored in your browser, running recon on your internal networks (whether at home or at work), and potentially even taking control of things like your printers, routers, etc…

That escalated quickly, and we’re getting ahead of ourselves, but it’s important to understand that XSS can have very serious impacts! At the end of this article, I’ll even share a link to a Tesla Model 3 XSS case study so you can see a real-world example.

By the way, this post is an excerpt from our course: Cross-Site Scripting(XSS): The 2021 Guide. If you’re serious about learning XSS attacks and defenses, definitely check it out.

Prefer learning through video? Subscribe to our channel.


When is Cross-Site Scripting (XSS) relevant?

Let’s say that you have a static website that’s made of only HTML and CSS. When you visit the website, the browser renders that HTML and CSS and that’s what you see on your screen…but nowadays, most websites also include dynamic elements, or require more advanced functionality than HTML and CSS alone can provide, and so most websites also include JavaScript of some sort.

For example, data gets pulled from a database, or it asks for user input and uses that user input to do something

Illustration of HTML + CSS web page with dynamic elements

So if we visualize that, you might see an input form, where the user submits information, and the application sends that information to another page via the URL. That other page grabs the URL, and parses the information in it to grab what the user submitted. 

Let’s say that this information is used to popular an HTML field on the page — in which case it grabs that data from the URL, parses it, and then outputs it in the HTML of the page, essentially allowing the user to modify that page, as shown in the illustration below:

Example of user input changing a web page

If we put our web developer hat on for a minute, and we pretend that we have direct access to modify the code on this page, we could say: “you know what, I need to add a script here in order to enhance functionality.” Usually, to follow best practices, we’d either add the script in the header or footer of the page, but technically, the script can be added anywhere in this page. In fact, we could add our script right below that header, and the browser would load it when the web page gets rendered:

Illustration of a developer adding a script block to a web page

This means that, under the right circumstances, an attacker can abuse that functionality to send a script payload via a vulnerable input, and then modify the application to do something that it wasn’t intended to.

In other words, the attacker could inject a malicious script via an input field which would then get added to the page as if it were part of the application’s code. The browser would see that code, and since the browser would think that it’s part of the application’s code and required for that page to function properly, it would execute the script:

Illustration of Cross-Site Scripting (XSS)

Enter Cross-Site Scripting…

What is Cross-Site Scripting (XSS)?

Cross-Site Scripting, aka XSS, is a type of injection attack where malicious scripts are injected in trusted websites, and executed by the visitor’s browser, just like we talked about.

Basically, imagine a simple, supposedly safe website with users, and an attacker finds a way to exploit that website in order to send malicious code to one or more of its users.

That malicious code is generally in the form of browser-side scripts, so nothing fancy or crazy. Literally what everyone in the world with internet has access to create.

An attacker finds a way to take their malicious code and infect the HTML document without causing the web server itself to be infected. Instead, it uses the server as a vector to present malicious content back to the user, either instantly from the request (which is called a reflected attack), or delayed through storage and retrieval (which is called a stored or persistent attack).

Reflected XSS
Stored or persisted XSS

To clarify, even when I say that the malicious code is stored and retrieved at a later time, I don’t mean that the server itself is infected with malicious code. The malicious code only gets executed through the user’s browser when that user visits a web page that fetches the stored code. So the core application remains unaltered, but it can be made to serve malicious content to clients.

With a third type, DOM-based, the payload technically never even has to reach the servers at all.

DOM-based XSS

That means that there are 3 main types of XSS attacks:

  1. Reflected
  2. Stored (or persisted)
  3. DOM-based

But when is Cross-Site Scripting possible? What causes the vulnerability, and how do people exploit that vulnerability?

When is XSS possible?

Like other types of web-based injection attacks, Cross-Site Scripting attacks are possible when an application uses input from a user within the output that it generates without properly validating or encoding that input.

If you’re not familiar with the term encoding, it simply means converting data or a sequence of characters into a specified format to securely process data. Validating refers to verifying that the data is what you expected. Validating and encoding are topics that cover an entire section of our course since it’s so important to defending against XSS, but I wanted to briefly mention it here in case you’re not familiar with those terms.

Input encoding and validation example

In any case, since we were talking about the application grabbing input from a user and then outputting the information, the page is only reflecting back what was submitted in a request, but the content of that request might have characters that break out of the ordinary and expected text content, and instead introduce HTML or JavaScript content that the developer did not intend for, and that the application did not expect.

Expected input: userName
Expected output: Hello, userName!

Unexpected input: Bob<script>alert(1)</script>
Unexpected output: Bob with a pop-up box saying 1

The good news is that modern browsers are getting smarter and smarter, and they now include a lot of defenses out of the box to prevent successful XSS attacks.

The bad news is that these browser security controls don’t prevent all XSS attacks, and while they’re definitely not as common as they used to be, they’re still found in the real-world, and they are one of the most commonly attempted attacks on applications. In fact, they are listed as the second most prevalent issue in the OWASP Top 10 with an Exploitability, Prevalence, and Detectability rating of 3, and a Technical impact rating of 2, since some of the XSS attacks will have much less severe impacts than others.

OWASP Top 10 XSS

What is the impact of XSS?

Successful exploitation of XSS can do a lot of damage. If an attacker is able to inject malicious scripts into a web application, then the browser may end up believing that the script comes from a trusted source, and it could let that script access cookies (document.cookie), session tokens, or other sensitive information stored in the browser and used with the site to:

  1. Grant login access or steal credentials
  2. Authorize unwanted payments
  3. Redirect users to a look-alike website
  4. and more

Some scripts could even potentially re-write the HTML on the page, changing the appearance of the website, causing you to perform actions you may not otherwise take, or sending your private information to another server without you even realizing it. For example, an attacker could insert a fake login form into the existing page, set the form’s action attribute to target their own server, and therefore trick the user into submitting sensitive information without even realizing it.

Malicious form injected via XSS illustration

Apart from information we’ve already talked about, an attacker could also register and send a user’s keystrokes to their own server, by adding what’s called an event listener (addEventListener) potentially recording sensitive information like passwords and credit card numbers.

XSS Keylogger illustration

As we explore in the course, attackers could try to use browser exploitation frameworks, such as BeEF, to ‘hook’ a browser’s victim and launch a number of different attacks using command modules.

BeEF command modules

Some of those modules aim to gather information about the target, while others contain exploits. For example, some of the modules use a technique called Cross-Site Request Forgery (CSRF) to exploit vulnerable routers, which could give the attacker admin access to your home or corporate router. That’s right — XSS can go beyond the browser once you’ve hooked a target through a vulnerable web application, because JavaScript is an incredibly powerful language.

Cross-Site Scripting (XSS) examples

Let’s take a look at some basic example XSS attack scripts so that we can illustrate the concepts we’ve been talking about.
One of the most common examples you will see, and also frankly one of the least realistic or practical examples, is the alert() injection:

<script src=javascript:alert("xss")>
or
<script>alert("xss")</script>

And this alert injection could happen via a URL, like this:
https://example.com/search?=%22%3Cscript%20src%20%3Djavascript%3Aalert%28%29%3E

Or it could be injected in vulnerable input fields.

Cross-Site Scripting (XSS) injection via input field

All this would do is create one of those annoying alert boxes that just writes out “xss”, but this is meant as a proof of concept to show that there is a vulnerability. There is very little practical usage otherwise.

Example alert(1) payload

For more examples of what XSS payloads can look like, check out these resources:

Conclusion

Alright, so we’ve talked about the potential impact that successful XSS attacks can have on applications and their users, we’ve talked about how vulnerabilities can be introduced and found, and the high-level process for how the 3 main types of XSS attacks work (Reflected, Stored or persisted, and DOM-based).

The next step is to gain a better understanding of the 3 main types, how they differ, and how to protect against them.

If you’re still not convinced that XSS is something to take seriously, check out this Tesla Model 3 XSS case study walkthrough to see a real-world example.

If you are convinced and want to learn more about XSS, including how to find vulnerabilities, craft payloads to evade filters & security controls, and ultimately how to prevent vulnerabilities in your applications, enroll in our course: Cross-Site Scripting (XSS): The 2021 Guide.

Related Articles

Responses

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.