Web Application Security

OWASP Top 10 : Cross-Site Scripting #3 Bad JavaScript Imports

By Sandeep Kamble

10 min read

OWASP Top 10 : Cross-Site Scripting - Bad JavaScript Imports

This blog covers Cross-Site Scripting (XSS) vulnerability from a different perspective. Generally, XSS is when the application takes user supplied JavaScript and displays it without escaping/encoding. In this blog, we will see how can XSS be exploited even if the application properly escapes/encodes the user inputted JavaScript using different methods. Exploiting XSS in this way can be as drastic as exploiting XSS in the regular way. Using this, an attacker can carry out attacks against the application users such as stealing cookies, creating a Trojan login. This is a regular XSS i.e. reflected XSS, but there are other types of XSS such as Stored and DOM based XSS.

Need to include cross domain resources:

The ever growing need of giving a rich user experience to website visitors have made the need for browsers to include cross origin resources. Sometimes these resources can be data, an iframe, an image or JavaScript.

For example:

A website can have the following cross origin resources:

Diagram of a web page referencing external image and jQuery script from other domains

Why do we need external scripts and why not refer internal scripts?

They enhance the performance of website as they are cached by the browser from the CDN.

Referring the following scripts will improve performance: <script src=”https://code.jquery.com/jquery-1.8.1.min.js”></script> because multiple sites use the same and the browser has to load it just once from the CDN and after that each time it is referenced, it is served from the browser cache.

If instead of the above, a website refers scripts internally. That is, downloads the jquery-1.8.1.min.js file and serves it from its own root directory like <script src =”http://example.com/jquery-1.8.1.min.js”></script> then it will cause browsers to download load scripts from the internet instead of using it from the browsers cache that is downloaded from CDN as a result of which the performance is affected.

We need external scripts because they allow developers to write less code and let them make DOM manipulations easy.

The following is a jquery’s single line of code to change background colour:

           $ (‘body’) .css (‘background’, ‘#ccc’);

In JavaScript, the code to perform the same functionality is:

          Function changeBachground(color) {

                  Document.body.style.background = color;

          }

          Onload=”changeBackground (‘red’);”

So, 1 line of code in JQuery is equivalent to 4 lines of code in JavaScript when it comes to changing background colour.

Let us see the security vulnerabilities that arise due to bad JavaSript imports:

Loading scripts from third parties cause security vulnerabilities because the provider has complete control over the content served on the web page. A malicious website can serve malicious JS to a victim site thus doing a Cross Site Scripting attack. Research has leaded us to find the following types of mistakes that cause vulnerabilities:

1. Script inclusion from localhost

Suppose in a development environment (for testing purpose), a developer references scripts from another web server running on localhost.

If this code of the web app goes in production then, in this case an attacker can exploit it in the following way:

  • Taking access of a computer
  • Starting a web server on localhost at port 4545 serving the malicious JS.
  • Attacker leaves the computer and victim uses it.
  • Victim browsers the vulnerable website.
  • The website launches and loads JS from the address http://127.0.0.1:4545/import.js and it results in XSS.

2. Script inclusion from Private IP from intranet

Suppose in a development environment (for testing purpose), a developer references scripts from another web server running on a private IP address of the intranet.

If this code of the web app goes in production then, in this case an attacker can exploit it in the following way:

  • Taking access of a computer within the intranet that has IP: 192.168.0.111.
  • Starting a web server on it serving a malicious JS.
  • Victim browsers the vulnerable website.
  • The website launches and loads JS from the address and it results in XSS.

3. From Non-Registered domains

A developer references scripts from a domain on the internet. For example: <script src=”https://securelayer7.net/import.js”></script>

But due to some reason, in future, this domain expires. Then, in this case an attacker can exploit it in the following way:

  • Registering the domain securelayer7.net and serving malicious JS.
  • Any victim user that uses the website from a remote location will load the script from the newly registered domain and it results in XSS.

4. From non-static IP

A script is referenced from an IP address instead of domain name. This sounds good as long as the IP address is static.

But if the IP address is subject to change, then, in this case an attacker can exploit it in the following way:

  • Attacker gets hold of that IP and serves malicious JS
  • Any victim user that uses the website from a remote location will load the script from the website on the old IP and it results in XSS.

5. From typing mistakes in domain names

A script is referenced from correct domain but with some typing mistake.

Notice that in above example, the spelling of JQuery contains ‘i’ in the end instead if ‘y’. In this case, an attacker can exploit it in the following way:

  • Attacker registers a domain with the incorrect name i.e. code.jqueri.com and serves malicious JS.
  • Any victim user that uses the website from a remote location will load the script from the newly registered domain resulting in XSS.

6. From a server serving content over HTTP (This vulnerability has now been patched in all modern browsers)

A script is referenced from a server that serves content over HTTP and NOT in HTTPS. 

Notice that the domain name is correct without any spelling mistake. In this case, an attacker can exploit it in the following way:

  • Being in a suitable position on the network.
  • Performing a man in the middle attack and changing legitimate JS to serve malicious JS resulting in XSS.

Mitigations:

Use of Sub-resource Integrity feature of browsers:

The Subresource Integrity feature enables to mitigate the risk of these attacks, by ensuring that the files Web application fetches (from a CDN or anywhere) have been delivered without a third-party having injected any additional content into those files, and without any other changes of any kind at all having been made to those files. It is done by using the Subresource Integrity feature by specifying a base64-encoded cryptographic hash of a resource the web application is telling the browser to fetch, in the value of the integrity attribute of any <script> or <link> element. For example:

You can use the following <script> element to tell a browser that before executing the https://securelayer7.net/import.js script, the browser must first compare the script to the expected hash, and verify that there’s a match.

<script src=”https://securelayer7.net/import.js integrity=”sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC” crossorigin=”anonymous”></script>

When a browser encounters a <script> or <link> element with an integrity attribute, before executing the script or before applying any stylesheet specified by the <link> element, the browser must first compare the script or stylesheet to the expected hash given in the integrity value.

As of now, it is supported by Mozilla Firefox, Google Chrome and Safari.

Apart from this following measure should be taken to secure your JS imports:

  • The code (in development environment) which serves scripts from localhost or any private IP address should be replaced with secure code that serves scripts from secure location when the code is deployed in production.
  • Monitoring of the existence of the domains should be continuously done in order to make sure that script is not referenced from a domain which is expired.
  • Scripts should not be referred from a non-static IP address.
  • Typing mistake should not be done while referencing scripts.
  • Scripts should only be served from servers which have implemented HTTPS.
  • Scripts should be served from a domain which you trust and which don’t have a RCE vulnerability. As it can lead to an attacker replacing the script that victim site references with malicious JS resulting in XSS in victim site.
  • If you are unsure of the above point, then prefer to save the scripts on your own server rather than external reference.

Looking to strengthen your security posture? SecureLayer7 helps organizations identify vulnerabilities, reduce risk, and defend against evolving cyber threats.

Contact our experts to get started.

References:

// SecureLayer7

How SecureLayer7 helps

SecureLayer7 web application penetration testing maps every external script source your app loads and flags imports from localhost, private IPs, expired domains, or non-static IPs that lead to XSS. We verify each finding by hand so you fix real attacker paths, not noise.
Get a web app pentest

Frequently Asked Questions

Can XSS happen even when an application properly escapes user input?

Yes. Output encoding stops attacker JavaScript that travels through user input fields. It does nothing about scripts the page loads from external sources. If an attacker controls one of those external script sources, their code runs in the page regardless of how well inputs are encoded.

Why is loading JavaScript from a third party a security risk?

The provider has full control over the file the browser fetches and runs. Whatever they serve executes with the same privileges as your own code, including access to cookies and the DOM. A compromised or malicious provider can swap the file and run arbitrary script against every visitor.

What happens if a production site references a script from localhost or 127.0.0.1?

The browser resolves 127.0.0.1 to the victim’s own machine, not your server. An attacker who runs a web server on that port serves malicious JavaScript that the page then loads and executes. This is common when a developer leaves a test-environment script tag in code that ships to production.

How can a script tag pointing to a private intranet IP be exploited?

An address like 192.168.0.111 resolves inside the victim’s local network. An attacker with a foothold on that intranet starts a server on that IP serving hostile JavaScript. When an internal user opens the vulnerable site, the page fetches and runs the attacker’s script. Like the localhost case, it usually comes from a development reference left in production.

What can an attacker do after achieving XSS through a bad script import?

The same things as classic XSS, since the injected code runs in the victim’s session. That includes stealing session cookies and planting a fake login form to harvest credentials. Impact does not depend on how the script got in, only on what it can reach once it executes.